[mq]: flexcom

--HG--
branch : 1.0.x
This commit is contained in:
holger krekel 2009-08-11 19:00:41 +02:00
parent b552f6eb46
commit 37976be529
13 changed files with 83 additions and 75 deletions

View File

@ -18,8 +18,8 @@ Changes between 1.0.0 and 1.0.1
* terser reporting of collection error tracebacks * terser reporting of collection error tracebacks
* streamlined internal plugin arch code, renamed of internal methods * simplified multicall mechanism and plugin architecture,
and argnames (related to py/_com.py multicall/plugin) renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0 Changes between 1.0.0b9 and 1.0.0
===================================== =====================================

View File

@ -90,13 +90,13 @@ Available py.test hooks
==================================== ====================================
py.test calls hooks functions to implement its `test collection`_, running and py.test calls hooks functions to implement its `test collection`_, running and
reporting process. Upon loading of a plugin py.test performs reporting process. When py.test loads a plugin it validates that all hook functions
strict checking on contained hook functions. Function and argument names conform to the `hook definition specification`_. The hook function name and its
need to match exactly one of `hook definition specification`_. It thus argument names need to match exactly but it is allowed for an implementation
provides useful error reporting on mistyped hook or argument names to accept *less* parameters. You'll get useful errors on mistyped hook or
and minimizes version incompatibilites. Below you find some introductory argument names. Read on for some introductory information on particular
information on particular hooks. It's sensible to look at existing hooks. It's sensible to look at existing plugins so see example usages
plugins so see example usages and start off with your own plugin. and start off with your own plugin.
.. _`hook definition specification`: plugin/hookspec.html .. _`hook definition specification`: plugin/hookspec.html

View File

@ -9,7 +9,8 @@ class MultiCall:
def __init__(self, methods, kwargs, firstresult=False): def __init__(self, methods, kwargs, firstresult=False):
self.methods = methods[:] self.methods = methods[:]
self.kwargs = kwargs self.kwargs = kwargs.copy()
self.kwargs['__multicall__'] = self
self.results = [] self.results = []
self.firstresult = firstresult self.firstresult = firstresult
@ -20,28 +21,30 @@ class MultiCall:
def execute(self): def execute(self):
while self.methods: while self.methods:
method = self.methods.pop() method = self.methods.pop()
res = self._call1(method) kwargs = self.getkwargs(method)
res = method(**kwargs)
if res is not None: if res is not None:
self.results.append(res) self.results.append(res)
if self.firstresult: if self.firstresult:
break return res
if not self.firstresult: if not self.firstresult:
return self.results return self.results
if self.results:
return self.results[-1]
def _call1(self, method): def getkwargs(self, method):
kwargs = self.kwargs kwargs = {}
if '__call__' in varnames(method): for argname in varnames(method):
kwargs = kwargs.copy() try:
kwargs['__call__'] = self kwargs[argname] = self.kwargs[argname]
return method(**kwargs) except KeyError:
pass # might be optional param
return kwargs
def varnames(rawcode): def varnames(rawcode):
ismethod = hasattr(rawcode, 'im_self')
rawcode = getattr(rawcode, 'im_func', rawcode) rawcode = getattr(rawcode, 'im_func', rawcode)
rawcode = getattr(rawcode, 'func_code', rawcode) rawcode = getattr(rawcode, 'func_code', rawcode)
try: try:
return rawcode.co_varnames return rawcode.co_varnames[ismethod:]
except AttributeError: except AttributeError:
return () return ()
@ -101,9 +104,6 @@ class HookRelay:
def _performcall(self, name, multicall): def _performcall(self, name, multicall):
return multicall.execute() return multicall.execute()
def __repr__(self):
return "<HookRelay %r %r>" %(self._hookspecs, self._registry)
class HookCaller: class HookCaller:
def __init__(self, hookrelay, name, firstresult, extralookup=()): def __init__(self, hookrelay, name, firstresult, extralookup=()):
self.hookrelay = hookrelay self.hookrelay = hookrelay
@ -112,12 +112,10 @@ class HookCaller:
self.extralookup = extralookup and [extralookup] or () self.extralookup = extralookup and [extralookup] or ()
def __repr__(self): def __repr__(self):
return "<HookCaller %r firstresult=%s %s>" %( return "<HookCaller %r>" %(self.name,)
self.name, self.firstresult, self.hookrelay)
def __call__(self, **kwargs): def __call__(self, **kwargs):
methods = self.hookrelay._getmethods(self.name, methods = self.hookrelay._getmethods(self.name, self.extralookup)
extralookup=self.extralookup)
mc = MultiCall(methods, kwargs, firstresult=self.firstresult) mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
return self.hookrelay._performcall(self.name, mc) return self.hookrelay._performcall(self.name, mc)

