Skip to content

Commit 4c26c7a

Browse files
committed
- bring pluggy to 0.3
- offering a add_hookcall_monitoring interface for adding before/after functions. - a tox.ini that makes pluggy run against pytest27 and pytest trunk
1 parent 532d223 commit 4c26c7a

File tree

4 files changed

+96
-36
lines changed

4 files changed

+96
-36
lines changed

MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include CHANGELOG
2+
include README.rst
3+
include setup.py
4+
include tox.ini
5+
include LICENSE
6+
include test_pluggy.py

pluggy.py

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
import sys
6868
import inspect
6969

70-
__version__ = '0.2.0'
70+
__version__ = '0.3.0'
7171
__all__ = ["PluginManager", "PluginValidationError",
7272
"HookspecMarker", "HookimplMarker"]
7373

@@ -294,10 +294,10 @@ def __init__(self, pluginmanager, before, after):
294294
assert not isinstance(self.oldcall, _TracedHookExecution)
295295
self.pluginmanager._inner_hookexec = self
296296

297-
def __call__(self, hook, methods, kwargs):
298-
self.before(hook, methods, kwargs)
299-
outcome = _CallOutcome(lambda: self.oldcall(hook, methods, kwargs))
300-
self.after(outcome, hook, methods, kwargs)
297+
def __call__(self, hook, hook_impls, kwargs):
298+
self.before(hook.name, hook_impls, kwargs)
299+
outcome = _CallOutcome(lambda: self.oldcall(hook, hook_impls, kwargs))
300+
self.after(outcome, hook.name, hook_impls, kwargs)
301301
return outcome.get_result()
302302

303303
def undo(self):
@@ -337,20 +337,35 @@ def _hookexec(self, hook, methods, kwargs):
337337
# enable_tracing will set its own wrapping function at self._inner_hookexec
338338
return self._inner_hookexec(hook, methods, kwargs)
339339

340+
def add_hookcall_monitoring(self, before, after):
341+
""" add before/after tracing functions for all hooks
342+
and return an undo function which, when called,
343+
will remove the added tracers.
344+
345+
``before(hook_name, hook_impls, kwargs)`` will be called ahead
346+
of all hook calls and receive a hookcaller instance, a list
347+
of HookImpl instances and the keyword arguments for the hook call.
348+
349+
``after(outcome, hook_name, hook_impls, kwargs)`` receives the
350+
same arguments as ``before`` but also a :py:class:`_CallOutcome`` object
351+
which represents the result of the overall hook call.
352+
"""
353+
return _TracedHookExecution(self, before, after).undo
354+
340355
def enable_tracing(self):
341356
""" enable tracing of hook calls and return an undo function. """
342357
hooktrace = self.hook._trace
343358

344-
def before(hook, methods, kwargs):
359+
def before(hook_name, methods, kwargs):
345360
hooktrace.root.indent += 1
346-
hooktrace(hook.name, kwargs)
361+
hooktrace(hook_name, kwargs)
347362

348-
def after(outcome, hook, methods, kwargs):
363+
def after(outcome, hook_name, methods, kwargs):
349364
if outcome.excinfo is None:
350-
hooktrace("finish", hook.name, "-->", outcome.result)
365+
hooktrace("finish", hook_name, "-->", outcome.result)
351366
hooktrace.root.indent -= 1
352367

353-
return _TracedHookExecution(self, before, after).undo
368+
return self.add_hookcall_monitoring(before, after)
354369

