Skip to content

Commit

Permalink
Merge branch 'master' into stephenfin/stop-using-argspec
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Nov 23, 2017
2 parents 5758ba0 + 7f865b0 commit 451515a
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 126 deletions.
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ matrix:
env: TOXENV=check
- python: '3.6'
env: TOXENV=docs
- python: '2.6'
env: TOXENV=py26-pytestrelease
- python: '2.7'
env: TOXENV=py27-pytestrelease
- python: '3.3'
env: TOXENV=py33-pytestrelease
- python: '3.4'
env: TOXENV=py34-pytestrelease
- python: '3.5'
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
0.6.0
-----

- Drop support for EOL Python 2.6 and 3.3 in PR `#103`_.

.. _#103: https://github.com/pytest-dev/pluggy/pull/103

0.5.2
-----
- fix bug where ``firstresult`` wrappers were being sent an incorrectly configured
Expand Down
1 change: 0 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ environment:
# note: please use "tox --listenvs" to populate the build matrix below
- TOXENV: "check"
- TOXENV: "docs"
- TOXENV: "py26-pytestrelease"
- TOXENV: "py27-pytestrelease"
- TOXENV: "py34-pytestrelease"
- TOXENV: "py35-pytestrelease"
Expand Down
15 changes: 9 additions & 6 deletions pluggy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ def __init__(self, project_name, implprefix=None):
self._implprefix = implprefix
self._inner_hookexec = lambda hook, methods, kwargs: \
hook.multicall(
methods, kwargs, specopts=hook.spec_opts, hook=hook
methods, kwargs,
firstresult=hook.spec_opts.get('firstresult'),
)

def _hookexec(self, hook, methods, kwargs):
Expand Down Expand Up @@ -528,20 +529,22 @@ def __init__(self, trace):


class _HookCaller(object):
def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None):
def __init__(self, name, hook_execute, specmodule_or_class=None,
spec_opts=None):
self.name = name
self._wrappers = []
self._nonwrappers = []
self._hookexec = hook_execute
self._specmodule_or_class = None
self.argnames = None
self.kwargnames = None
self.multicall = _multicall
self.spec_opts = spec_opts or {}
if specmodule_or_class is not None:
assert spec_opts is not None
self.set_specification(specmodule_or_class, spec_opts)

def has_spec(self):
return hasattr(self, "_specmodule_or_class")
return self._specmodule_or_class is not None

def set_specification(self, specmodule_or_class, spec_opts):
assert not self.has_spec()
Expand All @@ -550,7 +553,7 @@ def set_specification(self, specmodule_or_class, spec_opts):
# get spec arg signature
argnames, self.kwargnames = varnames(specfunc)
self.argnames = ["__multicall__"] + list(argnames)
self.spec_opts = spec_opts
self.spec_opts.update(spec_opts)
if spec_opts.get("historic"):
self._call_history = []

Expand Down Expand Up @@ -606,7 +609,7 @@ def __call__(self, *args, **kwargs):
kwargs.keys())
if notincall:
warnings.warn(
"Argument(s) {0} which are declared in the hookspec "
"Argument(s) {} which are declared in the hookspec "
"can not be found in this hook call"
.format(tuple(notincall)),
stacklevel=2,
Expand Down
15 changes: 6 additions & 9 deletions pluggy/callers.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,16 @@ class _LegacyMultiCall(object):
# so we can remove it soon, allowing to avoid the below recursion
# in execute() and simplify/speed up the execute loop.

def __init__(self, hook_impls, kwargs, specopts={}, hook=None):
self.hook = hook
def __init__(self, hook_impls, kwargs, firstresult=False):
self.hook_impls = hook_impls
self.caller_kwargs = kwargs # come from _HookCaller.__call__()
self.caller_kwargs["__multicall__"] = self
self.specopts = hook.spec_opts if hook else specopts
self.firstresult = firstresult

def execute(self):
caller_kwargs = self.caller_kwargs
self.results = results = []
firstresult = self.specopts.get("firstresult")
firstresult = self.firstresult

while self.hook_impls:
hook_impl = self.hook_impls.pop()
Expand Down Expand Up @@ -144,21 +143,19 @@ def __repr__(self):
return "<_MultiCall %s, kwargs=%r>" % (status, self.caller_kwargs)


def _legacymulticall(hook_impls, caller_kwargs, specopts={}, hook=None):
def _legacymulticall(hook_impls, caller_kwargs, firstresult=False):
return _LegacyMultiCall(
hook_impls, caller_kwargs, specopts=specopts, hook=hook).execute()
hook_impls, caller_kwargs, firstresult=firstresult).execute()


