[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
* streamlined internal plugin arch code, renamed of internal methods
and argnames (related to py/_com.py multicall/plugin)
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
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
reporting process. Upon loading of a plugin py.test performs
strict checking on contained hook functions. Function and argument names
need to match exactly one of `hook definition specification`_. It thus
provides useful error reporting on mistyped hook or argument names
and minimizes version incompatibilites. Below you find some introductory
information on particular hooks. It's sensible to look at existing
plugins so see example usages and start off with your own plugin.
reporting process. When py.test loads a plugin it validates that all hook functions
conform to the `hook definition specification`_. The hook function name and its
argument names need to match exactly but it is allowed for an implementation
to accept *less* parameters. You'll get useful errors on mistyped hook or
argument names. Read on for some introductory information on particular
hooks. It's sensible to look at existing plugins so see example usages
and start off with your own plugin.
.. _`hook definition specification`: plugin/hookspec.html

View File

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

View File

@ -1,7 +1,17 @@
import py
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:
def test_uses_copy_of_methods(self):
@ -14,15 +24,15 @@ class TestMultiCall:
def test_call_passing(self):
class P1:
def m(self, __call__, x):
assert len(__call__.results) == 1
assert not __call__.methods
def m(self, __multicall__, x):
assert len(__multicall__.results) == 1
assert not __multicall__.methods
return 17
class P2:
def m(self, __call__, x):
assert __call__.results == []
assert __call__.methods
def m(self, __multicall__, x):
assert __multicall__.results == []
assert __multicall__.methods
return 23
p1 = P1()
@ -37,24 +47,23 @@ class TestMultiCall:
def test_keyword_args(self):
def f(x):
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 "'y': 24" in repr(multicall)
reslist = multicall.execute()
assert reslist == [24]
assert "1 results" in repr(multicall)
assert reslist == [24+23, 24]
assert "2 results" in repr(multicall)
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_keywords_call_error(self):
multicall = MultiCall([lambda x: x], {})
py.test.raises(TypeError, "multicall.execute()")
def test_call_subexecute(self):
def m(__call__):
subresult = __call__.execute()
def m(__multicall__):
subresult = __multicall__.execute()
return subresult + 1
def n():

View File

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

View File

@ -181,11 +181,11 @@ class CaptureManager:
capfuncarg._finalize()
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)
self.resumecapture(method)
try:
rep = __call__.execute()
rep = __multicall__.execute()
finally:
outerr = self.suspendcapture()
addouterr(rep, outerr)
@ -204,11 +204,11 @@ class CaptureManager:
def pytest_runtest_teardown(self, 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)
self.resumecapture(method)
try:
rep = __call__.execute()
rep = __multicall__.execute()
finally:
outerr = self.suspendcapture()
if rep:
@ -219,9 +219,9 @@ class CaptureManager:
if hasattr(self, '_capturing'):
self.suspendcapture()
def pytest_runtest_makereport(self, __call__, item, call):
def pytest_runtest_makereport(self, __multicall__, item, call):
self.deactivate_funcargs()
rep = __call__.execute()
rep = __multicall__.execute()
outerr = self.suspendcapture()
outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
if not rep.passed:

View File

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

View File

@ -32,10 +32,10 @@ class Execnetcleanup:
#for gw in l:
# gw.join()
def pytest_pyfunc_call(self, __call__, pyfuncitem):
def pytest_pyfunc_call(self, __multicall__, pyfuncitem):
if self._gateways is not None:
gateways = self._gateways[:]
res = __call__.execute()
res = __multicall__.execute()
while len(self._gateways) > len(gateways):
self._gateways[-1].exit()
return res

View File

@ -47,7 +47,7 @@ import py
import inspect
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)
if SkipTest:
if call.excinfo and call.excinfo.errisinstance(SkipTest):

View File

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

View File

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

View File

@ -19,12 +19,12 @@ when it fails. Instead terminal reporting will list it in the
import py
def pytest_runtest_makereport(__call__, item, call):
def pytest_runtest_makereport(__multicall__, item, call):
if call.when != "call":
return
if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'):
if 'xfail' in item.obj.func_dict:
res = __call__.execute()
res = __multicall__.execute()
if call.excinfo:
res.skipped = True
res.failed = res.passed = False

View File

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