remove/reduce internal global state: py._com.registry is now fully contained and always instantiated from the py.test PluginManager class.

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-12-29 12:36:17 +01:00
parent 8737254a74
commit 425e4849f3
14 changed files with 351 additions and 406 deletions

View File

@ -14,6 +14,5 @@ def pytest(argv=None):
except SystemExit: except SystemExit:
pass pass
# we need to reset the global py.test.config object # we need to reset the global py.test.config object
py._com.comregistry = py._com.comregistry.__class__([])
py.test.config = py.test.config.__class__( py.test.config = py.test.config.__class__(
pluginmanager=py.test._PluginManager(py._com.comregistry)) pluginmanager=py.test._PluginManager())

View File

@ -25,12 +25,6 @@ py.apipkg.initpkg(__name__, dict(
_pydirs = '.impl._metainfo:pydirs', _pydirs = '.impl._metainfo:pydirs',
version = 'py:__version__', # backward compatibility version = 'py:__version__', # backward compatibility
_com = {
'Registry': '.impl._com:Registry',
'MultiCall': '.impl._com:MultiCall',
'comregistry': '.impl._com:comregistry',
'HookRelay': '.impl._com:HookRelay',
},
cmdline = { cmdline = {
'pytest': '.impl.cmdline.pytest:main', 'pytest': '.impl.cmdline.pytest:main',
'pylookup': '.impl.cmdline.pylookup:main', 'pylookup': '.impl.cmdline.pylookup:main',

View File

@ -1,125 +0,0 @@
"""
py lib plugins and plugin call management
"""
import py
import inspect
__all__ = ['Registry', 'MultiCall', 'comregistry', 'HookRelay']
class MultiCall:
""" execute a call into multiple python functions/methods. """
def __init__(self, methods, kwargs, firstresult=False):
self.methods = methods[:]
self.kwargs = kwargs.copy()
self.kwargs['__multicall__'] = self
self.results = []
self.firstresult = firstresult
def __repr__(self):
status = "%d results, %d meths" % (len(self.results), len(self.methods))
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
def execute(self):
while self.methods:
method = self.methods.pop()
kwargs = self.getkwargs(method)
res = method(**kwargs)
if res is not None:
self.results.append(res)
if self.firstresult:
return res
if not self.firstresult:
return self.results
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(func):
ismethod = inspect.ismethod(func)
rawcode = py.code.getrawcode(func)
try:
return rawcode.co_varnames[ismethod:]
except AttributeError:
return ()
class Registry:
"""
Manage Plugins: register/unregister call calls to plugins.
"""
def __init__(self, plugins=None):
if plugins is None:
plugins = []
self._plugins = plugins
def register(self, plugin):
assert not isinstance(plugin, str)
assert not plugin in self._plugins
self._plugins.append(plugin)
def unregister(self, plugin):
self._plugins.remove(plugin)
def isregistered(self, plugin):
return plugin in self._plugins
def __iter__(self):
return iter(self._plugins)
def listattr(self, attrname, plugins=None, extra=(), reverse=False):
l = []
if plugins is None:
plugins = self._plugins
candidates = list(plugins) + list(extra)
for plugin in candidates:
try:
l.append(getattr(plugin, attrname))
except AttributeError:
continue
if reverse:
l.reverse()
return l
class HookRelay:
def __init__(self, hookspecs, registry):
self._hookspecs = hookspecs
self._registry = registry
for name, method in vars(hookspecs).items():
if name[:1] != "_":
setattr(self, name, self._makecall(name))
def _makecall(self, name, extralookup=None):
hookspecmethod = getattr(self._hookspecs, name)
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()
class HookCaller:
def __init__(self, hookrelay, name, firstresult, extralookup=None):
self.hookrelay = hookrelay
self.name = name
self.firstresult = firstresult
self.extralookup = extralookup and [extralookup] or ()
def __repr__(self):
return "<HookCaller %r>" %(self.name,)
def __call__(self, **kwargs):
methods = self.hookrelay._getmethods(self.name, self.extralookup)
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
return self.hookrelay._performcall(self.name, mc)
comregistry = Registry([])

View File

@ -107,9 +107,8 @@ class Config(object):
# warning global side effects: # warning global side effects:
# * registering to py lib plugins # * registering to py lib plugins
# * setting py.test.config # * setting py.test.config
py._com.comregistry = py._com.Registry()
self.__init__( self.__init__(
pluginmanager=py.test._PluginManager(py._com.comregistry), pluginmanager=py.test._PluginManager(),
topdir=py.path.local(), topdir=py.path.local(),
) )
# we have to set py.test.config because preparse() # we have to set py.test.config because preparse()
@ -310,6 +309,6 @@ def gettopdir(args):
# this is the one per-process instance of py.test configuration # this is the one per-process instance of py.test configuration
config_per_process = Config( config_per_process = Config(
pluginmanager=py.test._PluginManager(py._com.comregistry) pluginmanager=py.test._PluginManager()
) )

View File

@ -2,6 +2,7 @@
managing loading and interacting with pytest plugins. managing loading and interacting with pytest plugins.
""" """
import py import py
import inspect
from py.plugin import hookspec from py.plugin import hookspec
from py.impl.test.outcome import Skipped from py.impl.test.outcome import Skipped
@ -16,15 +17,10 @@ def check_old_use(mod, modname):
class PluginManager(object): class PluginManager(object):
class Error(Exception): class Error(Exception):
"""signals a plugin specific error.""" """signals a plugin specific error."""
def __init__(self, comregistry=None): def __init__(self):
if comregistry is None: self.registry = Registry()
comregistry = py._com.Registry()
self.comregistry = comregistry
self._name2plugin = {} self._name2plugin = {}
self.hook = HookRelay(hookspecs=hookspec, registry=self.registry)
self.hook = py._com.HookRelay(
hookspecs=hookspec,
registry=self.comregistry)
self.register(self) self.register(self)
for spec in default_plugins: for spec in default_plugins:
self.import_plugin(spec) self.import_plugin(spec)
@ -39,18 +35,18 @@ class PluginManager(object):
def register(self, plugin, name=None): def register(self, plugin, name=None):
assert not self.isregistered(plugin), plugin assert not self.isregistered(plugin), plugin
assert not self.comregistry.isregistered(plugin), plugin assert not self.registry.isregistered(plugin), plugin
name = self._getpluginname(plugin, name) name = self._getpluginname(plugin, name)
if name in self._name2plugin: if name in self._name2plugin:
return False return False
self._name2plugin[name] = plugin self._name2plugin[name] = plugin
self.hook.pytest_plugin_registered(manager=self, plugin=plugin) self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
self.comregistry.register(plugin) self.registry.register(plugin)
return True return True
def unregister(self, plugin): def unregister(self, plugin):
self.hook.pytest_plugin_unregistered(plugin=plugin) self.hook.pytest_plugin_unregistered(plugin=plugin)
self.comregistry.unregister(plugin) self.registry.unregister(plugin)
for name, value in list(self._name2plugin.items()): for name, value in list(self._name2plugin.items()):
if value == plugin: if value == plugin:
del self._name2plugin[name] del self._name2plugin[name]
@ -63,7 +59,7 @@ class PluginManager(object):
return True return True
def getplugins(self): def getplugins(self):
return list(self.comregistry) return list(self.registry)
def getplugin(self, name): def getplugin(self, name):
try: try:
@ -143,7 +139,7 @@ class PluginManager(object):
# #
# #
def listattr(self, attrname, plugins=None, extra=()): def listattr(self, attrname, plugins=None, extra=()):
return self.comregistry.listattr(attrname, plugins=plugins, extra=extra) return self.registry.listattr(attrname, plugins=plugins, extra=extra)
def notify_exception(self, excinfo=None): def notify_exception(self, excinfo=None):
if excinfo is None: if excinfo is None:
@ -153,8 +149,8 @@ class PluginManager(object):
def do_addoption(self, parser): def do_addoption(self, parser):
mname = "pytest_addoption" mname = "pytest_addoption"
methods = self.comregistry.listattr(mname, reverse=True) methods = self.registry.listattr(mname, reverse=True)
mc = py._com.MultiCall(methods, {'parser': parser}) mc = MultiCall(methods, {'parser': parser})
mc.execute() mc.execute()
def pytest_plugin_registered(self, plugin): def pytest_plugin_registered(self, plugin):
@ -168,7 +164,7 @@ class PluginManager(object):
{'config': self._config}) {'config': self._config})
def call_plugin(self, plugin, methname, kwargs): def call_plugin(self, plugin, methname, kwargs):
return py._com.MultiCall( return MultiCall(
methods=self.listattr(methname, plugins=[plugin]), methods=self.listattr(methname, plugins=[plugin]),
kwargs=kwargs, firstresult=True).execute() kwargs=kwargs, firstresult=True).execute()
@ -210,3 +206,118 @@ def importplugin(importspec):
class MultiCall:
""" execute a call into multiple python functions/methods. """
def __init__(self, methods, kwargs, firstresult=False):
self.methods = methods[:]
self.kwargs = kwargs.copy()
self.kwargs['__multicall__'] = self
self.results = []
self.firstresult = firstresult
def __repr__(self):
status = "%d results, %d meths" % (len(self.results), len(self.methods))
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
def execute(self):
while self.methods:
method = self.methods.pop()
kwargs = self.getkwargs(method)
res = method(**kwargs)
if res is not None:
self.results.append(res)
if self.firstresult:
return res
if not self.firstresult:
return self.results
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(func):
ismethod = inspect.ismethod(func)
rawcode = py.code.getrawcode(func)
try:
return rawcode.co_varnames[ismethod:]
except AttributeError:
return ()
class Registry:
"""
Manage Plugins: register/unregister call calls to plugins.
"""
def __init__(self, plugins=None):
if plugins is None:
plugins = []
self._plugins = plugins
def register(self, plugin):
assert not isinstance(plugin, str)
assert not plugin in self._plugins
self._plugins.append(plugin)
def unregister(self, plugin):
self._plugins.remove(plugin)
def isregistered(self, plugin):
return plugin in self._plugins
def __iter__(self):
return iter(self._plugins)
def listattr(self, attrname, plugins=None, extra=(), reverse=False):
l = []
if plugins is None:
plugins = self._plugins
candidates = list(plugins) + list(extra)
for plugin in candidates:
try:
l.append(getattr(plugin, attrname))
except AttributeError:
continue
if reverse:
l.reverse()
return l
class HookRelay:
def __init__(self, hookspecs, registry):
self._hookspecs = hookspecs
self._registry = registry
for name, method in vars(hookspecs).items():
if name[:1] != "_":
setattr(self, name, self._makecall(name))
def _makecall(self, name, extralookup=None):
hookspecmethod = getattr(self._hookspecs, name)
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()
class HookCaller:
def __init__(self, hookrelay, name, firstresult, extralookup=None):
self.hookrelay = hookrelay
self.name = name
self.firstresult = firstresult
self.extralookup = extralookup and [extralookup] or ()
def __repr__(self):
return "<HookCaller %r>" %(self.name,)
def __call__(self, **kwargs):
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,21 +1,17 @@
import py import py
from py.impl.test.pluginmanager import HookRelay
def pytest_funcarg___pytest(request): def pytest_funcarg___pytest(request):
return PytestArg(request) return PytestArg(request)
class PytestArg: class PytestArg:
def __init__(self, request): def __init__(self, request):
self.request = request self.request = request
self.monkeypatch = self.request.getfuncargvalue("monkeypatch")
self.comregistry = py._com.Registry()
self.monkeypatch.setattr(py._com, 'comregistry', self.comregistry)
def gethookrecorder(self, hookspecs, registry=None): def gethookrecorder(self, hook):
if registry is not None: hookrecorder = HookRecorder(hook._registry)
self.monkeypatch.setattr(py._com, 'comregistry', registry) hookrecorder.start_recording(hook._hookspecs)
self.comregistry = registry
hookrecorder = HookRecorder(self.comregistry)
hookrecorder.start_recording(hookspecs)
self.request.addfinalizer(hookrecorder.finish_recording) self.request.addfinalizer(hookrecorder.finish_recording)
return hookrecorder return hookrecorder
@ -32,8 +28,8 @@ class ParsedCall:
return "<ParsedCall %r(**%r)>" %(self._name, d) return "<ParsedCall %r(**%r)>" %(self._name, d)
class HookRecorder: class HookRecorder:
def __init__(self, comregistry): def __init__(self, registry):
self._comregistry = comregistry self._registry = registry
self.calls = [] self.calls = []
self._recorders = {} self._recorders = {}
@ -46,12 +42,12 @@ class HookRecorder:
setattr(RecordCalls, name, self._makecallparser(method)) setattr(RecordCalls, name, self._makecallparser(method))
recorder = RecordCalls() recorder = RecordCalls()
self._recorders[hookspecs] = recorder self._recorders[hookspecs] = recorder
self._comregistry.register(recorder) self._registry.register(recorder)
self.hook = py._com.HookRelay(hookspecs, registry=self._comregistry) self.hook = HookRelay(hookspecs, registry=self._registry)
def finish_recording(self): def finish_recording(self):
for recorder in self._recorders.values(): for recorder in self._recorders.values():
self._comregistry.unregister(recorder) self._registry.unregister(recorder)
self._recorders.clear() self._recorders.clear()
def _makecallparser(self, method): def _makecallparser(self, method):

View File

@ -22,11 +22,6 @@ def pytest_funcarg__testdir(request):
tmptestdir = TmpTestdir(request) tmptestdir = TmpTestdir(request)
return tmptestdir return tmptestdir
def pytest_funcarg__reportrecorder(request):
reprec = ReportRecorder(py._com.comregistry)
request.addfinalizer(lambda: reprec.comregistry.unregister(reprec))
return reprec
rex_outcome = re.compile("(\d+) (\w+)") rex_outcome = re.compile("(\d+) (\w+)")
class RunResult: class RunResult:
def __init__(self, ret, outlines, errlines): def __init__(self, ret, outlines, errlines):
@ -71,10 +66,10 @@ class TmpTestdir:
def __repr__(self): def __repr__(self):
return "<TmpTestdir %r>" % (self.tmpdir,) return "<TmpTestdir %r>" % (self.tmpdir,)
def Config(self, comregistry=None, topdir=None): def Config(self, registry=None, topdir=None):
if topdir is None: if topdir is None:
topdir = self.tmpdir.dirpath() topdir = self.tmpdir.dirpath()
return pytestConfig(comregistry, topdir=topdir) return pytestConfig(registry, topdir=topdir)
def finalize(self): def finalize(self):
for p in self._syspathremove: for p in self._syspathremove:
@ -89,19 +84,13 @@ class TmpTestdir:
del sys.modules[name] del sys.modules[name]
def getreportrecorder(self, obj): def getreportrecorder(self, obj):
if isinstance(obj, py._com.Registry): if hasattr(obj, 'config'):
registry = obj obj = obj.config
elif hasattr(obj, 'comregistry'): if hasattr(obj, 'hook'):
registry = obj.comregistry obj = obj.hook
elif hasattr(obj, 'pluginmanager'): assert hasattr(obj, '_hookspecs'), obj
registry = obj.pluginmanager.comregistry reprec = ReportRecorder(obj)
elif hasattr(obj, 'config'): reprec.hookrecorder = self._pytest.gethookrecorder(obj)
registry = obj.config.pluginmanager.comregistry
else:
raise ValueError("obj %r provides no comregistry" %(obj,))
assert isinstance(registry, py._com.Registry)
reprec = ReportRecorder(registry)
reprec.hookrecorder = self._pytest.gethookrecorder(hookspec, registry)
reprec.hook = reprec.hookrecorder.hook reprec.hook = reprec.hookrecorder.hook
return reprec return reprec
@ -334,9 +323,10 @@ class PseudoPlugin:
self.__dict__.update(vars) self.__dict__.update(vars)
class ReportRecorder(object): class ReportRecorder(object):
def __init__(self, comregistry): def __init__(self, hook):
self.comregistry = comregistry self.hook = hook
comregistry.register(self) self.registry = hook._registry
self.registry.register(self)
def getcall(self, name): def getcall(self, name):
return self.hookrecorder.getcall(name) return self.hookrecorder.getcall(name)
@ -401,7 +391,7 @@ class ReportRecorder(object):
self.hookrecorder.calls[:] = [] self.hookrecorder.calls[:] = []
def unregister(self): def unregister(self):
self.comregistry.unregister(self) self.registry.unregister(self)
self.hookrecorder.finish_recording() self.hookrecorder.finish_recording()
class LineComp: class LineComp:

View File

@ -1,9 +1,10 @@
import py import py
import sys
from py.plugin.pytest__pytest import HookRecorder from py.plugin.pytest__pytest import HookRecorder
from py.impl.test.pluginmanager import Registry
def test_hookrecorder_basic(): def test_hookrecorder_basic():
comregistry = py._com.Registry() rec = HookRecorder(Registry())
rec = HookRecorder(comregistry)
class ApiClass: class ApiClass:
def xyz(self, arg): def xyz(self, arg):
pass pass
@ -15,9 +16,7 @@ def test_hookrecorder_basic():
py.test.raises(ValueError, "rec.popcall('abc')") py.test.raises(ValueError, "rec.popcall('abc')")
def test_hookrecorder_basic_no_args_hook(): def test_hookrecorder_basic_no_args_hook():
import sys rec = HookRecorder(Registry())
comregistry = py._com.Registry()
rec = HookRecorder(comregistry)
apimod = type(sys)('api') apimod = type(sys)('api')
def xyz(): def xyz():
pass pass
@ -27,23 +26,20 @@ def test_hookrecorder_basic_no_args_hook():
call = rec.popcall("xyz") call = rec.popcall("xyz")
assert call._name == "xyz" assert call._name == "xyz"
reg = py._com.comregistry
def test_functional_default(testdir, _pytest):
assert _pytest.comregistry == py._com.comregistry
assert _pytest.comregistry != reg
def test_functional(testdir, linecomp): def test_functional(testdir, linecomp):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
import py import py
from py.impl.test.pluginmanager import HookRelay, Registry
pytest_plugins="_pytest" pytest_plugins="_pytest"
def test_func(_pytest): def test_func(_pytest):
class ApiClass: class ApiClass:
def xyz(self, arg): pass def xyz(self, arg): pass
rec = _pytest.gethookrecorder(ApiClass) hook = HookRelay(ApiClass, Registry())
rec = _pytest.gethookrecorder(hook)
class Plugin: class Plugin:
def xyz(self, arg): def xyz(self, arg):
return arg + 1 return arg + 1
rec._comregistry.register(Plugin()) rec._registry.register(Plugin())
res = rec.hook.xyz(arg=41) res = rec.hook.xyz(arg=41)
assert res == [42] assert res == [42]
""") """)

View File

@ -2,10 +2,9 @@ import py
from py.plugin.pytest_pytester import LineMatcher, LineComp from py.plugin.pytest_pytester import LineMatcher, LineComp
def test_reportrecorder(testdir): def test_reportrecorder(testdir):
registry = py._com.Registry()
recorder = testdir.getreportrecorder(registry)
assert not recorder.getfailures()
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
recorder = testdir.getreportrecorder(item.config)
assert not recorder.getfailures()
class rep: class rep:
excinfo = None excinfo = None
passed = False passed = False

View File

@ -8,17 +8,17 @@
import py import py
import os import os
from py.impl.test.dist.gwmanage import GatewayManager, HostRSync from py.impl.test.dist.gwmanage import GatewayManager, HostRSync
from py.impl.test.pluginmanager import HookRelay, Registry
from py.plugin import hookspec from py.plugin import hookspec
import execnet import execnet
def pytest_funcarg__hookrecorder(request): def pytest_funcarg__hookrecorder(request):
_pytest = request.getfuncargvalue('_pytest') _pytest = request.getfuncargvalue('_pytest')
hook = request.getfuncargvalue('hook') hook = request.getfuncargvalue('hook')
return _pytest.gethookrecorder(hook._hookspecs, hook._registry) return _pytest.gethookrecorder(hook)
def pytest_funcarg__hook(request): def pytest_funcarg__hook(request):
registry = py._com.Registry() return HookRelay(hookspec, Registry())
return py._com.HookRelay(hookspec, registry)
class TestGatewayManagerPopen: class TestGatewayManagerPopen:
def test_popen_no_default_chdir(self, hook): def test_popen_no_default_chdir(self, hook):
@ -90,7 +90,6 @@ class pytest_funcarg__mysetup:
tmp = request.getfuncargvalue('tmpdir') tmp = request.getfuncargvalue('tmpdir')
self.source = tmp.mkdir("source") self.source = tmp.mkdir("source")
self.dest = tmp.mkdir("dest") self.dest = tmp.mkdir("dest")
request.getfuncargvalue("_pytest") # to have patching of py._com.comregistry
class TestHRSync: class TestHRSync:
def test_hrsync_filter(self, mysetup): def test_hrsync_filter(self, mysetup):

View File

@ -264,9 +264,6 @@ def test_options_on_small_file_do_not_blow_up(testdir):
['--traceconfig'], ['-v'], ['-v', '-v']): ['--traceconfig'], ['-v'], ['-v', '-v']):
runfiletest(opts + [path]) runfiletest(opts + [path])
def test_default_registry():
assert py.test.config.pluginmanager.comregistry is py._com.comregistry
def test_ensuretemp(): def test_ensuretemp():
# XXX test for deprecation # XXX test for deprecation
d1 = py.test.ensuretemp('hello') d1 = py.test.ensuretemp('hello')

