Skip to content

Commit c8ab794

Browse files
authored
Merge pull request #1811 from nicoddemus/revert-invocation-fixtures
Revert invocation-fixtures code
2 parents 09e647c + 707b6b5 commit c8ab794

File tree

10 files changed

+31
-425
lines changed

10 files changed

+31
-425
lines changed

CHANGELOG.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,6 @@ time or change existing behaviors in order to make them less surprising/more use
143143
never fail because tuples are always truthy and are usually a mistake
144144
(see `#1562`_). Thanks `@kvas-it`_, for the PR.
145145

146-
* Experimentally introduce new ``"invocation"`` fixture scope. At invocation scope a
147-
fixture function is cached in the same way as the fixture or test function that requests it.
148-
You can now use the builtin ``monkeypatch`` fixture from ``session``-scoped fixtures
149-
where previously you would get an error that you can not use a ``function``-scoped fixture from a
150-
``session``-scoped one.*
151-
Thanks `@nicoddemus`_ for the PR.
152-
153146
* Allow passing a custom debugger class (e.g. ``--pdbcls=IPython.core.debugger:Pdb``).
154147
Thanks to `@anntzer`_ for the PR.
155148

_pytest/fixtures.py

Lines changed: 26 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,8 @@ def __init__(self, pyfuncitem):
274274
self.fixturename = None
275275
#: Scope string, one of "function", "class", "module", "session"
276276
self.scope = "function"
277-
# rename both attributes below because their key has changed; better an attribute error
278-
# than subtle key misses; also backward incompatibility
279-
self._fixture_values = {} # (argname, scope) -> fixture value
280-
self._fixture_defs = {} # (argname, scope) -> FixtureDef
277+
self._fixture_values = {} # argname -> fixture value
278+
self._fixture_defs = {} # argname -> FixtureDef
281279
fixtureinfo = pyfuncitem._fixtureinfo
282280
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
283281
self._arg2index = {}
@@ -294,31 +292,20 @@ def node(self):
294292
return self._getscopeitem(self.scope)
295293

296294

297-
def _getnextfixturedef(self, argname, scope):
298-
def trygetfixturedefs(argname):
299-
fixturedefs = self._arg2fixturedefs.get(argname, None)
300-
if fixturedefs is None:
301-
fixturedefs = self._arg2fixturedefs.get(argname + ':' + scope, None)
302-
return fixturedefs
303-
304-
fixturedefs = trygetfixturedefs(argname)
295+
def _getnextfixturedef(self, argname):
296+
fixturedefs = self._arg2fixturedefs.get(argname, None)
305297
if fixturedefs is None:
306298
# we arrive here because of a a dynamic call to
307299
# getfixturevalue(argname) usage which was naturally
308300
# not known at parsing/collection time
309301
parentid = self._pyfuncitem.parent.nodeid
310302
fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid)
311-
if fixturedefs:
312-
self._arg2fixturedefs[argname] = fixturedefs
313-
fixturedefs_by_argname = self._fixturemanager.getfixturedefs_multiple_scopes(argname, parentid)
314-
if fixturedefs_by_argname:
315-
self._arg2fixturedefs.update(fixturedefs_by_argname)
316-
fixturedefs = trygetfixturedefs(argname)
303+
self._arg2fixturedefs[argname] = fixturedefs
317304
# fixturedefs list is immutable so we maintain a decreasing index
318-
index = self._arg2index.get((argname, scope), 0) - 1
305+
index = self._arg2index.get(argname, 0) - 1
319306
if fixturedefs is None or (-index > len(fixturedefs)):
320307
raise FixtureLookupError(argname, self)
321-
self._arg2index[(argname, scope)] = index
308+
self._arg2index[argname] = index
322309
return fixturedefs[index]
323310

324311
@property
@@ -458,10 +445,10 @@ def getfuncargvalue(self, argname):
458445

459446
def _get_active_fixturedef(self, argname):
460447
try:
461-
return self._fixture_defs[(argname, self.scope)]
448+
return self._fixture_defs[argname]
462449
except KeyError:
463450
try:
464-
fixturedef = self._getnextfixturedef(argname, self.scope)
451+
fixturedef = self._getnextfixturedef(argname)
465452
except FixtureLookupError:
466453
if argname == "request":
467454
class PseudoFixtureDef:
@@ -472,8 +459,8 @@ class PseudoFixtureDef:
472459
# remove indent to prevent the python3 exception
473460
# from leaking into the call
474461
result = self._getfixturevalue(fixturedef)
475-
self._fixture_values[(argname, self.scope)] = result
476-
self._fixture_defs[(argname, self.scope)] = fixturedef
462+
self._fixture_values[argname] = result
463+
self._fixture_defs[argname] = fixturedef
477464
return fixturedef
478465

