-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Future warnings for hookspec changes AND support defaults to enable deprecation #34
Conversation
@@ -659,7 +688,9 @@ def varnames(func): | |||
return () | |||
|
|||
args, defaults = spec.args, spec.defaults | |||
args = args[:-len(defaults)] if defaults else args | |||
index = -len(defaults) if defaults else None | |||
args, defaults = args[slice(0, index)], args[slice( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too fancy of an xpression
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RonnyPfannschmidt that I do it on a single line or the slice
stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the expression is magical enough that i have to think very hard to follow it by now
and this can be a very simple if .. else thats really easy to follow
) | ||
|
||
# warn about new/upcoming arguments to the spec | ||
if notinimpl: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that one is explicitly breaking what we use pluggy for (as in not naming all arguments if you dont need thme all)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still find this weird.
Is it supposed to be a feature that you don't have to match the spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the feature is, that a hook only has to take what it needs, no need for unused argument, and future-safety for adding new arguments
if notinspec: | ||
raise PluginValidationError( | ||
"Plugin %r for hook %r\nhookimpl definition: %s\n%s" | ||
"Positional args {0} are declared in the hookimpl but " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one at least from seeing the code is more confusing than before (this may be a miss-impression)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh shoot I left in the format string syntax by accident...
The only thing I wanted to improve was reporting all the incompatible args in a single error instead of requiring that user runs multiple times to figure them all out.
@@ -104,7 +114,7 @@ def setattr_hookspec_opts(func): | |||
if historic and firstresult: | |||
raise ValueError("cannot have a historic firstresult hook") | |||
setattr(func, self.project_name + "_spec", | |||
dict(firstresult=firstresult, historic=historic)) | |||
dict(firstresult=firstresult, historic=historic)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we perhaps get a separate PR fixing the indentation, its problematic to make it as part of a functionality changing pr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep of course.
|
||
def execute(self): | ||
all_kwargs = self.kwargs | ||
caller_kwargs = copy.copy(self.caller_kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at first glance this looks incomplete, was a .update call planned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RonnyPfannschmidt yes this was from when I was trying to approach this by calling hooks with func(**kwargs)
and popping undeclared defaults beforehand.
I'll probably remove it unless we end up finding another use for it.
@RonnyPfannschmidt bump. |
@@ -689,8 +677,8 @@ def varnames(func): | |||
|
|||
args, defaults = spec.args, spec.defaults | |||
index = -len(defaults) if defaults else None | |||
args, defaults = args[slice(0, index)], args[slice( | |||
index, None if index else 0)] | |||
args = args[:index] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that leads to always empty defaults due to replacing args and is till no if/else without a slice all
index = -len(defaults) | ||
args, defaults = args[:index], args[index:] | ||
else: | ||
defaults = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well done
btw, shouldn't those be hash-able? (as in made into tuple)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RonnyPfannschmidt we do hash them per function already in varnames()
no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh you mean each list of names. Yeah I can add that.
b61f9a2
to
9ab0ebe
Compare
@RonnyPfannschmidt @hpk42 bump! I think this adds what you originally requested @RonnyPfannschmidt but I personally think it's quite hideous, ill performs, and is confusing given normal function call semantics. I still have to add tests of course. |
Warn when either a hook call doesn't match a hookspec. Additionally, - Extend `varnames()` to return the list of both the arg and kwarg names for a function - Rename `_MultiCall.kwargs` to `caller_kwargs` to be more explicit - Store hookspec kwargs on `_HookRelay` and `HookImpl` instances Relates to pytest-dev#15
@RonnyPfannschmidt @hpk42 rebased onto the new test reorg changes. |
Add support for using declared keyword argument values from both hookspecs and hookimpls. The current logic will inspect the hookimpl and, if it contains defaults, values will be first looked up from the caller provided data and if not defined will be taken from the hookspec's declared defaults. If the spec does not define defaults the value is taken from the hookimpl's defaults as is expected under normal function call semantics. Resolves pytest-dev#15
Verify that defaults declared in hook impls and specs adhere to the lookup order: call provided value, hook spec default, and finally falling back to the spec's default value.
@RonnyPfannschmidt @hpk42 alright I added a test which shows how this all works now. I still think the implementation of I'd like you guys to take a look before I do any more changes since I'm pretty sure this PR's current state matches the original requirements of #15. |
self.kwargvalues = inspect.getargspec(function).defaults | ||
self.kwargs = dict( | ||
((name, value) for name, value in | ||
zip(self.kwargnames, inspect.getargspec(function).defaults)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inspect.getargspec(function).defaults
should just be self.kwargvalues
here?
Just a gentle ping, what's the status of this PR? |
@nicoddemus well it's in dire need of thorough review :) I have my own criticisms of this particular approach but I wanted to at least get something going for #15. I know @RonnyPfannschmidt mentioned that he thought a test might be missing. |
@@ -724,6 +745,8 @@ def remove(wrappers): | |||
raise ValueError("plugin %r not found" % (plugin,)) | |||
|
|||
def _add_hookimpl(self, hookimpl): | |||
"""A an implementation to the callback chain. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/A/Add
As per discussion with @RonnyPfannschmidt I'm putting this draft up to start looking at dealing with #15.
I've broke some of the tests due to a change to
varnames()
which now returns both thearg
andkwarg
names for a function.There's also some other changes that I didn't fully revert that pertained to trying to validate
kwargs
that we might decide to declare inhookspecs
as a way to define defaults when a hook call doesn't provide them.