def _multicall(hook_impls, caller_kwargs, specopts={}, hook=None):
def _multicall(hook_impls, caller_kwargs, firstresult=False):
"""Execute a call into multiple python functions/methods and return the
result(s).
``caller_kwargs`` comes from _HookCaller.__call__().
"""
__tracebackhide__ = True
specopts = hook.spec_opts if hook else specopts
results = []
firstresult = specopts.get("firstresult")
excinfo = None
try: # run impl and wrapper setup functions in a loop
teardowns = []
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy'] + [
('Programming Language :: Python :: %s' % x) for x in
'2 2.6 2.7 3 3.3 3.4 3.5 3.6'.split()]
'2 2.7 3 3.4 3.5 3.6'.split()]

with open('README.rst') as fd:
long_description = fd.read()
Expand Down
4 changes: 2 additions & 2 deletions testing/test_hookrelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def hello(self, arg):

plugin = Plugin()
pm.register(plugin)
l = hook.hello(arg=3)
assert l == [4]
out = hook.hello(arg=3)
assert out == [4]
assert not hasattr(hook, 'world')
pm.unregister(plugin)
assert hook.hello(arg=3) == []
Expand Down
54 changes: 28 additions & 26 deletions testing/test_method_ordering.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,42 +215,42 @@ def test_load_setuptools_not_installed(monkeypatch, pm):


def test_add_tracefuncs(he_pm):
l = []
out = []

class api1(object):
@hookimpl
def he_method1(self):
l.append("he_method1-api1")
out.append("he_method1-api1")

class api2(object):
@hookimpl
def he_method1(self):
l.append("he_method1-api2")
out.append("he_method1-api2")

he_pm.register(api1())
he_pm.register(api2())

def before(hook_name, hook_impls, kwargs):
l.append((hook_name, list(hook_impls), kwargs))
out.append((hook_name, list(hook_impls), kwargs))

def after(outcome, hook_name, hook_impls, kwargs):
l.append((outcome, hook_name, list(hook_impls), kwargs))
out.append((outcome, hook_name, list(hook_impls), kwargs))

undo = he_pm.add_hookcall_monitoring(before, after)

he_pm.hook.he_method1(arg=1)
assert len(l) == 4
assert l[0][0] == "he_method1"
assert len(l[0][1]) == 2
assert isinstance(l[0][2], dict)
assert l[1] == "he_method1-api2"
assert l[2] == "he_method1-api1"
assert len(l[3]) == 4
assert l[3][1] == l[0][0]
assert len(out) == 4
assert out[0][0] == "he_method1"
assert len(out[0][1]) == 2
assert isinstance(out[0][2], dict)
assert out[1] == "he_method1-api2"
assert out[2] == "he_method1-api1"
assert len(out[3]) == 4
assert out[3][1] == out[0][0]

undo()
he_pm.hook.he_method1(arg=1)
assert len(l) == 4 + 2
assert len(out) == 4 + 2


def test_hook_tracing(he_pm):
Expand All @@ -268,18 +268,18 @@ def he_method1(self):
raise ValueError()

he_pm.register(api1())
l = []
he_pm.trace.root.setwriter(l.append)
out = []
he_pm.trace.root.setwriter(out.append)
undo = he_pm.enable_tracing()
try:
indent = he_pm.trace.root.indent
he_pm.hook.he_method1(arg=1)
assert indent == he_pm.trace.root.indent
assert len(l) == 2
assert 'he_method1' in l[0]
assert 'finish' in l[1]
assert len(out) == 2
assert 'he_method1' in out[0]
assert 'finish' in out[1]