479466
def _get_fixturestack(self):
@@ -615,16 +602,6 @@ def scopemismatch(currentscope, newscope):
615602
return scopes.index(newscope) > scopes.index(currentscope)
616603

617604

618-
def strip_invocation_scope_suffix(name):
619-
"""Remove the invocation-scope suffix from the given name.
620-
621-
Invocation scope fixtures have their scope in the name of the fixture.
622-
For example, "monkeypatch:session". This function strips the suffix
623-
returning the user-frienldy name of the fixture.
624-
"""
625-
return name.split(':')[0]
626-
627-
628605
class FixtureLookupError(LookupError):
629606
""" could not return a requested Fixture (missing or invalid). """
630607
def __init__(self, argname, request, msg=None):
@@ -664,7 +641,6 @@ def formatrepr(self):
664641
parentid = self.request._pyfuncitem.parent.nodeid
665642
for name, fixturedefs in fm._arg2fixturedefs.items():
666643
faclist = list(fm._matchfactories(fixturedefs, parentid))
667-
name = strip_invocation_scope_suffix(name)
668644
if faclist and name not in available:
669645
available.append(name)
670646
msg = "fixture %r not found" % (self.argname,)
@@ -847,7 +823,7 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
847823
function will be injected.
848824
849825
:arg scope: the scope for which this fixture is shared, one of
850-
"function" (default), "class", "module", "session" or "invocation".
826+
"function" (default), "class", "module" or "session".
851827
852828
:arg params: an optional list of parameters which will cause multiple
853829
invocations of the fixture function and all of the tests
@@ -1029,11 +1005,6 @@ def merge(otherlist):
10291005
if fixturedefs:
10301006
arg2fixturedefs[argname] = fixturedefs
10311007
merge(fixturedefs[-1].argnames)
1032-
fixturedefs_by_argname = self.getfixturedefs_multiple_scopes(argname, parentid)
1033-
if fixturedefs_by_argname:
1034-
arg2fixturedefs.update(fixturedefs_by_argname)
1035-
for fixturedefs in fixturedefs_by_argname.values():
1036-
merge(fixturedefs[-1].argnames)
10371008
return fixturenames_closure, arg2fixturedefs
10381009

10391010
def pytest_generate_tests(self, metafunc):
@@ -1093,30 +1064,22 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
10931064
'and be decorated with @pytest.fixture:\n%s' % name
10941065
assert not name.startswith(self._argprefix), msg
10951066

1096-
def new_fixture_def(name, scope):
1097-
"""Create and registers a new FixtureDef with given name and scope."""
1098-
fixture_def = FixtureDef(self, nodeid, name, obj,
1099-
scope, marker.params,
1100-
unittest=unittest, ids=marker.ids)
1067+
fixture_def = FixtureDef(self, nodeid, name, obj,
1068+
marker.scope, marker.params,
1069+
unittest=unittest, ids=marker.ids)
11011070

1102-
faclist = self._arg2fixturedefs.setdefault(name, [])
1103-
if fixture_def.has_location:
1104-
faclist.append(fixture_def)
1105-
else:
1106-
# fixturedefs with no location are at the front
1107-
# so this inserts the current fixturedef after the
1108-
# existing fixturedefs from external plugins but
1109-
# before the fixturedefs provided in conftests.
1110-
i = len([f for f in faclist if not f.has_location])
1111-
faclist.insert(i, fixture_def)
1112-
if marker.autouse:
1113-
autousenames.append(name)
1114-
1115-
if marker.scope == 'invocation':
1116-
for new_scope in scopes:
1117-
new_fixture_def(name + ':{0}'.format(new_scope), new_scope)
1071+
faclist = self._arg2fixturedefs.setdefault(name, [])
1072+
if fixture_def.has_location:
1073+
faclist.append(fixture_def)
11181074
else:
1119-
new_fixture_def(name, marker.scope)
1075+
# fixturedefs with no location are at the front
1076+
# so this inserts the current fixturedef after the
1077+
# existing fixturedefs from external plugins but
1078+
# before the fixturedefs provided in conftests.
1079+
i = len([f for f in faclist if not f.has_location])
1080+
faclist.insert(i, fixture_def)
1081+
if marker.autouse:
1082+
autousenames.append(name)
11201083

11211084
if autousenames:
11221085
self._nodeid_and_autousenames.append((nodeid or '', autousenames))
@@ -1141,23 +1104,3 @@ def _matchfactories(self, fixturedefs, nodeid):
11411104
if nodeid.startswith(fixturedef.baseid):
11421105
yield fixturedef
11431106

1144-
def getfixturedefs_multiple_scopes(self, argname, nodeid):
1145-
"""
1146-
Gets multiple scoped fixtures which are applicable to the given nodeid. Multiple scoped
1147-
fixtures are created by "invocation" scoped fixtures and have argnames in
1148-
the form: "<argname>:<scope>" (for example "tmpdir:session").
1149-
1150-
:return: dict of "argname" -> [FixtureDef].
1151-
1152-
Arguments similar to ``getfixturedefs``.
1153-
"""
1154-
prefix = argname + ':'
1155-
fixturedefs_by_argname = dict((k, v) for k, v in self._arg2fixturedefs.items()
1156-
if k.startswith(prefix))
1157-
if fixturedefs_by_argname:
1158-
result = {}
1159-
for argname, fixturedefs in fixturedefs_by_argname.items():
1160-
result[argname] = tuple(self._matchfactories(fixturedefs, nodeid))
1161-
return result
1162-
else:
1163-
return None

_pytest/monkeypatch.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
1111

1212

13-
@pytest.fixture(scope='invocation')
13+
@pytest.fixture
1414
def monkeypatch(request):
1515
"""The returned ``monkeypatch`` fixture provides these
1616
helper methods to modify objects, dictionaries or os.environ::
@@ -28,8 +28,6 @@ def monkeypatch(request):
2828
test function or fixture has finished. The ``raising``
2929
parameter determines if a KeyError or AttributeError
3030
will be raised if the set/deletion operation has no target.
31-
32-
This fixture is ``invocation``-scoped.
3331
"""
3432
mpatch = MonkeyPatch()
3533
request.addfinalizer(mpatch.undo)

_pytest/python.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,6 @@ def showfixtures(config):
10081008

10091009
def _showfixtures_main(config, session):
10101010
import _pytest.config
1011-
from _pytest.fixtures import strip_invocation_scope_suffix
10121011
session.perform_collect()
10131012
curdir = py.path.local()
10141013
tw = _pytest.config.create_terminal_writer(config)
@@ -1025,16 +1024,13 @@ def _showfixtures_main(config, session):
10251024
continue
10261025
for fixturedef in fixturedefs:
10271026
loc = getlocation(fixturedef.func, curdir)
1028-
# invocation-scoped fixtures have argname in the form
1029-
# "<name>:<scope>" (for example: "monkeypatch:session").
1030-
fixture_argname = strip_invocation_scope_suffix(fixturedef.argname)
1031-
if (fixture_argname, loc) in seen:
1027+
if (fixturedef.argname, loc) in seen:
10321028
continue
1033-
seen.add((fixture_argname, loc))
1029+
seen.add((fixturedef.argname, loc))
10341030
available.append((len(fixturedef.baseid),
10351031
fixturedef.func.__module__,
10361032
curdir.bestrelpath(loc),
1037-
fixture_argname, fixturedef))
1033+
fixturedef.argname, fixturedef))
10381034

10391035
available.sort()
10401036
currentmodule = None

doc/en/fixture.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -865,13 +865,6 @@ All test methods in this TestClass will use the transaction fixture while
865865
other test classes or functions in the module will not use it unless
866866
they also add a ``transact`` reference.
867867

868-
invocation-scoped fixtures
869-
--------------------------
870-
871-
pytest 3.0 introduced a new advanced scope for fixtures: ``"invocation"``. Fixtures marked with
872-
this scope can be requested from any other scope, providing a version of the fixture for that scope.
873-
874-
See more in :ref:`invocation_scoped_fixture`.
875868

876869
Shifting (visibility of) fixture functions
877870
----------------------------------------------------

doc/en/invocation-fixture.rst

Lines changed: 0 additions & 63 deletions
This file was deleted.

doc/en/monkeypatch.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ and a discussion of its motivation.
1414

1515
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
1616

17-
As of pytest-3.0, the ``monkeypatch`` fixture is :ref:`invocation-scoped <invocation_scoped_fixture>`
18-
meaning it can be requested from fixtures of any scope.
1917

2018
Simple example: monkeypatching functions
2119
---------------------------------------------------

testing/python/fixture.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,7 @@ def test_lookup_error(unknown):
433433
"*1 error*",
434434
])
435435
assert "INTERNAL" not in result.stdout.str()
436-
# invocation-scoped fixture should appear with their friendly name only
437-
assert 'monkeypatch:session' not in result.stdout.str()
436+
438437

439438
def test_fixture_excinfo_leak(self, testdir):
440439
# on python2 sys.excinfo would leak into fixture executions
@@ -2743,13 +2742,6 @@ def test_bar(arg1):
27432742
Hi from test module
27442743
''')
27452744

2746-
def test_show_invocation_fixtures(self, testdir):
2747-
"""py.test --fixtures should display invocation-scoped fixtures once.
2748-
"""
2749-
result = testdir.runpytest("--fixtures")
2750-
result.stdout.fnmatch_lines('''monkeypatch''')
2751-
assert 'monkeypatch:session' not in result.stdout.str()
2752-
27532745

27542746
@pytest.mark.parametrize('flavor', ['fixture', 'yield_fixture'])
27552747
class TestContextManagerFixtureFuncs:

0 commit comments

Comments
 (0)