make factorydeflist immutable by using an index
This commit is contained in:
parent
ccaa1af534
commit
37965657d0
2
IMPL.txt
2
IMPL.txt
|
@ -36,5 +36,5 @@ attributes:
|
|||
|
||||
- names_initial: list of initial fixture names (see above)
|
||||
- names_closure: closure of all fixture names
|
||||
- name2fixturedeflist: for creating the value
|
||||
- name2fixturedefs: for creating the value
|
||||
|
||||
|
|
|
@ -361,26 +361,16 @@ class FixtureMapper:
|
|||
usefixtures = getattr(func, "usefixtures", None)
|
||||
if usefixtures is not None:
|
||||
argnames = usefixtures.args + argnames
|
||||
names_closure, arg2fixturedeflist = self.fm.getfixtureclosure(
|
||||
names_closure, arg2fixturedefs = self.fm.getfixtureclosure(
|
||||
argnames, self.node)
|
||||
fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedeflist)
|
||||
fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedefs)
|
||||
self._name2fixtureinfo[func] = fixtureinfo
|
||||
return fixtureinfo
|
||||
|
||||
class FuncFixtureInfo:
|
||||
def __init__(self, names_closure, name2fixturedeflist):
|
||||
def __init__(self, names_closure, name2fixturedefs):
|
||||
self.names_closure = names_closure
|
||||
self.name2fixturedeflist = name2fixturedeflist
|
||||
|
||||
def getname2fixturedeflist_copy(self):
|
||||
d = {}
|
||||
for name, val in self.name2fixturedeflist.items():
|
||||
try:
|
||||
val = list(val)
|
||||
except TypeError:
|
||||
pass
|
||||
d[name] = val
|
||||
return d
|
||||
self.name2fixturedefs = name2fixturedefs
|
||||
|
||||
def transfer_markers(funcobj, cls, mod):
|
||||
# XXX this should rather be code in the mark plugin or the mark
|
||||
|
@ -675,7 +665,7 @@ class Metafunc(FuncargnamesCompatAttr):
|
|||
self.module = module
|
||||
self.function = function
|
||||
self.fixturenames = fixtureinfo.names_closure
|
||||
self._arg2fixturedeflist = fixtureinfo.name2fixturedeflist
|
||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||
self.cls = cls
|
||||
self.module = module
|
||||
self._calls = []
|
||||
|
@ -804,12 +794,12 @@ def _showfixtures_main(config, session):
|
|||
fm = session._fixturemanager
|
||||
|
||||
available = []
|
||||
for argname in fm.arg2fixturedeflist:
|
||||
fixturedeflist = fm.getfixturedeflist(argname, nodeid)
|
||||
assert fixturedeflist is not None
|
||||
if not fixturedeflist:
|
||||
for argname in fm.arg2fixturedefs:
|
||||
fixturedefs = fm.getfixturedefs(argname, nodeid)
|
||||
assert fixturedefs is not None
|
||||
if not fixturedefs:
|
||||
continue
|
||||
fixturedef = fixturedeflist[-1]
|
||||
fixturedef = fixturedefs[-1]
|
||||
loc = getlocation(fixturedef.func, curdir)
|
||||
available.append((len(fixturedef.baseid),
|
||||
curdir.bestrelpath(loc),
|
||||
|
@ -1055,7 +1045,8 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
|||
self.getparent = pyfuncitem.getparent
|
||||
self._funcargs = self._pyfuncitem.funcargs.copy()
|
||||
self._fixtureinfo = fi = pyfuncitem._fixtureinfo
|
||||
self._arg2fixturedeflist = fi.getname2fixturedeflist_copy()
|
||||
self._arg2fixturedefs = fi.name2fixturedefs
|
||||
self._arg2index = {}
|
||||
self.fixturenames = self._fixtureinfo.names_closure
|
||||
self._fixturemanager = pyfuncitem.session._fixturemanager
|
||||
self._parentid = pyfuncitem.parent.nodeid
|
||||
|
@ -1066,15 +1057,20 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
|||
""" underlying collection node (depends on current request scope)"""
|
||||
return self._getscopeitem(self.scope)
|
||||
|
||||
def _getfixturedeflist(self, argname):
|
||||
fixturedeflist = self._arg2fixturedeflist.get(argname, None)
|
||||
if fixturedeflist is None:
|
||||
fixturedeflist = self._fixturemanager.getfixturedeflist(
|
||||
def _getnextfixturedef(self, argname):
|
||||
fixturedefs = self._arg2fixturedefs.get(argname, None)
|
||||
if fixturedefs is None:
|
||||
# we arrive here because of a getfuncargvalue(argname) usage which
|
||||
# was naturally not knowable at parsing/collection time
|
||||
fixturedefs = self._fixturemanager.getfixturedefs(
|
||||
argname, self._parentid)
|
||||
self._arg2fixturedeflist[argname] = fixturedeflist
|
||||
if not fixturedeflist:
|
||||
self._arg2fixturedefs[argname] = fixturedefs
|
||||
# fixturedefs is immutable so we maintain a decreasing index
|
||||
index = self._arg2index.get(argname, 0) - 1
|
||||
if fixturedefs is None or (-index > len(fixturedefs)):
|
||||
raise FixtureLookupError(argname, self)
|
||||
return fixturedeflist
|
||||
self._arg2index[argname] = index
|
||||
return fixturedefs[index]
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
|
@ -1212,12 +1208,11 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
|||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
fixturedeflist = self._getfixturedeflist(argname)
|
||||
fixturedef = self._getnextfixturedef(argname)
|
||||
except FixtureLookupError:
|
||||
if argname == "request":
|
||||
return self
|
||||
raise
|
||||
fixturedef = fixturedeflist.pop()
|
||||
self._fixturestack.append(fixturedef)
|
||||
try:
|
||||
result = self._getfuncargvalue(fixturedef)
|
||||
|
@ -1353,7 +1348,7 @@ class FixtureLookupError(LookupError):
|
|||
fm = self.request._fixturemanager
|
||||
nodeid = self.request._parentid
|
||||
available = []
|
||||
for name, fixturedef in fm.arg2fixturedeflist.items():
|
||||
for name, fixturedef in fm.arg2fixturedefs.items():
|
||||
faclist = list(fm._matchfactories(fixturedef, self.request._parentid))
|
||||
if faclist:
|
||||
available.append(name)
|
||||
|
@ -1388,7 +1383,7 @@ class FixtureManager:
|
|||
def __init__(self, session):
|
||||
self.session = session
|
||||
self.config = session.config
|
||||
self.arg2fixturedeflist = {}
|
||||
self.arg2fixturedefs = {}
|
||||
self._seenplugins = set()
|
||||
self._holderobjseen = set()
|
||||
self._arg2finish = {}
|
||||
|
@ -1427,7 +1422,7 @@ class FixtureManager:
|
|||
# make sure the self._autofixtures list is sorted
|
||||
# by scope, scopenum 0 is session
|
||||
self._autofixtures.sort(
|
||||
key=lambda x: self.arg2fixturedeflist[x][-1].scopenum)
|
||||
key=lambda x: self.arg2fixturedefs[x][-1].scopenum)
|
||||
defaultfixtures = defaultfixtures + tuple(self._autofixtures)
|
||||
self._defaultfixtures = defaultfixtures
|
||||
return defaultfixtures
|
||||
|
@ -1435,7 +1430,7 @@ class FixtureManager:
|
|||
def getfixtureclosure(self, fixturenames, parentnode):
|
||||
# collect the closure of all fixtures , starting with the given
|
||||
# fixturenames as the initial set. As we have to visit all
|
||||
# factory definitions anyway, we also return a arg2fixturedeflist
|
||||
# factory definitions anyway, we also return a arg2fixturedefs
|
||||
# mapping so that the caller can reuse it and does not have
|
||||
# to re-discover fixturedefs again for each fixturename
|
||||
# (discovering matching fixtures for a given name/node is expensive)
|
||||
|
@ -1447,23 +1442,23 @@ class FixtureManager:
|
|||
if arg not in fixturenames_closure:
|
||||
fixturenames_closure.append(arg)
|
||||
merge(fixturenames)
|
||||
arg2fixturedeflist = {}
|
||||
arg2fixturedefs = {}
|
||||
lastlen = -1
|
||||
while lastlen != len(fixturenames_closure):
|
||||
lastlen = len(fixturenames_closure)
|
||||
for argname in fixturenames_closure:
|
||||
if argname in arg2fixturedeflist:
|
||||
if argname in arg2fixturedefs:
|
||||
continue
|
||||
fixturedeflist = self.getfixturedeflist(argname, parentid)
|
||||
arg2fixturedeflist[argname] = fixturedeflist
|
||||
if fixturedeflist is not None:
|
||||
for fixturedef in fixturedeflist:
|
||||
fixturedefs = self.getfixturedefs(argname, parentid)
|
||||
arg2fixturedefs[argname] = fixturedefs
|
||||
if fixturedefs is not None:
|
||||
for fixturedef in fixturedefs:
|
||||
merge(fixturedef.fixturenames)
|
||||
return fixturenames_closure, arg2fixturedeflist
|
||||
return fixturenames_closure, arg2fixturedefs
|
||||
|
||||
def pytest_generate_tests(self, metafunc):
|
||||
for argname in metafunc.fixturenames:
|
||||
faclist = metafunc._arg2fixturedeflist[argname]
|
||||
faclist = metafunc._arg2fixturedefs[argname]
|
||||
if faclist is None:
|
||||
continue # will raise FixtureLookupError at setup time
|
||||
for fixturedef in faclist:
|
||||
|
@ -1527,7 +1522,7 @@ class FixtureManager:
|
|||
fixturedef = FixtureDef(self, nodeid, name, obj,
|
||||
marker.scope, marker.params,
|
||||
unittest=unittest)
|
||||
faclist = self.arg2fixturedeflist.setdefault(name, [])
|
||||
faclist = self.arg2fixturedefs.setdefault(name, [])
|
||||
faclist.append(fixturedef)
|
||||
if marker.autouse:
|
||||
self._autofixtures.append(name)
|
||||
|
@ -1536,16 +1531,16 @@ class FixtureManager:
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
def getfixturedeflist(self, argname, nodeid):
|
||||
def getfixturedefs(self, argname, nodeid):
|
||||
try:
|
||||
fixturedeflist = self.arg2fixturedeflist[argname]
|
||||
fixturedefs = self.arg2fixturedefs[argname]
|
||||
except KeyError:
|
||||
return None
|
||||
else:
|
||||
return list(self._matchfactories(fixturedeflist, nodeid))
|
||||
return tuple(self._matchfactories(fixturedefs, nodeid))
|
||||
|
||||
def _matchfactories(self, fixturedeflist, nodeid):
|
||||
for fixturedef in fixturedeflist:
|
||||
def _matchfactories(self, fixturedefs, nodeid):
|
||||
for fixturedef in fixturedefs:
|
||||
if nodeid.startswith(fixturedef.baseid):
|
||||
yield fixturedef
|
||||
|
||||
|
|
|
@ -640,7 +640,7 @@ class TestRequest:
|
|||
assert req.cls.__name__ == "TestB"
|
||||
assert req.instance.__class__ == req.cls
|
||||
|
||||
def XXXtest_request_contains_funcarg_arg2fixturedeflist(self, testdir):
|
||||
def XXXtest_request_contains_funcarg_arg2fixturedefs(self, testdir):
|
||||
modcol = testdir.getmodulecol("""
|
||||
def pytest_funcarg__something(request):
|
||||
pass
|
||||
|
@ -650,9 +650,9 @@ class TestRequest:
|
|||
""")
|
||||
item1, = testdir.genitems([modcol])
|
||||
assert item1.name == "test_method"
|
||||
arg2fixturedeflist = funcargs.FixtureRequest(item1)._arg2fixturedeflist
|
||||
assert len(arg2fixturedeflist) == 1
|
||||
assert arg2fixturedeflist[0].__name__ == "pytest_funcarg__something"
|
||||
arg2fixturedefs = funcargs.FixtureRequest(item1)._arg2fixturedefs
|
||||
assert len(arg2fixturedefs) == 1
|
||||
assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something"
|
||||
|
||||
def test_getfuncargvalue_recursive(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
|
@ -918,7 +918,7 @@ class TestMetafunc:
|
|||
# on the funcarg level, so we don't need a full blown
|
||||
# initiliazation
|
||||
class FixtureInfo:
|
||||
name2fixturedeflist = None
|
||||
name2fixturedefs = None
|
||||
def __init__(self, names):
|
||||
self.names_closure = names
|
||||
names = funcargs.getfuncargnames(func)
|
||||
|
@ -1974,7 +1974,7 @@ class TestFixtureManager:
|
|||
testdir.makepyfile("""
|
||||
def test_hello(item, fm):
|
||||
for name in ("fm", "hello", "item"):
|
||||
faclist = fm.getfixturedeflist(name, item.nodeid)
|
||||
faclist = fm.getfixturedefs(name, item.nodeid)
|
||||
assert len(faclist) == 1
|
||||
fac = faclist[0]
|
||||
assert fac.func.__name__ == "pytest_funcarg__" + name
|
||||
|
@ -1990,7 +1990,7 @@ class TestFixtureManager:
|
|||
def pytest_funcarg__hello(self, request):
|
||||
return "class"
|
||||
def test_hello(self, item, fm):
|
||||
faclist = fm.getfixturedeflist("hello", item.nodeid)
|
||||
faclist = fm.getfixturedefs("hello", item.nodeid)
|
||||
print (faclist)
|
||||
assert len(faclist) == 3
|
||||
assert faclist[0].func(item._request) == "conftest"
|
||||
|
|
Loading…
Reference in New Issue