355370
def subset_hook_caller(self, name, remove_plugins):
356371
""" Return a new _HookCaller instance for the named method
@@ -394,7 +409,7 @@ def register(self, plugin, name=None):
394409
if hookimpl_opts is not None:
395410
normalize_hookimpl_opts(hookimpl_opts)
396411
method = getattr(plugin, name)
397-
hookimpl = _HookImpl(plugin, plugin_name, method, hookimpl_opts)
412+
hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
398413
hook = getattr(self.hook, name, None)
399414
if hook is None:
400415
hook = _HookCaller(name, self._hookexec)
@@ -561,8 +576,8 @@ class _MultiCall:
561576
# so we can remove it soon, allowing to avoid the below recursion
562577
# in execute() and simplify/speed up the execute loop.
563578

564-
def __init__(self, methods, kwargs, specopts={}):
565-
self.methods = methods
579+
def __init__(self, hook_impls, kwargs, specopts={}):
580+
self.hook_impls = hook_impls
566581
self.kwargs = kwargs
567582
self.kwargs["__multicall__"] = self
568583
self.specopts = specopts
@@ -572,12 +587,12 @@ def execute(self):
572587
self.results = results = []
573588
firstresult = self.specopts.get("firstresult")
574589

575-
while self.methods:
576-
method = self.methods.pop()
577-
args = [all_kwargs[argname] for argname in method.argnames]
578-
if method.hookwrapper:
579-
return _wrapped_call(method.function(*args), self.execute)
580-
res = method.function(*args)
590+
while self.hook_impls:
591+
hook_impl = self.hook_impls.pop()
592+
args = [all_kwargs[argname] for argname in hook_impl.argnames]
593+
if hook_impl.hookwrapper:
594+
return _wrapped_call(hook_impl.function(*args), self.execute)
595+
res = hook_impl.function(*args)
581596
if res is not None:
582597
if firstresult:
583598
return res
@@ -587,7 +602,7 @@ def execute(self):
587602
return results
588603

589604
def __repr__(self):
590-
status = "%d meths" % (len(self.methods),)
605+
status = "%d meths" % (len(self.hook_impls),)
591606
if hasattr(self, "results"):
592607
status = ("%d results, " % len(self.results)) + status
593608
return "<_MultiCall %s, kwargs=%r>" % (status, self.kwargs)
@@ -718,7 +733,7 @@ def call_extra(self, methods, kwargs):
718733
old = list(self._nonwrappers), list(self._wrappers)
719734
for method in methods:
720735
opts = dict(hookwrapper=False, trylast=False, tryfirst=False)
721-
hookimpl = _HookImpl(None, "<temp>", method, opts)
736+
hookimpl = HookImpl(None, "<temp>", method, opts)
722737
self._add_hookimpl(hookimpl)
723738
try:
724739
return self(**kwargs)
@@ -733,7 +748,7 @@ def _maybe_apply_history(self, method):
733748
proc(res[0])
734749

735750

736-
class _HookImpl:
751+
class HookImpl:
737752
def __init__(self, plugin, plugin_name, function, hook_impl_opts):
738753
self.function = function
739754
self.argnames = varnames(self.function)

test_pluggy.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pluggy import (PluginManager, varnames, PluginValidationError,
77
HookimplMarker, HookspecMarker)
88

9-
from pluggy import (_MultiCall, _TagTracer, _HookImpl)
9+
from pluggy import (_MultiCall, _TagTracer, HookImpl)
1010

1111
hookspec = HookspecMarker("example")
1212
hookimpl = HookimplMarker("example")
@@ -305,7 +305,7 @@ def addmeth(tryfirst=False, trylast=False, hookwrapper=False):
305305
def wrap(func):
306306
hookimpl(tryfirst=tryfirst, trylast=trylast,
307307
hookwrapper=hookwrapper)(func)
308-
hc._add_hookimpl(_HookImpl(None, "<temp>", func, func.example_impl))
308+
hc._add_hookimpl(HookImpl(None, "<temp>", func, func.example_impl))
309309
return func
310310
return wrap
311311
return addmeth
@@ -497,6 +497,44 @@ def test_load_setuptools_not_installed(self, monkeypatch, pm):
497497
with pytest.raises(ImportError):
498498
pm.load_setuptools_entrypoints("qwe")
499499

500+
def test_add_tracefuncs(self, he_pm):
501+
l = []
502+
503+
class api1:
504+
@hookimpl
505+
def he_method1(self):
506+
l.append("he_method1-api1")
507+
508+
class api2:
509+
@hookimpl
510+
def he_method1(self):
511+
l.append("he_method1-api2")
512+
513+
he_pm.register(api1())
514+
he_pm.register(api2())
515+
516+
def before(hook_name, hook_impls, kwargs):
517+
l.append((hook_name, list(hook_impls), kwargs))
518+
519+
def after(outcome, hook_name, hook_impls, kwargs):
520+
l.append((outcome, hook_name, list(hook_impls), kwargs))
521+
522+
undo = he_pm.add_hookcall_monitoring(before, after)
523+
524+
he_pm.hook.he_method1()
525+
assert len(l) == 4
526+
assert l[0][0] == "he_method1"
527+
assert len(l[0][1]) == 2
528+
assert isinstance(l[0][2], dict)
529+
assert l[1] == "he_method1-api2"
530+
assert l[2] == "he_method1-api1"
531+
assert len(l[3]) == 4
532+
assert l[3][1] == l[0][0]
533+
534+
undo()
535+
he_pm.hook.he_method1()
536+
assert len(l) == 4 + 2
537+
500538
def test_hook_tracing(self, he_pm):
501539
saveindent = []
502540

@@ -639,7 +677,7 @@ def test_uses_copy_of_methods(self):
639677
def MC(self, methods, kwargs, firstresult=False):
640678
hookfuncs = []
641679
for method in methods:
642-
f = _HookImpl(None, "<temp>", method, method.example_impl)
680+
f = HookImpl(None, "<temp>", method, method.example_impl)
643681
hookfuncs.append(f)
644682
return _MultiCall(hookfuncs, kwargs, {"firstresult": firstresult})
645683

@@ -648,14 +686,14 @@ class P1:
648686
@hookimpl
649687
def m(self, __multicall__, x):
650688
assert len(__multicall__.results) == 1
651-
assert not __multicall__.methods
689+
assert not __multicall__.hook_impls
652690
return 17
653691

654692
class P2:
655693
@hookimpl
656694
def m(self, __multicall__, x):
657695
assert __multicall__.results == []
658-
assert __multicall__.methods
696+
assert __multicall__.hook_impls
659697
return 23
660698

661699
p1 = P1()

tox.ini

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
[tox]
2-
envlist=pep8,flakes,py26,py27,py34,pypy
2+
envlist=check,{py26,py27,py34,pypy}-{pytest27,pytest}
33

44
[testenv]
55
commands= py.test {posargs:test_pluggy.py}
66
deps=
7-
pytest
7+
pytest27: pytest>=2.7.0,<2.8.0
8+
pytest: pytest>=2.8.0.dev1
89

9-
[testenv:pep8]
10-
deps = pytest-pep8
11-
commands = py.test --pep8
12-
13-
[testenv:flakes]
10+
[testenv:check]
1411
usedevelop=True
15-
deps = pytest-flakes
16-
commands = py.test --flakes -m flakes pluggy.py test_pluggy.py
12+
deps = pytest>=2.7.0,<2.8.0
13+
pytest-pep8
14+
pytest-flakes
15+
commands =
16+
py.test --pep8
17+
py.test --flakes -m flakes --pyargs pluggy test_pluggy
1718

1819
[pytest]
1920
minversion=2.0

0 commit comments

Comments
 (0)