View File

@ -1,8 +1,18 @@
import py import py
import os import os
from py._com import Registry, MultiCall, HookRelay from py.__._com import Registry, MultiCall, HookRelay, varnames
def test_varnames():
def f(x):
pass
class A:
def f(self, y):
pass
assert varnames(f) == ("x",)
assert varnames(A.f) == ('y',)
assert varnames(A().f) == ('y',)
class TestMultiCall: class TestMultiCall:
def test_uses_copy_of_methods(self): def test_uses_copy_of_methods(self):
l = [lambda: 42] l = [lambda: 42]
@ -14,15 +24,15 @@ class TestMultiCall:
def test_call_passing(self): def test_call_passing(self):
class P1: class P1:
def m(self, __call__, x): def m(self, __multicall__, x):
assert len(__call__.results) == 1 assert len(__multicall__.results) == 1
assert not __call__.methods assert not __multicall__.methods
return 17 return 17
class P2: class P2:
def m(self, __call__, x): def m(self, __multicall__, x):
assert __call__.results == [] assert __multicall__.results == []
assert __call__.methods assert __multicall__.methods
return 23 return 23
p1 = P1() p1 = P1()
@ -37,24 +47,23 @@ 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], dict(x=23)) class A:
def f(self, x, y):
return x + y
multicall = MultiCall([f, A().f], dict(x=23, y=24))
assert "'x': 23" in repr(multicall) assert "'x': 23" in repr(multicall)
assert "'y': 24" in repr(multicall)
reslist = multicall.execute() reslist = multicall.execute()
assert reslist == [24] assert reslist == [24+23, 24]
assert "1 results" in repr(multicall) assert "2 results" in repr(multicall)
def test_keywords_call_error(self):
multicall = MultiCall([lambda x: x], {})
py.test.raises(TypeError, "multicall.execute()")
def test_optionalcallarg(self):
class P1:
def m(self, x):
return x
call = MultiCall([P1().m], dict(x=23))
assert "23" in repr(call)
assert call.execute() == [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(__multicall__):
subresult = __call__.execute() subresult = __multicall__.execute()
return subresult + 1 return subresult + 1
def n(): def n():

View File

@ -77,8 +77,8 @@ class DSession(Session):
self.item2nodes = {} self.item2nodes = {}
super(DSession, self).__init__(config=config) super(DSession, self).__init__(config=config)
#def pytest_configure(self, __call__, config): #def pytest_configure(self, __multicall__, config):
# __call__.execute() # __multicall__.execute()
# try: # try:
# config.getxspecs() # config.getxspecs()
# except config.Error: # except config.Error:

View File

@ -181,11 +181,11 @@ class CaptureManager:
capfuncarg._finalize() capfuncarg._finalize()
del self._capturing_funcargs del self._capturing_funcargs
def pytest_make_collect_report(self, __call__, collector): def pytest_make_collect_report(self, __multicall__, collector):
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() rep = __multicall__.execute()
finally: finally:
outerr = self.suspendcapture() outerr = self.suspendcapture()
addouterr(rep, outerr) addouterr(rep, outerr)
@ -204,11 +204,11 @@ class CaptureManager:
def pytest_runtest_teardown(self, item): def pytest_runtest_teardown(self, item):
self.resumecapture_item(item) self.resumecapture_item(item)
def pytest__teardown_final(self, __call__, session): def pytest__teardown_final(self, __multicall__, session):
method = self._getmethod(session.config, None) method = self._getmethod(session.config, None)
self.resumecapture(method) self.resumecapture(method)
try: try:
rep = __call__.execute() rep = __multicall__.execute()
finally: finally:
outerr = self.suspendcapture() outerr = self.suspendcapture()
if rep: if rep:
@ -219,9 +219,9 @@ class CaptureManager:
if hasattr(self, '_capturing'): if hasattr(self, '_capturing'):
self.suspendcapture() self.suspendcapture()
def pytest_runtest_makereport(self, __call__, item, call): def pytest_runtest_makereport(self, __multicall__, item, call):
self.deactivate_funcargs() self.deactivate_funcargs()
rep = __call__.execute() rep = __multicall__.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:

View File

@ -2,8 +2,8 @@
import py import py
def pytest_pyfunc_call(__call__, pyfuncitem): def pytest_pyfunc_call(__multicall__, pyfuncitem):
if not __call__.execute(): if not __multicall__.execute():
testfunction = pyfuncitem.obj testfunction = pyfuncitem.obj
if pyfuncitem._isyieldedfunction(): if pyfuncitem._isyieldedfunction():
testfunction(*pyfuncitem._args) testfunction(*pyfuncitem._args)

View File

@ -32,10 +32,10 @@ class Execnetcleanup:
#for gw in l: #for gw in l:
# gw.join() # gw.join()
def pytest_pyfunc_call(self, __call__, pyfuncitem): def pytest_pyfunc_call(self, __multicall__, pyfuncitem):
if self._gateways is not None: if self._gateways is not None:
gateways = self._gateways[:] gateways = self._gateways[:]
res = __call__.execute() res = __multicall__.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

View File

@ -47,7 +47,7 @@ import py
import inspect import inspect
import sys import sys
def pytest_runtest_makereport(__call__, item, call): def pytest_runtest_makereport(__multicall__, item, call):
SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None) SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
if SkipTest: if SkipTest:
if call.excinfo and call.excinfo.errisinstance(SkipTest): if call.excinfo and call.excinfo.errisinstance(SkipTest):