View File

@ -3,14 +3,11 @@ import pickle
def setglobals(request): def setglobals(request):
oldconfig = py.test.config oldconfig = py.test.config
oldcom = py._com.comregistry
print("setting py.test.config to None") print("setting py.test.config to None")
py.test.config = None py.test.config = None
py._com.comregistry = py._com.Registry()
def resetglobals(): def resetglobals():
py.builtin.print_("setting py.test.config to", oldconfig) py.builtin.print_("setting py.test.config to", oldconfig)
py.test.config = oldconfig py.test.config = oldconfig
py._com.comregistry = oldcom
request.addfinalizer(resetglobals) request.addfinalizer(resetglobals)
def pytest_funcarg__testdir(request): def pytest_funcarg__testdir(request):
@ -190,7 +187,7 @@ def test_config__setstate__wired_correctly_in_childprocess(testdir):
from py.impl.test.dist.mypickle import PickleChannel from py.impl.test.dist.mypickle import PickleChannel
channel = PickleChannel(channel) channel = PickleChannel(channel)
config = channel.receive() config = channel.receive()
assert py.test.config.pluginmanager.comregistry == py._com.comregistry, "comregistry wrong" assert py.test.config == config
""") """)
channel = PickleChannel(channel) channel = PickleChannel(channel)
config = testdir.parseconfig() config = testdir.parseconfig()

View File

@ -1,5 +1,7 @@
import py, os import py, os
from py.impl.test.pluginmanager import PluginManager, canonical_importname from py.impl.test.pluginmanager import PluginManager, canonical_importname
from py.impl.test.pluginmanager import Registry, MultiCall, HookRelay, varnames
class TestBootstrapping: class TestBootstrapping:
def test_consider_env_fails_to_import(self, monkeypatch): def test_consider_env_fails_to_import(self, monkeypatch):
@ -278,3 +280,184 @@ def test_namespace_has_default_and_env_plugins(testdir):
""") """)
result = testdir.runpython(p) result = testdir.runpython(p)
assert result.ret == 0 assert result.ret == 0
def test_varnames():
def f(x):
pass
class A:
def f(self, y):
pass
assert varnames(f) == ("x",)
assert varnames(A().f) == ('y',)
class TestMultiCall:
def test_uses_copy_of_methods(self):
l = [lambda: 42]
mc = MultiCall(l, {})
repr(mc)
l[:] = []
res = mc.execute()
return res == 42
def test_call_passing(self):
class P1:
def m(self, __multicall__, x):
assert len(__multicall__.results) == 1
assert not __multicall__.methods
return 17
class P2:
def m(self, __multicall__, x):
assert __multicall__.results == []
assert __multicall__.methods
return 23
p1 = P1()
p2 = P2()
multicall = MultiCall([p1.m, p2.m], {'x': 23})
assert "23" in repr(multicall)
reslist = multicall.execute()
assert len(reslist) == 2
# ensure reversed order
assert reslist == [23, 17]
def test_keyword_args(self):
def f(x):
return x + 1
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+23, 24]
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_call_subexecute(self):
def m(__multicall__):
subresult = __multicall__.execute()
return subresult + 1
def n():
return 1
call = MultiCall([n, m], {}, firstresult=True)
res = call.execute()
assert res == 2
def test_call_none_is_no_result(self):
def m1():
return 1
def m2():
return None
res = MultiCall([m1, m2], {}, firstresult=True).execute()
assert res == 1
res = MultiCall([m1, m2], {}).execute()
assert res == [1]
class TestRegistry:
def test_register(self):
registry = Registry()
class MyPlugin:
pass
my = MyPlugin()
registry.register(my)
assert list(registry) == [my]
my2 = MyPlugin()
registry.register(my2)
assert list(registry) == [my, my2]
assert registry.isregistered(my)
assert registry.isregistered(my2)
registry.unregister(my)
assert not registry.isregistered(my)
assert list(registry) == [my2]
def test_listattr(self):
plugins = Registry()
class api1:
x = 41
class api2:
x = 42
class api3:
x = 43
plugins.register(api1())
plugins.register(api2())
plugins.register(api3())
l = list(plugins.listattr('x'))
assert l == [41, 42, 43]
l = list(plugins.listattr('x', reverse=True))
assert l == [43, 42, 41]
class api4:
x = 44
l = list(plugins.listattr('x', extra=(api4,)))
assert l == [41,42,43,44]
assert len(list(plugins)) == 3 # otherwise extra added
class TestHookRelay:
def test_happypath(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
mcm = HookRelay(hookspecs=Api, registry=registry)
assert hasattr(mcm, 'hello')
assert repr(mcm.hello).find("hello") != -1
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
l = mcm.hello(arg=3)
assert l == [4]
assert not hasattr(mcm, 'world')
def test_only_kwargs(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
mcm = HookRelay(hookspecs=Api, registry=registry)
py.test.raises(TypeError, "mcm.hello(3)")
def test_firstresult_definition(self):
registry = Registry()
class Api:
def hello(self, arg): pass
hello.firstresult = True
mcm = HookRelay(hookspecs=Api, registry=registry)
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
res = mcm.hello(arg=3)
assert res == 4
def test_hooks_extra_plugins(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
hookrelay = HookRelay(hookspecs=Api, registry=registry)
hook_hello = hookrelay.hello
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
class Plugin2:
def hello(self, arg):
return arg + 2
newhook = hookrelay._makecall("hello", extralookup=Plugin2())
l = newhook(arg=3)
assert l == [5, 4]
l2 = hook_hello(arg=3)
assert l2 == [4]

View File

@ -1,193 +1,3 @@
import py import py
import os import os
from py.impl._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',)
class TestMultiCall:
def test_uses_copy_of_methods(self):
l = [lambda: 42]
mc = MultiCall(l, {})
repr(mc)
l[:] = []
res = mc.execute()
return res == 42
def test_call_passing(self):
class P1:
def m(self, __multicall__, x):
assert len(__multicall__.results) == 1
assert not __multicall__.methods
return 17
class P2:
def m(self, __multicall__, x):
assert __multicall__.results == []
assert __multicall__.methods
return 23
p1 = P1()
p2 = P2()
multicall = MultiCall([p1.m, p2.m], {'x': 23})
assert "23" in repr(multicall)
reslist = multicall.execute()
assert len(reslist) == 2
# ensure reversed order
assert reslist == [23, 17]
def test_keyword_args(self):
def f(x):
return x + 1
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+23, 24]
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_call_subexecute(self):
def m(__multicall__):
subresult = __multicall__.execute()
return subresult + 1
def n():
return 1
call = MultiCall([n, m], {}, firstresult=True)
res = call.execute()
assert res == 2
def test_call_none_is_no_result(self):
def m1():
return 1
def m2():
return None
res = MultiCall([m1, m2], {}, firstresult=True).execute()
assert res == 1
res = MultiCall([m1, m2], {}).execute()
assert res == [1]
class TestRegistry:
def test_register(self):
registry = Registry()
class MyPlugin:
pass
my = MyPlugin()
registry.register(my)
assert list(registry) == [my]
my2 = MyPlugin()
registry.register(my2)
assert list(registry) == [my, my2]
assert registry.isregistered(my)
assert registry.isregistered(my2)
registry.unregister(my)
assert not registry.isregistered(my)
assert list(registry) == [my2]
def test_listattr(self):
plugins = Registry()
class api1:
x = 41
class api2:
x = 42
class api3:
x = 43
plugins.register(api1())
plugins.register(api2())
plugins.register(api3())
l = list(plugins.listattr('x'))
assert l == [41, 42, 43]
l = list(plugins.listattr('x', reverse=True))
assert l == [43, 42, 41]
class api4:
x = 44
l = list(plugins.listattr('x', extra=(api4,)))
assert l == [41,42,43,44]
assert len(list(plugins)) == 3 # otherwise extra added
def test_api_and_defaults():
assert isinstance(py._com.comregistry, Registry)
class TestHookRelay:
def test_happypath(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
mcm = HookRelay(hookspecs=Api, registry=registry)
assert hasattr(mcm, 'hello')
assert repr(mcm.hello).find("hello") != -1
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
l = mcm.hello(arg=3)
assert l == [4]
assert not hasattr(mcm, 'world')
def test_only_kwargs(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
mcm = HookRelay(hookspecs=Api, registry=registry)
py.test.raises(TypeError, "mcm.hello(3)")
def test_firstresult_definition(self):
registry = Registry()
class Api:
def hello(self, arg): pass
hello.firstresult = True
mcm = HookRelay(hookspecs=Api, registry=registry)
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
res = mcm.hello(arg=3)
assert res == 4
def test_default_plugins(self):
class Api: pass
mcm = HookRelay(hookspecs=Api, registry=py._com.comregistry)
assert mcm._registry == py._com.comregistry
def test_hooks_extra_plugins(self):
registry = Registry()
class Api:
def hello(self, arg):
pass
hookrelay = HookRelay(hookspecs=Api, registry=registry)
hook_hello = hookrelay.hello
class Plugin:
def hello(self, arg):
return arg + 1
registry.register(Plugin())
class Plugin2:
def hello(self, arg):
return arg + 2
newhook = hookrelay._makecall("hello", extralookup=Plugin2())
l = newhook(arg=3)
assert l == [5, 4]
l2 = hook_hello(arg=3)
assert l2 == [4]