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