Closed urosn74 closed 5 years ago
Hi @urosn74 thank you so much for taking the time to contribute back your improvements to this library!
At a quick glance, I like what I see. I definitely see how this adds a new degree of flexibility and re-usability for step definitions.
I'm a bit slammed at the moment, but I hope to get around to reviewing this soon; probably this week!
These may not necessarily have much to do with the PR, and certainly don't have to be flushed out to merge. This is just for my own sake in thinking about about clever ways to operate around behave
. So, please feel free to ignore the following....
Environment variable transformer; transform value based on a value in an environment variable
???
I wonder if this can somehow be provided with a custom matcher/Match class in such a way that all definitions could gain this benefit effortlessly.
A matcher has a match
method which returns a Match
object with arguments returned from the matcher's check_match
method.
The Match
object provides a run
method which calls the decorated function with the arguments. But what's important to note is this method has access to the context
(where matchers do not).
So, I am plotting in my mind if it's possible to write some custom matchers and Match object that accomplishes this thought. Since behave-webdriver utilizes two kinds of matchers, we'd need this for both the parse matcher and regex matcher.
(aside: Unfortunately, because of some inflexibilities with behave
I don't think an end-user could easily provide this themselves. So we have to make up for this inflexibility.)
from behave.matchers import Match, ParseMatcher, RegexMatcher
from behave_webdriver.parameter_transformations import NoTransformation
class TransformingMatch(Match):
def run(self, context):
args = []
kwargs = {}
for arg in self.arguments:
if arg.name is not None:
kwargs[arg.name] = arg.value
else:
args.append(arg.value)
with context.use_with_user_mode():
# the above is a COPY/PASTE of the original `run` implementation,
# THESE next 3 lines are the key change vvv
transformer = context.get('transformer', NoTransformation())
args = [transformer.eval(arg) for arg in args]
kwargs = {name: transformer.eval(value) for name, value in kwargs.items()}
self.func(context, *args, **kwargs)
class TransformMixin(object):
def match(self, step):
# -- PROTECT AGAINST: Type conversion errors (with ParseMatcher).
try:
result = self.check_match(step)
except Exception as e: # pylint: disable=broad-except
return MatchWithError(self.func, e)
if result is None:
return None # -- NO-MATCH
# the above is a COPY/PASTE of original implementation
# THIS one line is the key v
return TransformingMatch(self.func, result)
class TransformParseMatcher(TransformMixin, ParseMatcher):
pass
class TransformRegexMatcher(TransformMixin, RegexMatcher):
pass
Now, suppose that behave-webdriver
registers these matchers and uses them........ I believe that's all we would need to give all definitions the benefit of transformations automagically based on a transformer set on context.transformer
(or some other aptly named attribute)
Then we just change the lines where we call use_step_matcher
and replace with the two matchers defined above.
/endrambling
Hi @spyoungtech. Thanks for taking the time for this PR. If I understand you correctly, matchers would "magically" apply transformations on parameters, before the call to the step function? That would be quite powerful and would keep step implementations nice and tidy.
Environment variables were the original "target", though I think it is already quite easy to pull them into the context that is used for parameter transformations. Unless you aim to eliminate the need to initialise the context in the environment - then it would make sense to move that concern to a specialised transformer.
matchers would "magically" apply transformations on parameters, before the call to the step function
That's the idea. It's actually the custom Match
object that handles this, since that's what will ultimately have access to the context
, where a user could supply a 'transformer'. We alter the matchers to make sure they use the custom TransformingMatch
class, instead of the regular Match
.
I've merged this into a feature branch for further work. I've opened #61 to continue tracking this effort.
Hi Spencer,
Thanks for super useful library. I have implemented a small extension to support our use case - it adds simple string interpolation capability for parameter values that allows us to efficiently reuse features across different environments.
I would appreciate if you will consider integrating this pull request into your base and eventually publish new release.
Best regards, Uroš