l[:] = []
out[:] = []
he_pm.register(api2())

with pytest.raises(ValueError):
Expand All @@ -290,15 +290,17 @@ def he_method1(self):
undo()


def test_prefix_hookimpl():
@pytest.mark.parametrize('include_hookspec', [True, False])
def test_prefix_hookimpl(include_hookspec):
pm = PluginManager(hookspec.project_name, "hello_")

class HookSpec(object):
@hookspec
def hello_myhook(self, arg1):
""" add to arg1 """
if include_hookspec:
class HookSpec(object):
@hookspec
def hello_myhook(self, arg1):
""" add to arg1 """

pm.add_hookspecs(HookSpec)
pm.add_hookspecs(HookSpec)

class Plugin(object):
def hello_myhook(self, arg1):
Expand Down
46 changes: 23 additions & 23 deletions testing/test_multicall.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@


def test_uses_copy_of_methods():
l = [lambda: 42]
mc = _LegacyMultiCall(l, {})
out = [lambda: 42]
mc = _LegacyMultiCall(out, {})
repr(mc)
l[:] = []
out[:] = []
res = mc.execute()
return res == 42

Expand All @@ -26,7 +26,7 @@ def MC(methods, kwargs, firstresult=False):
hookfuncs.append(f)
if '__multicall__' in f.argnames:
caller = _legacymulticall
return caller(hookfuncs, kwargs, specopts={"firstresult": firstresult})
return caller(hookfuncs, kwargs, firstresult=firstresult)


def test_call_passing():
Expand Down Expand Up @@ -105,53 +105,53 @@ def m1():
def m2():
return None

res = MC([m1, m2], {}, {"firstresult": True})
res = MC([m1, m2], {}, firstresult=True)
assert res == 1
res = MC([m1, m2], {}, {})
assert res == [1]


def test_hookwrapper():
l = []
out = []

@hookimpl(hookwrapper=True)
def m1():
l.append("m1 init")
out.append("m1 init")
yield None
l.append("m1 finish")
out.append("m1 finish")

@hookimpl
def m2():
l.append("m2")
out.append("m2")
return 2

res = MC([m2, m1], {})
assert res == [2]
assert l == ["m1 init", "m2", "m1 finish"]
l[:] = []
res = MC([m2, m1], {}, {"firstresult": True})
assert out == ["m1 init", "m2", "m1 finish"]
out[:] = []
res = MC([m2, m1], {}, firstresult=True)
assert res == 2
assert l == ["m1 init", "m2", "m1 finish"]
assert out == ["m1 init", "m2", "m1 finish"]


def test_hookwrapper_order():
l = []
out = []

@hookimpl(hookwrapper=True)
def m1():
l.append("m1 init")
out.append("m1 init")
yield 1
l.append("m1 finish")
out.append("m1 finish")

@hookimpl(hookwrapper=True)
def m2():
l.append("m2 init")
out.append("m2 init")
yield 2
l.append("m2 finish")
out.append("m2 finish")

res = MC([m2, m1], {})
assert res == []
assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
assert out == ["m1 init", "m2 init", "m2 finish", "m1 finish"]


def test_hookwrapper_not_yield():
Expand All @@ -177,18 +177,18 @@ def m1():

@pytest.mark.parametrize("exc", [ValueError, SystemExit])
def test_hookwrapper_exception(exc):
l = []
out = []

@hookimpl(hookwrapper=True)
def m1():
l.append("m1 init")
out.append("m1 init")
yield None
l.append("m1 finish")
out.append("m1 finish")

@hookimpl
def m2():
raise exc

with pytest.raises(exc):
MC([m2, m1], {})
assert l == ["m1 init", "m1 finish"]
assert out == ["m1 init", "m1 finish"]
Loading

0 comments on commit 451515a

Please sign in to comment.