Skip to content
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

Closed
wants to merge 4 commits into from

Conversation

goodboy
Copy link
Contributor

@goodboy goodboy commented Nov 14, 2016

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 the arg and kwarg 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 in hookspecs as a way to define defaults when a hook call doesn't provide them.

@@ -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(
Copy link
Member

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

Copy link
Contributor Author

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?

Copy link
Member

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:
Copy link
Member

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)

Copy link
Contributor Author

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?

Copy link
Member

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 "
Copy link
Member

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)

Copy link
Contributor Author

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))
Copy link
Member

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

Copy link
Contributor Author

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)
Copy link
Member

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?

Copy link
Contributor Author

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.

@goodboy
Copy link
Contributor Author

goodboy commented Nov 14, 2016

@RonnyPfannschmidt bump.
Tried to fix up most of the things you asked.
The part in varnames() where I parse out the kwarg names didn't change much (still have to use the slice()). Not sure how you were envisioning I simplify it further.

@@ -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]
Copy link
Member

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 = []
Copy link
Member

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)

Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

@goodboy
Copy link
Contributor Author

goodboy commented Nov 18, 2016

@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.

Tyler Goodlet added 2 commits November 18, 2016 10:53
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
@goodboy
Copy link
Contributor Author

goodboy commented Nov 18, 2016

@RonnyPfannschmidt @hpk42 rebased onto the new test reorg changes.

Tyler Goodlet added 2 commits November 19, 2016 12:46
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.
@goodboy
Copy link
Contributor Author

goodboy commented Nov 19, 2016

@RonnyPfannschmidt @hpk42 alright I added a test which shows how this all works now.

I still think the implementation of _MultiCall.execute() would be better implemented like I suggested quite a while ago using func(**kwargs).

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))
Copy link
Contributor Author

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?

@nicoddemus
Copy link
Member

Just a gentle ping, what's the status of this PR?

@goodboy
Copy link
Contributor Author

goodboy commented Feb 1, 2017

@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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/A/Add

@goodboy goodboy changed the title Future warnings for hookspec changes Future warnings for hookspec changes AND support defaults to enable deprecation Feb 9, 2017
@goodboy
Copy link
Contributor Author

goodboy commented Feb 13, 2017

Declining this as I have reintroduced the change set using two smaller (and more focused) PRs #42 and #43 to simplify the review process.

@goodboy goodboy closed this Feb 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants