simplify internal plugin dispatching code, rename parts of the py._com plugin helpers
--HG-- branch : 1.0.x
This commit is contained in:
parent
5c8df1d4ca
commit
a01e4769cc
11
CHANGELOG
11
CHANGELOG
|
@ -1,18 +1,21 @@
|
||||||
Changes between 1.0.0 and 1.0.1
|
Changes between 1.0.0 and 1.0.1
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
* various unicode fixes: capturing and prints of unicode strings now
|
* unicode fixes: capturing and unicode writes to sys.stdout
|
||||||
work within tests, they are encoded as "utf8" by default, terminalwriting
|
(through e.g a print statement) now work within tests,
|
||||||
|
they are encoded as "utf8" by default, also terminalwriting
|
||||||
was adapted and somewhat unified between windows and linux
|
was adapted and somewhat unified between windows and linux
|
||||||
|
|
||||||
* fix issue #27: better reporting on non-collectable items given on commandline
|
* fix issue #27: better reporting on non-collectable items given on commandline
|
||||||
(e.g. pyc files)
|
(e.g. pyc files)
|
||||||
|
|
||||||
* "Test" prefixed classes with an __init__ method are *not* collected by default anymore
|
* "Test" prefixed classes are *not* collected by default anymore if they
|
||||||
|
have an __init__ method
|
||||||
|
|
||||||
* terser reporting of collection error tracebacks
|
* terser reporting of collection error tracebacks
|
||||||
|
|
||||||
* renaming of arguments to some special rather internal hooks
|
* streamlined internal plugin arch code, renamed of internal methods
|
||||||
|
and argnames (related to py/_com.py multicall/plugin)
|
||||||
|
|
||||||
Changes between 1.0.0b9 and 1.0.0
|
Changes between 1.0.0b9 and 1.0.0
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
@ -52,7 +52,7 @@ initpkg(__name__,
|
||||||
'_com.Registry' : ('./_com.py', 'Registry'),
|
'_com.Registry' : ('./_com.py', 'Registry'),
|
||||||
'_com.MultiCall' : ('./_com.py', 'MultiCall'),
|
'_com.MultiCall' : ('./_com.py', 'MultiCall'),
|
||||||
'_com.comregistry' : ('./_com.py', 'comregistry'),
|
'_com.comregistry' : ('./_com.py', 'comregistry'),
|
||||||
'_com.Hooks' : ('./_com.py', 'Hooks'),
|
'_com.HookRelay' : ('./_com.py', 'HookRelay'),
|
||||||
|
|
||||||
# py lib cmdline tools
|
# py lib cmdline tools
|
||||||
'cmdline.pytest' : ('./cmdline/pytest.py', 'main',),
|
'cmdline.pytest' : ('./cmdline/pytest.py', 'main',),
|
||||||
|
|
135
py/_com.py
135
py/_com.py
|
@ -5,77 +5,50 @@ py lib plugins and plugin call management
|
||||||
import py
|
import py
|
||||||
|
|
||||||
class MultiCall:
|
class MultiCall:
|
||||||
""" Manage a specific call into many python functions/methods.
|
""" execute a call into multiple python functions/methods. """
|
||||||
|
|
||||||
Simple example:
|
def __init__(self, methods, kwargs, firstresult=False):
|
||||||
MultiCall([list1.append, list2.append], 42).execute()
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, methods, *args, **kwargs):
|
|
||||||
self.methods = methods[:]
|
self.methods = methods[:]
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self.firstresult = firstresult
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
args = []
|
status = "%d results, %d meths" % (len(self.results), len(self.methods))
|
||||||
if self.args:
|
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
||||||
args.append("posargs=%r" %(self.args,))
|
|
||||||
kw = self.kwargs
|
|
||||||
args.append(", ".join(["%s=%r" % x for x in self.kwargs.items()]))
|
|
||||||
args = " ".join(args)
|
|
||||||
status = "results: %r, rmethods: %r" % (self.results, self.methods)
|
|
||||||
return "<MultiCall %s %s>" %(args, status)
|
|
||||||
|
|
||||||
def execute(self, firstresult=False):
|
def execute(self):
|
||||||
while self.methods:
|
while self.methods:
|
||||||
currentmethod = self.methods.pop()
|
method = self.methods.pop()
|
||||||
res = self.execute_method(currentmethod)
|
res = self._call1(method)
|
||||||
if hasattr(self, '_ex1'):
|
|
||||||
self.results = [res]
|
|
||||||
break
|
|
||||||
if res is not None:
|
if res is not None:
|
||||||
self.results.append(res)
|
self.results.append(res)
|
||||||
if firstresult:
|
if self.firstresult:
|
||||||
break
|
break
|
||||||
if not firstresult:
|
if not self.firstresult:
|
||||||
return self.results
|
return self.results
|
||||||
if self.results:
|
if self.results:
|
||||||
return self.results[-1]
|
return self.results[-1]
|
||||||
|
|
||||||
def execute_method(self, currentmethod):
|
def _call1(self, method):
|
||||||
self.currentmethod = currentmethod
|
kwargs = self.kwargs
|
||||||
# provide call introspection if "__call__" is the first positional argument
|
if '__call__' in varnames(method):
|
||||||
if hasattr(currentmethod, 'im_self'):
|
kwargs = kwargs.copy()
|
||||||
varnames = currentmethod.im_func.func_code.co_varnames
|
kwargs['__call__'] = self
|
||||||
needscall = varnames[1:2] == ('__call__',)
|
return method(**kwargs)
|
||||||
else:
|
|
||||||
|
def varnames(rawcode):
|
||||||
|
rawcode = getattr(rawcode, 'im_func', rawcode)
|
||||||
|
rawcode = getattr(rawcode, 'func_code', rawcode)
|
||||||
try:
|
try:
|
||||||
varnames = currentmethod.func_code.co_varnames
|
return rawcode.co_varnames
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# builtin function
|
return ()
|
||||||
varnames = ()
|
|
||||||
needscall = varnames[:1] == ('__call__',)
|
|
||||||
if needscall:
|
|
||||||
return currentmethod(self, *self.args, **self.kwargs)
|
|
||||||
else:
|
|
||||||
#try:
|
|
||||||
return currentmethod(*self.args, **self.kwargs)
|
|
||||||
#except TypeError:
|
|
||||||
# print currentmethod.__module__, currentmethod.__name__, self.args, self.kwargs
|
|
||||||
# raise
|
|
||||||
|
|
||||||
def exclude_other_results(self):
|
|
||||||
self._ex1 = True
|
|
||||||
|
|
||||||
|
|
||||||
class Registry:
|
class Registry:
|
||||||
"""
|
"""
|
||||||
Manage Plugins: Load plugins and manage calls to plugins.
|
Manage Plugins: register/unregister call calls to plugins.
|
||||||
"""
|
"""
|
||||||
logfile = None
|
|
||||||
MultiCall = MultiCall
|
|
||||||
|
|
||||||
def __init__(self, plugins=None):
|
def __init__(self, plugins=None):
|
||||||
if plugins is None:
|
if plugins is None:
|
||||||
plugins = []
|
plugins = []
|
||||||
|
@ -83,6 +56,7 @@ class Registry:
|
||||||
|
|
||||||
def register(self, plugin):
|
def register(self, plugin):
|
||||||
assert not isinstance(plugin, str)
|
assert not isinstance(plugin, str)
|
||||||
|
assert not plugin in self._plugins
|
||||||
self._plugins.append(plugin)
|
self._plugins.append(plugin)
|
||||||
|
|
||||||
def unregister(self, plugin):
|
def unregister(self, plugin):
|
||||||
|
@ -107,45 +81,44 @@ class Registry:
|
||||||
l.reverse()
|
l.reverse()
|
||||||
return l
|
return l
|
||||||
|
|
||||||
class Hooks:
|
class HookRelay:
|
||||||
def __init__(self, hookspecs, registry=None):
|
def __init__(self, hookspecs, registry):
|
||||||
self._hookspecs = hookspecs
|
self._hookspecs = hookspecs
|
||||||
if registry is None:
|
self._registry = registry
|
||||||
registry = py._com.comregistry
|
|
||||||
self.registry = registry
|
|
||||||
for name, method in vars(hookspecs).items():
|
for name, method in vars(hookspecs).items():
|
||||||
if name[:1] != "_":
|
if name[:1] != "_":
|
||||||
firstresult = getattr(method, 'firstresult', False)
|
setattr(self, name, self._makecall(name))
|
||||||
mm = HookCall(registry, name, firstresult=firstresult)
|
|
||||||
setattr(self, name, mm)
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Hooks %r %r>" %(self._hookspecs, self.registry)
|
|
||||||
|
|
||||||
class HookCall:
|
def _makecall(self, name, extralookup=None):
|
||||||
def __init__(self, registry, name, firstresult, extralookup=None):
|
hookspecmethod = getattr(self._hookspecs, name)
|
||||||
self.registry = registry
|
firstresult = getattr(hookspecmethod, 'firstresult', False)
|
||||||
|
return HookCaller(self, name, firstresult=firstresult,
|
||||||
|
extralookup=extralookup)
|
||||||
|
|
||||||
|
def _getmethods(self, name, extralookup=()):
|
||||||
|
return self._registry.listattr(name, extra=extralookup)
|
||||||
|
|
||||||
|
def _performcall(self, name, multicall):
|
||||||
|
return multicall.execute()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<HookRelay %r %r>" %(self._hookspecs, self._registry)
|
||||||
|
|
||||||
|
class HookCaller:
|
||||||
|
def __init__(self, hookrelay, name, firstresult, extralookup=()):
|
||||||
|
self.hookrelay = hookrelay
|
||||||
self.name = name
|
self.name = name
|
||||||
self.firstresult = firstresult
|
self.firstresult = firstresult
|
||||||
self.extralookup = extralookup and [extralookup] or ()
|
self.extralookup = extralookup and [extralookup] or ()
|
||||||
|
|
||||||
def clone(self, extralookup):
|
|
||||||
return HookCall(self.registry, self.name, self.firstresult, extralookup)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
mode = self.firstresult and "firstresult" or "each"
|
return "<HookCaller %r firstresult=%s %s>" %(
|
||||||
return "<HookCall %r mode=%s %s>" %(self.name, mode, self.registry)
|
self.name, self.firstresult, self.hookrelay)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, **kwargs):
|
||||||
if args:
|
methods = self.hookrelay._getmethods(self.name,
|
||||||
raise TypeError("only keyword arguments allowed "
|
extralookup=self.extralookup)
|
||||||
"for api call to %r" % self.name)
|
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||||
attr = self.registry.listattr(self.name, extra=self.extralookup)
|
return self.hookrelay._performcall(self.name, mc)
|
||||||
mc = MultiCall(attr, **kwargs)
|
|
||||||
# XXX this should be doable from a hook impl:
|
|
||||||
if self.registry.logfile:
|
|
||||||
self.registry.logfile.write("%s(**%s) # firstresult=%s\n" %
|
|
||||||
(self.name, kwargs, self.firstresult))
|
|
||||||
self.registry.logfile.flush()
|
|
||||||
return mc.execute(firstresult=self.firstresult)
|
|
||||||
|
|
||||||
comregistry = Registry()
|
comregistry = Registry([])
|
||||||
|
|
|
@ -88,8 +88,8 @@ class Gateway(object):
|
||||||
self._channelfactory = ChannelFactory(self, _startcount)
|
self._channelfactory = ChannelFactory(self, _startcount)
|
||||||
self._cleanup.register(self)
|
self._cleanup.register(self)
|
||||||
if _startcount == 1: # only import 'py' on the "client" side
|
if _startcount == 1: # only import 'py' on the "client" side
|
||||||
from py._com import Hooks
|
import py
|
||||||
self.hook = Hooks(ExecnetAPI)
|
self.hook = py._com.HookRelay(ExecnetAPI, py._com.comregistry)
|
||||||
else:
|
else:
|
||||||
self.hook = ExecnetAPI()
|
self.hook = ExecnetAPI()
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ class GatewayManager:
|
||||||
if not spec.chdir and not spec.popen:
|
if not spec.chdir and not spec.popen:
|
||||||
spec.chdir = defaultchdir
|
spec.chdir = defaultchdir
|
||||||
self.specs.append(spec)
|
self.specs.append(spec)
|
||||||
self.hook = py._com.Hooks(py.execnet._HookSpecs)
|
self.hook = py._com.HookRelay(
|
||||||
|
py.execnet._HookSpecs, py._com.comregistry)
|
||||||
|
|
||||||
def makegateways(self):
|
def makegateways(self):
|
||||||
assert not self.gateways
|
assert not self.gateways
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import os
|
import os
|
||||||
from py._com import Registry, MultiCall
|
from py._com import Registry, MultiCall, HookRelay
|
||||||
from py._com import Hooks
|
|
||||||
|
|
||||||
pytest_plugins = "xfail"
|
|
||||||
|
|
||||||
class TestMultiCall:
|
class TestMultiCall:
|
||||||
def test_uses_copy_of_methods(self):
|
def test_uses_copy_of_methods(self):
|
||||||
l = [lambda: 42]
|
l = [lambda: 42]
|
||||||
mc = MultiCall(l)
|
mc = MultiCall(l, {})
|
||||||
repr(mc)
|
repr(mc)
|
||||||
l[:] = []
|
l[:] = []
|
||||||
res = mc.execute()
|
res = mc.execute()
|
||||||
|
@ -18,22 +15,19 @@ class TestMultiCall:
|
||||||
def test_call_passing(self):
|
def test_call_passing(self):
|
||||||
class P1:
|
class P1:
|
||||||
def m(self, __call__, x):
|
def m(self, __call__, x):
|
||||||
assert __call__.currentmethod == self.m
|
|
||||||
assert len(__call__.results) == 1
|
assert len(__call__.results) == 1
|
||||||
assert not __call__.methods
|
assert not __call__.methods
|
||||||
return 17
|
return 17
|
||||||
|
|
||||||
class P2:
|
class P2:
|
||||||
def m(self, __call__, x):
|
def m(self, __call__, x):
|
||||||
assert __call__.currentmethod == self.m
|
|
||||||
assert __call__.args
|
|
||||||
assert __call__.results == []
|
assert __call__.results == []
|
||||||
assert __call__.methods
|
assert __call__.methods
|
||||||
return 23
|
return 23
|
||||||
|
|
||||||
p1 = P1()
|
p1 = P1()
|
||||||
p2 = P2()
|
p2 = P2()
|
||||||
multicall = MultiCall([p1.m, p2.m], 23)
|
multicall = MultiCall([p1.m, p2.m], {'x': 23})
|
||||||
assert "23" in repr(multicall)
|
assert "23" in repr(multicall)
|
||||||
reslist = multicall.execute()
|
reslist = multicall.execute()
|
||||||
assert len(reslist) == 2
|
assert len(reslist) == 2
|
||||||
|
@ -43,62 +37,44 @@ class TestMultiCall:
|
||||||
def test_keyword_args(self):
|
def test_keyword_args(self):
|
||||||
def f(x):
|
def f(x):
|
||||||
return x + 1
|
return x + 1
|
||||||
multicall = MultiCall([f], x=23)
|
multicall = MultiCall([f], dict(x=23))
|
||||||
assert "x=23" in repr(multicall)
|
assert "'x': 23" in repr(multicall)
|
||||||
reslist = multicall.execute()
|
reslist = multicall.execute()
|
||||||
assert reslist == [24]
|
assert reslist == [24]
|
||||||
assert "24" in repr(multicall)
|
assert "1 results" in repr(multicall)
|
||||||
|
|
||||||
def test_optionalcallarg(self):
|
def test_optionalcallarg(self):
|
||||||
class P1:
|
class P1:
|
||||||
def m(self, x):
|
def m(self, x):
|
||||||
return x
|
return x
|
||||||
call = MultiCall([P1().m], 23)
|
call = MultiCall([P1().m], dict(x=23))
|
||||||
assert "23" in repr(call)
|
assert "23" in repr(call)
|
||||||
assert call.execute() == [23]
|
assert call.execute() == [23]
|
||||||
assert call.execute(firstresult=True) == 23
|
call = MultiCall([P1().m], dict(x=23), firstresult=True)
|
||||||
|
|
||||||
def test_call_subexecute(self):
|
def test_call_subexecute(self):
|
||||||
def m(__call__):
|
def m(__call__):
|
||||||
subresult = __call__.execute(firstresult=True)
|
subresult = __call__.execute()
|
||||||
return subresult + 1
|
return subresult + 1
|
||||||
|
|
||||||
def n():
|
def n():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
call = MultiCall([n, m])
|
call = MultiCall([n, m], {}, firstresult=True)
|
||||||
res = call.execute(firstresult=True)
|
|
||||||
assert res == 2
|
|
||||||
|
|
||||||
def test_call_exclude_other_results(self):
|
|
||||||
def m(__call__):
|
|
||||||
__call__.exclude_other_results()
|
|
||||||
return 10
|
|
||||||
|
|
||||||
def n():
|
|
||||||
return 1
|
|
||||||
|
|
||||||
call = MultiCall([n, n, m, n])
|
|
||||||
res = call.execute()
|
res = call.execute()
|
||||||
assert res == [10]
|
assert res == 2
|
||||||
# doesn't really make sense for firstresult-mode - because
|
|
||||||
# we might not have had a chance to run at all.
|
|
||||||
#res = call.execute(firstresult=True)
|
|
||||||
#assert res == 10
|
|
||||||
|
|
||||||
def test_call_none_is_no_result(self):
|
def test_call_none_is_no_result(self):
|
||||||
def m1():
|
def m1():
|
||||||
return 1
|
return 1
|
||||||
def m2():
|
def m2():
|
||||||
return None
|
return None
|
||||||
mc = MultiCall([m1, m2])
|
res = MultiCall([m1, m2], {}, firstresult=True).execute()
|
||||||
res = mc.execute(firstresult=True)
|
|
||||||
assert res == 1
|
assert res == 1
|
||||||
|
res = MultiCall([m1, m2], {}).execute()
|
||||||
|
assert res == [1]
|
||||||
|
|
||||||
class TestRegistry:
|
class TestRegistry:
|
||||||
def test_MultiCall(self):
|
|
||||||
plugins = Registry()
|
|
||||||
assert hasattr(plugins, "MultiCall")
|
|
||||||
|
|
||||||
def test_register(self):
|
def test_register(self):
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
|
@ -142,14 +118,14 @@ class TestRegistry:
|
||||||
def test_api_and_defaults():
|
def test_api_and_defaults():
|
||||||
assert isinstance(py._com.comregistry, Registry)
|
assert isinstance(py._com.comregistry, Registry)
|
||||||
|
|
||||||
class TestHooks:
|
class TestHookRelay:
|
||||||
def test_happypath(self):
|
def test_happypath(self):
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||||
assert hasattr(mcm, 'hello')
|
assert hasattr(mcm, 'hello')
|
||||||
assert repr(mcm.hello).find("hello") != -1
|
assert repr(mcm.hello).find("hello") != -1
|
||||||
class Plugin:
|
class Plugin:
|
||||||
|
@ -160,23 +136,21 @@ class TestHooks:
|
||||||
assert l == [4]
|
assert l == [4]
|
||||||
assert not hasattr(mcm, 'world')
|
assert not hasattr(mcm, 'world')
|
||||||
|
|
||||||
def test_needskeywordargs(self):
|
def test_only_kwargs(self):
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
pass
|
pass
|
||||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||||
excinfo = py.test.raises(TypeError, "mcm.hello(3)")
|
py.test.raises(TypeError, "mcm.hello(3)")
|
||||||
assert str(excinfo.value).find("only keyword arguments") != -1
|
|
||||||
assert str(excinfo.value).find("hello(self, arg)")
|
|
||||||
|
|
||||||
def test_firstresult(self):
|
def test_firstresult_definition(self):
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg): pass
|
def hello(self, arg): pass
|
||||||
hello.firstresult = True
|
hello.firstresult = True
|
||||||
|
|
||||||
mcm = Hooks(hookspecs=Api, registry=registry)
|
mcm = HookRelay(hookspecs=Api, registry=registry)
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
|
@ -186,15 +160,16 @@ class TestHooks:
|
||||||
|
|
||||||
def test_default_plugins(self):
|
def test_default_plugins(self):
|
||||||
class Api: pass
|
class Api: pass
|
||||||
mcm = Hooks(hookspecs=Api)
|
mcm = HookRelay(hookspecs=Api, registry=py._com.comregistry)
|
||||||
assert mcm.registry == py._com.comregistry
|
assert mcm._registry == py._com.comregistry
|
||||||
|
|
||||||
def test_hooks_extra_plugins(self):
|
def test_hooks_extra_plugins(self):
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
pass
|
pass
|
||||||
hook_hello = Hooks(hookspecs=Api, registry=registry).hello
|
hookrelay = HookRelay(hookspecs=Api, registry=registry)
|
||||||
|
hook_hello = hookrelay.hello
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
|
@ -202,7 +177,7 @@ class TestHooks:
|
||||||
class Plugin2:
|
class Plugin2:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 2
|
return arg + 2
|
||||||
newhook = hook_hello.clone(extralookup=Plugin2())
|
newhook = hookrelay._makecall("hello", extralookup=Plugin2())
|
||||||
l = newhook(arg=3)
|
l = newhook(arg=3)
|
||||||
assert l == [5, 4]
|
assert l == [5, 4]
|
||||||
l2 = hook_hello(arg=3)
|
l2 = hook_hello(arg=3)
|
||||||
|
|
|
@ -47,7 +47,7 @@ class HookRecorder:
|
||||||
recorder = RecordCalls()
|
recorder = RecordCalls()
|
||||||
self._recorders[hookspecs] = recorder
|
self._recorders[hookspecs] = recorder
|
||||||
self._comregistry.register(recorder)
|
self._comregistry.register(recorder)
|
||||||
self.hook = py._com.Hooks(hookspecs, registry=self._comregistry)
|
self.hook = py._com.HookRelay(hookspecs, registry=self._comregistry)
|
||||||
|
|
||||||
def finish_recording(self):
|
def finish_recording(self):
|
||||||
for recorder in self._recorders.values():
|
for recorder in self._recorders.values():
|
||||||
|
|
|
@ -185,7 +185,7 @@ class CaptureManager:
|
||||||
method = self._getmethod(collector.config, collector.fspath)
|
method = self._getmethod(collector.config, collector.fspath)
|
||||||
self.resumecapture(method)
|
self.resumecapture(method)
|
||||||
try:
|
try:
|
||||||
rep = __call__.execute(firstresult=True)
|
rep = __call__.execute()
|
||||||
finally:
|
finally:
|
||||||
outerr = self.suspendcapture()
|
outerr = self.suspendcapture()
|
||||||
addouterr(rep, outerr)
|
addouterr(rep, outerr)
|
||||||
|
@ -208,7 +208,7 @@ class CaptureManager:
|
||||||
method = self._getmethod(session.config, None)
|
method = self._getmethod(session.config, None)
|
||||||
self.resumecapture(method)
|
self.resumecapture(method)
|
||||||
try:
|
try:
|
||||||
rep = __call__.execute(firstresult=True)
|
rep = __call__.execute()
|
||||||
finally:
|
finally:
|
||||||
outerr = self.suspendcapture()
|
outerr = self.suspendcapture()
|
||||||
if rep:
|
if rep:
|
||||||
|
@ -221,7 +221,7 @@ class CaptureManager:
|
||||||
|
|
||||||
def pytest_runtest_makereport(self, __call__, item, call):
|
def pytest_runtest_makereport(self, __call__, item, call):
|
||||||
self.deactivate_funcargs()
|
self.deactivate_funcargs()
|
||||||
rep = __call__.execute(firstresult=True)
|
rep = __call__.execute()
|
||||||
outerr = self.suspendcapture()
|
outerr = self.suspendcapture()
|
||||||
outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
|
outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
|
||||||
if not rep.passed:
|
if not rep.passed:
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
def pytest_pyfunc_call(__call__, pyfuncitem):
|
def pytest_pyfunc_call(__call__, pyfuncitem):
|
||||||
if not __call__.execute(firstresult=True):
|
if not __call__.execute():
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
if pyfuncitem._isyieldedfunction():
|
if pyfuncitem._isyieldedfunction():
|
||||||
testfunction(*pyfuncitem._args)
|
testfunction(*pyfuncitem._args)
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Execnetcleanup:
|
||||||
def pytest_pyfunc_call(self, __call__, pyfuncitem):
|
def pytest_pyfunc_call(self, __call__, pyfuncitem):
|
||||||
if self._gateways is not None:
|
if self._gateways is not None:
|
||||||
gateways = self._gateways[:]
|
gateways = self._gateways[:]
|
||||||
res = __call__.execute(firstresult=True)
|
res = __call__.execute()
|
||||||
while len(self._gateways) > len(gateways):
|
while len(self._gateways) > len(gateways):
|
||||||
self._gateways[-1].exit()
|
self._gateways[-1].exit()
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -8,14 +8,27 @@ def pytest_addoption(parser):
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
hooklog = config.getvalue("hooklog")
|
hooklog = config.getvalue("hooklog")
|
||||||
if hooklog:
|
if hooklog:
|
||||||
assert not config.pluginmanager.comregistry.logfile
|
config._hooklogfile = open(hooklog, 'w', 0)
|
||||||
config.pluginmanager.comregistry.logfile = open(hooklog, 'w')
|
config._hooklog_oldperformcall = config.hook._performcall
|
||||||
|
config.hook._performcall = (lambda name, multicall:
|
||||||
|
logged_call(name=name, multicall=multicall, config=config))
|
||||||
|
|
||||||
|
def logged_call(name, multicall, config):
|
||||||
|
f = config._hooklogfile
|
||||||
|
f.write("%s(**%s)\n" % (name, multicall.kwargs))
|
||||||
|
try:
|
||||||
|
res = config._hooklog_oldperformcall(name=name, multicall=multicall)
|
||||||
|
except:
|
||||||
|
f.write("-> exception")
|
||||||
|
raise
|
||||||
|
f.write("-> %r" % (res,))
|
||||||
|
return res
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
f = config.pluginmanager.comregistry.logfile
|
try:
|
||||||
if f:
|
del config.hook.__dict__['_performcall']
|
||||||
f.close()
|
except KeyError:
|
||||||
config.pluginmanager.comregistry.logfile = None
|
pass
|
||||||
|
|
||||||
# ===============================================================================
|
# ===============================================================================
|
||||||
# plugin tests
|
# plugin tests
|
||||||
|
|
|
@ -24,7 +24,7 @@ def pytest_runtest_makereport(__call__, item, call):
|
||||||
return
|
return
|
||||||
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
|
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
|
||||||
if 'xfail' in item.obj.func_dict:
|
if 'xfail' in item.obj.func_dict:
|
||||||
res = __call__.execute(firstresult=True)
|
res = __call__.execute()
|
||||||
if call.excinfo:
|
if call.excinfo:
|
||||||
res.skipped = True
|
res.skipped = True
|
||||||
res.failed = res.passed = False
|
res.failed = res.passed = False
|
||||||
|
|
|
@ -16,10 +16,9 @@ class PluginManager(object):
|
||||||
if comregistry is None:
|
if comregistry is None:
|
||||||
comregistry = py._com.Registry()
|
comregistry = py._com.Registry()
|
||||||
self.comregistry = comregistry
|
self.comregistry = comregistry
|
||||||
self.MultiCall = self.comregistry.MultiCall
|
|
||||||
self.impname2plugin = {}
|
self.impname2plugin = {}
|
||||||
|
|
||||||
self.hook = py._com.Hooks(
|
self.hook = py._com.HookRelay(
|
||||||
hookspecs=hookspec,
|
hookspecs=hookspec,
|
||||||
registry=self.comregistry)
|
registry=self.comregistry)
|
||||||
|
|
||||||
|
@ -166,20 +165,24 @@ class PluginManager(object):
|
||||||
return self.hook.pytest_internalerror(excrepr=excrepr)
|
return self.hook.pytest_internalerror(excrepr=excrepr)
|
||||||
|
|
||||||
def do_addoption(self, parser):
|
def do_addoption(self, parser):
|
||||||
methods = self.comregistry.listattr("pytest_addoption", reverse=True)
|
mname = "pytest_addoption"
|
||||||
mc = py._com.MultiCall(methods, parser=parser)
|
methods = self.comregistry.listattr(mname, reverse=True)
|
||||||
|
mc = py._com.MultiCall(methods, {'parser': parser})
|
||||||
mc.execute()
|
mc.execute()
|
||||||
|
|
||||||
def pytest_plugin_registered(self, plugin):
|
def pytest_plugin_registered(self, plugin):
|
||||||
if hasattr(self, '_config'):
|
if hasattr(self, '_config'):
|
||||||
self.call_plugin(plugin, "pytest_addoption", parser=self._config._parser)
|
self.call_plugin(plugin, "pytest_addoption",
|
||||||
self.call_plugin(plugin, "pytest_configure", config=self._config)
|
{'parser': self._config._parser})
|
||||||
|
self.call_plugin(plugin, "pytest_configure",
|
||||||
|
{'config': self._config})
|
||||||
#dic = self.call_plugin(plugin, "pytest_namespace")
|
#dic = self.call_plugin(plugin, "pytest_namespace")
|
||||||
#self._updateext(dic)
|
#self._updateext(dic)
|
||||||
|
|
||||||
def call_plugin(self, plugin, methname, **kwargs):
|
def call_plugin(self, plugin, methname, kwargs):
|
||||||
return self.MultiCall(self.listattr(methname, plugins=[plugin]),
|
return py._com.MultiCall(
|
||||||
**kwargs).execute(firstresult=True)
|
methods=self.listattr(methname, plugins=[plugin]),
|
||||||
|
kwargs=kwargs, firstresult=True).execute()
|
||||||
|
|
||||||
def _updateext(self, dic):
|
def _updateext(self, dic):
|
||||||
if dic:
|
if dic:
|
||||||
|
|
|
@ -155,8 +155,8 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
|
||||||
cls = clscol and clscol.obj or None
|
cls = clscol and clscol.obj or None
|
||||||
metafunc = funcargs.Metafunc(funcobj, config=self.config,
|
metafunc = funcargs.Metafunc(funcobj, config=self.config,
|
||||||
cls=cls, module=module)
|
cls=cls, module=module)
|
||||||
gentesthook = self.config.hook.pytest_generate_tests.clone(
|
gentesthook = self.config.hook._makecall(
|
||||||
extralookup=module)
|
"pytest_generate_tests", extralookup=module)
|
||||||
gentesthook(metafunc=metafunc)
|
gentesthook(metafunc=metafunc)
|
||||||
if not metafunc._calls:
|
if not metafunc._calls:
|
||||||
return self.Function(name, parent=self)
|
return self.Function(name, parent=self)
|
||||||
|
|
|
@ -145,7 +145,7 @@ class TestCollectFS:
|
||||||
names = [x.name for x in col.collect()]
|
names = [x.name for x in col.collect()]
|
||||||
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
||||||
|
|
||||||
class TestCollectPluginHooks:
|
class TestCollectPluginHookRelay:
|
||||||
def test_pytest_collect_file(self, testdir):
|
def test_pytest_collect_file(self, testdir):
|
||||||
tmpdir = testdir.tmpdir
|
tmpdir = testdir.tmpdir
|
||||||
wascalled = []
|
wascalled = []
|
||||||
|
|
|
@ -222,10 +222,6 @@ class TestPytestPluginInteractions:
|
||||||
config.pluginmanager.register(A())
|
config.pluginmanager.register(A())
|
||||||
assert len(l) == 2
|
assert len(l) == 2
|
||||||
|
|
||||||
def test_MultiCall(self):
|
|
||||||
pp = PluginManager()
|
|
||||||
assert hasattr(pp, 'MultiCall')
|
|
||||||
|
|
||||||
# lower level API
|
# lower level API
|
||||||
|
|
||||||
def test_listattr(self):
|
def test_listattr(self):
|
||||||
|
|
Loading…
Reference in New Issue