half the overhead for calling a test function by introducing some caching

This commit is contained in:
holger krekel 2011-03-07 18:28:45 +01:00
parent 5470cadbff
commit f2670651b3
7 changed files with 32 additions and 13 deletions

View File

@ -1,6 +1,8 @@
Changes between 2.0.1 and 2.0.2 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. - fix issue30 - extended xfail/skipif handling and improved reporting.
If you have a syntax error in your skip/xfail If you have a syntax error in your skip/xfail
expressions you now get nice error reports. expressions you now get nice error reports.

View File

@ -60,6 +60,7 @@ class TagTracerSub:
class PluginManager(object): class PluginManager(object):
def __init__(self, load=False): def __init__(self, load=False):
self._name2plugin = {} self._name2plugin = {}
self._listattrcache = {}
self._plugins = [] self._plugins = []
self._hints = [] self._hints = []
self.trace = TagTracer().get("pluginmanage") self.trace = TagTracer().get("pluginmanage")
@ -272,6 +273,11 @@ class PluginManager(object):
def listattr(self, attrname, plugins=None): def listattr(self, attrname, plugins=None):
if plugins is None: if plugins is None:
plugins = self._plugins plugins = self._plugins
key = (attrname,) + tuple(plugins)
try:
return list(self._listattrcache[key])
except KeyError:
pass
l = [] l = []
last = [] last = []
for plugin in plugins: for plugin in plugins:
@ -286,6 +292,7 @@ class PluginManager(object):
except AttributeError: except AttributeError:
continue continue
l.extend(last) l.extend(last)
self._listattrcache[key] = list(l)
return l return l
def call_plugin(self, plugin, methname, kwargs): def call_plugin(self, plugin, methname, kwargs):
@ -340,14 +347,20 @@ class MultiCall:
return kwargs return kwargs
def varnames(func): def varnames(func):
try:
return func._varnames
except AttributeError:
pass
if not inspect.isfunction(func) and not inspect.ismethod(func): if not inspect.isfunction(func) and not inspect.ismethod(func):
func = getattr(func, '__call__', func) func = getattr(func, '__call__', func)
ismethod = inspect.ismethod(func) ismethod = inspect.ismethod(func)
rawcode = py.code.getrawcode(func) rawcode = py.code.getrawcode(func)
try: try:
return rawcode.co_varnames[ismethod:rawcode.co_argcount] x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
except AttributeError: except AttributeError:
return () x = ()
py.builtin._getfuncdict(func)['_varnames'] = x
return x
class HookRelay: class HookRelay:
def __init__(self, hookspecs, pm, prefix="pytest_"): def __init__(self, hookspecs, pm, prefix="pytest_"):

View File

@ -2,6 +2,7 @@
import py import py
import pytest import pytest
import inspect, sys import inspect, sys
from _pytest.core import varnames
def pytest_addoption(parser): def pytest_addoption(parser):
group = parser.getgroup('debugconfig') group = parser.getgroup('debugconfig')
@ -135,12 +136,11 @@ def pytest_plugin_registered(manager, plugin):
fail = True fail = True
else: else:
#print "checking", method #print "checking", method
method_args = getargs(method) method_args = list(varnames(method))
#print "method_args", method_args
if '__multicall__' in method_args: if '__multicall__' in method_args:
method_args.remove('__multicall__') method_args.remove('__multicall__')
hook = hooks[name] hook = hooks[name]
hookargs = getargs(hook) hookargs = varnames(hook)
for arg in method_args: for arg in method_args:
if arg not in hookargs: if arg not in hookargs:
Print("argument %r not available" %(arg, )) Print("argument %r not available" %(arg, ))
@ -162,11 +162,6 @@ def isgenerichook(name):
return name == "pytest_plugins" or \ return name == "pytest_plugins" or \
name.startswith("pytest_funcarg__") 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): def collectattr(obj):
methods = {} methods = {}
for apiname in dir(obj): for apiname in dir(obj):

View File

@ -326,7 +326,13 @@ class Item(Node):
return self._location return self._location
except AttributeError: except AttributeError:
location = self.reportinfo() location = self.reportinfo()
# 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]) fspath = self.session.fspath.bestrelpath(location[0])
cache[location[0]] = fspath
location = (fspath, location[1], str(location[2])) location = (fspath, location[1], str(location[2]))
self._location = location self._location = location
return location return location

View File

@ -1,7 +1,7 @@
""" """
unit and functional testing with Python. unit and functional testing with Python.
""" """
__version__ = '2.0.2.dev6' __version__ = '2.0.2.dev7'
__all__ = ['main'] __all__ = ['main']
from _pytest.core import main, UsageError, _preloadplugins from _pytest.core import main, UsageError, _preloadplugins

View File

@ -22,7 +22,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.0.2.dev6', version='2.0.2.dev7',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -433,6 +433,9 @@ class TestPytestPluginInteractions:
pluginmanager.register(p3) pluginmanager.register(p3)
methods = pluginmanager.listattr('m') methods = pluginmanager.listattr('m')
assert methods == [p2.m, p3.m, p1.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'] del P1.m.__dict__['tryfirst']
pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m)) pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))