View File

@ -33,9 +33,9 @@ def pytest_addoption(parser):
type="choice", choices=['failed', 'all'], type="choice", choices=['failed', 'all'],
help="send failed|all info to Pocoo pastebin service.") help="send failed|all info to Pocoo pastebin service.")
def pytest_configure(__call__, config): def pytest_configure(__multicall__, config):
import tempfile import tempfile
__call__.execute() __multicall__.execute()
if config.option.pastebin == "all": if config.option.pastebin == "all":
config._pastebinfile = tempfile.TemporaryFile() config._pastebinfile = tempfile.TemporaryFile()
tr = config.pluginmanager.impname2plugin['terminalreporter'] tr = config.pluginmanager.impname2plugin['terminalreporter']

View File

@ -250,8 +250,8 @@ class TerminalReporter:
for i, testarg in py.builtin.enumerate(self.config.args): for i, testarg in py.builtin.enumerate(self.config.args):
self.write_line("test object %d: %s" %(i+1, testarg)) self.write_line("test object %d: %s" %(i+1, testarg))
def pytest_sessionfinish(self, __call__, session, exitstatus): def pytest_sessionfinish(self, exitstatus, __multicall__):
__call__.execute() __multicall__.execute()
self._tw.line("") self._tw.line("")
if exitstatus in (0, 1, 2): if exitstatus in (0, 1, 2):
self.summary_errors() self.summary_errors()

View File

@ -19,12 +19,12 @@ when it fails. Instead terminal reporting will list it in the
import py import py
def pytest_runtest_makereport(__call__, item, call): def pytest_runtest_makereport(__multicall__, item, call):
if call.when != "call": if call.when != "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() res = __multicall__.execute()
if call.excinfo: if call.excinfo:
res.skipped = True res.skipped = True
res.failed = res.passed = False res.failed = res.passed = False

View File

@ -134,15 +134,16 @@ class PluginManager(object):
fail = True fail = True
else: else:
method_args = getargs(method) method_args = getargs(method)
if '__call__' in method_args: if '__multicall__' in method_args:
method_args.remove('__call__') method_args.remove('__multicall__')
hook = hooks[name] hook = hooks[name]
hookargs = getargs(hook) hookargs = getargs(hook)
for arg, hookarg in zip(method_args, hookargs): for arg in method_args:
if arg != hookarg: if arg not in hookargs:
Print("argument mismatch: %r != %r" %(arg, hookarg)) Print("argument %r not available" %(arg, ))
Print("actual : %s" %(formatdef(method))) Print("actual definition: %s" %(formatdef(method)))
Print("required:", formatdef(hook)) Print("available hook arguments: %s" %
", ".join(hookargs))
fail = True fail = True
break break
#if not fail: #if not fail: