diff --git a/CHANGELOG b/CHANGELOG index 4c5694ec7..eed468cdd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.0.1 and 2.0.2 ---------------------------------------------- +- tackle issue32 - half the overhead for running test functions + - fix issue30 - extended xfail/skipif handling and improved reporting. If you have a syntax error in your skip/xfail expressions you now get nice error reports. diff --git a/_pytest/core.py b/_pytest/core.py index 7a67e7cbd..2bd110f51 100644 --- a/_pytest/core.py +++ b/_pytest/core.py @@ -60,6 +60,7 @@ class TagTracerSub: class PluginManager(object): def __init__(self, load=False): self._name2plugin = {} + self._listattrcache = {} self._plugins = [] self._hints = [] self.trace = TagTracer().get("pluginmanage") @@ -272,6 +273,11 @@ class PluginManager(object): def listattr(self, attrname, plugins=None): if plugins is None: plugins = self._plugins + key = (attrname,) + tuple(plugins) + try: + return list(self._listattrcache[key]) + except KeyError: + pass l = [] last = [] for plugin in plugins: @@ -286,6 +292,7 @@ class PluginManager(object): except AttributeError: continue l.extend(last) + self._listattrcache[key] = list(l) return l def call_plugin(self, plugin, methname, kwargs): @@ -340,14 +347,20 @@ class MultiCall: return kwargs def varnames(func): + try: + return func._varnames + except AttributeError: + pass if not inspect.isfunction(func) and not inspect.ismethod(func): func = getattr(func, '__call__', func) ismethod = inspect.ismethod(func) rawcode = py.code.getrawcode(func) try: - return rawcode.co_varnames[ismethod:rawcode.co_argcount] + x = rawcode.co_varnames[ismethod:rawcode.co_argcount] except AttributeError: - return () + x = () + py.builtin._getfuncdict(func)['_varnames'] = x + return x class HookRelay: def __init__(self, hookspecs, pm, prefix="pytest_"): diff --git a/_pytest/helpconfig.py b/_pytest/helpconfig.py index 0e37affcf..b89b33b56 100644 --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -2,6 +2,7 @@ import py import pytest import inspect, sys +from _pytest.core import varnames def pytest_addoption(parser): group = parser.getgroup('debugconfig') @@ -135,12 +136,11 @@ def pytest_plugin_registered(manager, plugin): fail = True else: #print "checking", method - method_args = getargs(method) - #print "method_args", method_args + method_args = list(varnames(method)) if '__multicall__' in method_args: method_args.remove('__multicall__') hook = hooks[name] - hookargs = getargs(hook) + hookargs = varnames(hook) for arg in method_args: if arg not in hookargs: Print("argument %r not available" %(arg, )) @@ -162,11 +162,6 @@ def isgenerichook(name): return name == "pytest_plugins" or \ name.startswith("pytest_funcarg__") -def getargs(func): - args = inspect.getargs(py.code.getrawcode(func))[0] - startindex = inspect.ismethod(func) and 1 or 0 - return args[startindex:] - def collectattr(obj): methods = {} for apiname in dir(obj): diff --git a/_pytest/main.py b/_pytest/main.py index a257ea56f..d7948c6fa 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -326,7 +326,13 @@ class Item(Node): return self._location except AttributeError: location = self.reportinfo() - fspath = self.session.fspath.bestrelpath(location[0]) + # bestrelpath is a quite slow function + cache = self.config.__dict__.setdefault("_bestrelpathcache", {}) + try: + fspath = cache[location[0]] + except KeyError: + fspath = self.session.fspath.bestrelpath(location[0]) + cache[location[0]] = fspath location = (fspath, location[1], str(location[2])) self._location = location return location diff --git a/pytest.py b/pytest.py index 4f21ac899..8947e365e 100644 --- a/pytest.py +++ b/pytest.py @@ -1,7 +1,7 @@ """ unit and functional testing with Python. """ -__version__ = '2.0.2.dev6' +__version__ = '2.0.2.dev7' __all__ = ['main'] from _pytest.core import main, UsageError, _preloadplugins diff --git a/setup.py b/setup.py index 7a464a0c0..aa0503013 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def main(): name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.0.2.dev6', + version='2.0.2.dev7', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/test_core.py b/testing/test_core.py index c389b7ddf..e53d76739 100644 --- a/testing/test_core.py +++ b/testing/test_core.py @@ -433,6 +433,9 @@ class TestPytestPluginInteractions: pluginmanager.register(p3) methods = pluginmanager.listattr('m') assert methods == [p2.m, p3.m, p1.m] + # listattr keeps a cache and deleting + # a function attribute requires clearing it + pluginmanager._listattrcache.clear() del P1.m.__dict__['tryfirst'] pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))