@@ -1838,34 +1838,51 @@ def __init__(self, pyfuncitem):
1838
1838
self .fixturename = None
1839
1839
#: Scope string, one of "function", "class", "module", "session"
1840
1840
self .scope = "function"
1841
- self ._funcargs = {}
1842
- self ._fixturedefs = {}
1841
+ # rename both attributes below because their key has changed; better an attribute error
1842
+ # than subtle key misses; also backward incompatibility
1843
+ self ._fixture_values = {} # (argname, scope) -> fixture value
1844
+ self ._fixture_defs = {} # (argname, scope) -> FixtureDef
1843
1845
fixtureinfo = pyfuncitem ._fixtureinfo
1844
1846
self ._arg2fixturedefs = fixtureinfo .name2fixturedefs .copy ()
1845
1847
self ._arg2index = {}
1846
- self .fixturenames = fixtureinfo .names_closure
1847
1848
self ._fixturemanager = pyfuncitem .session ._fixturemanager
1848
1849
1850
+ @property
1851
+ def fixturenames (self ):
1852
+ # backward incompatible note: now a readonly property
1853
+ return list (self ._pyfuncitem ._fixtureinfo .names_closure )
1854
+
1849
1855
@property
1850
1856
def node (self ):
1851
1857
""" underlying collection node (depends on current request scope)"""
1852
1858
return self ._getscopeitem (self .scope )
1853
1859
1854
1860
1855
- def _getnextfixturedef (self , argname ):
1856
- fixturedefs = self ._arg2fixturedefs .get (argname , None )
1861
+ def _getnextfixturedef (self , argname , scope ):
1862
+ def trygetfixturedefs (argname ):
1863
+ fixturedefs = self ._arg2fixturedefs .get (argname , None )
1864
+ if fixturedefs is None :
1865
+ fixturedefs = self ._arg2fixturedefs .get (argname + ':' + scope , None )
1866
+ return fixturedefs
1867
+
1868
+ fixturedefs = trygetfixturedefs (argname )
1857
1869
if fixturedefs is None :
1858
1870
# we arrive here because of a a dynamic call to
1859
1871
# getfixturevalue(argname) usage which was naturally
1860
1872
# not known at parsing/collection time
1861
- fixturedefs = self ._fixturemanager .getfixturedefs (
1862
- argname , self ._pyfuncitem .parent .nodeid )
1863
- self ._arg2fixturedefs [argname ] = fixturedefs
1873
+ parentid = self ._pyfuncitem .parent .nodeid
1874
+ fixturedefs = self ._fixturemanager .getfixturedefs (argname , parentid )
1875
+ if fixturedefs :
1876
+ self ._arg2fixturedefs [argname ] = fixturedefs
1877
+ fixturedefs_by_argname = self ._fixturemanager .getfixturedefs_multiple_scopes (argname , parentid )
1878
+ if fixturedefs_by_argname :
1879
+ self ._arg2fixturedefs .update (fixturedefs_by_argname )
1880
+ fixturedefs = trygetfixturedefs (argname )
1864
1881
# fixturedefs list is immutable so we maintain a decreasing index
1865
- index = self ._arg2index .get (argname , 0 ) - 1
1882
+ index = self ._arg2index .get (( argname , scope ) , 0 ) - 1
1866
1883
if fixturedefs is None or (- index > len (fixturedefs )):
1867
1884
raise FixtureLookupError (argname , self )
1868
- self ._arg2index [argname ] = index
1885
+ self ._arg2index [( argname , scope ) ] = index
1869
1886
return fixturedefs [index ]
1870
1887
1871
1888
@property
@@ -2004,10 +2021,10 @@ def getfuncargvalue(self, argname):
2004
2021
2005
2022
def _get_active_fixturedef (self , argname ):
2006
2023
try :
2007
- return self ._fixturedefs [ argname ]
2024
+ return self ._fixture_defs [( argname , self . scope ) ]
2008
2025
except KeyError :
2009
2026
try :
2010
- fixturedef = self ._getnextfixturedef (argname )
2027
+ fixturedef = self ._getnextfixturedef (argname , self . scope )
2011
2028
except FixtureLookupError :
2012
2029
if argname == "request" :
2013
2030
class PseudoFixtureDef :
@@ -2018,8 +2035,8 @@ class PseudoFixtureDef:
2018
2035
# remove indent to prevent the python3 exception
2019
2036
# from leaking into the call
2020
2037
result = self ._getfixturevalue (fixturedef )
2021
- self ._funcargs [ argname ] = result
2022
- self ._fixturedefs [ argname ] = fixturedef
2038
+ self ._fixture_values [( argname , self . scope ) ] = result
2039
+ self ._fixture_defs [( argname , self . scope ) ] = fixturedef
2023
2040
return fixturedef
2024
2041
2025
2042
def _get_fixturestack (self ):
@@ -2140,11 +2157,10 @@ def __init__(self, request, scope, param, param_index, fixturedef):
2140
2157
self ._fixturedef = fixturedef
2141
2158
self .addfinalizer = fixturedef .addfinalizer
2142
2159
self ._pyfuncitem = request ._pyfuncitem
2143
- self ._funcargs = request ._funcargs
2144
- self ._fixturedefs = request ._fixturedefs
2160
+ self ._fixture_values = request ._fixture_values
2161
+ self ._fixture_defs = request ._fixture_defs
2145
2162
self ._arg2fixturedefs = request ._arg2fixturedefs
2146
2163
self ._arg2index = request ._arg2index
2147
- self .fixturenames = request .fixturenames
2148
2164
self ._fixturemanager = request ._fixturemanager
2149
2165
2150
2166
def __repr__ (self ):
@@ -2184,7 +2200,7 @@ def formatrepr(self):
2184
2200
fspath , lineno = getfslineno (function )
2185
2201
try :
2186
2202
lines , _ = inspect .getsourcelines (get_real_func (function ))
2187
- except (IOError , IndexError ):
2203
+ except (IOError , IndexError , TypeError ):
2188
2204
error_msg = "file %s, line %s: source code not available"
2189
2205
addline (error_msg % (fspath , lineno + 1 ))
2190
2206
else :
@@ -2198,9 +2214,9 @@ def formatrepr(self):
2198
2214
if msg is None :
2199
2215
fm = self .request ._fixturemanager
2200
2216
available = []
2201
- for name , fixturedef in fm . _arg2fixturedefs . items ():
2202
- parentid = self . request . _pyfuncitem . parent . nodeid
2203
- faclist = list (fm ._matchfactories (fixturedef , parentid ))
2217
+ parentid = self . request . _pyfuncitem . parent . nodeid
2218
+ for name , fixturedefs in fm . _arg2fixturedefs . items ():
2219
+ faclist = list (fm ._matchfactories (fixturedefs , parentid ))
2204
2220
if faclist :
2205
2221
available .append (name )
2206
2222
msg = "fixture %r not found" % (self .argname ,)
@@ -2348,6 +2364,11 @@ def merge(otherlist):
2348
2364
if fixturedefs :
2349
2365
arg2fixturedefs [argname ] = fixturedefs
2350
2366
merge (fixturedefs [- 1 ].argnames )
2367
+ fixturedefs_by_argname = self .getfixturedefs_multiple_scopes (argname , parentid )
2368
+ if fixturedefs_by_argname :
2369
+ arg2fixturedefs .update (fixturedefs_by_argname )
2370
+ for fixturedefs in fixturedefs_by_argname .values ():
2371
+ merge (fixturedefs [- 1 ].argnames )
2351
2372
return fixturenames_closure , arg2fixturedefs
2352
2373
2353
2374
def pytest_generate_tests (self , metafunc ):
@@ -2366,7 +2387,7 @@ def pytest_generate_tests(self, metafunc):
2366
2387
indirect = True , scope = fixturedef .scope ,
2367
2388
ids = fixturedef .ids )
2368
2389
else :
2369
- continue # will raise FixtureLookupError at setup time
2390
+ continue # will raise FixtureLookupError at setup time
2370
2391
2371
2392
def pytest_collection_modifyitems (self , items ):
2372
2393
# separate parametrized setups
@@ -2402,21 +2423,31 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
2402
2423
if marker .name :
2403
2424
name = marker .name
2404
2425
assert not name .startswith (self ._argprefix ), name
2405
- fixturedef = FixtureDef (self , nodeid , name , obj ,
2406
- marker .scope , marker .params ,
2407
- unittest = unittest , ids = marker .ids )
2408
- faclist = self ._arg2fixturedefs .setdefault (name , [])
2409
- if fixturedef .has_location :
2410
- faclist .append (fixturedef )
2426
+
2427
+ def new_fixture_def (name , scope ):
2428
+ fixture_def = FixtureDef (self , nodeid , name , obj ,
2429
+ scope , marker .params ,
2430
+ unittest = unittest , ids = marker .ids )
2431
+
2432
+ faclist = self ._arg2fixturedefs .setdefault (name , [])
2433
+ if fixture_def .has_location :
2434
+ faclist .append (fixture_def )
2435
+ else :
2436
+ # fixturedefs with no location are at the front
2437
+ # so this inserts the current fixturedef after the
2438
+ # existing fixturedefs from external plugins but
2439
+ # before the fixturedefs provided in conftests.
2440
+ i = len ([f for f in faclist if not f .has_location ])
2441
+ faclist .insert (i , fixture_def )
2442
+ if marker .autouse :
2443
+ autousenames .append (name )
2444
+
2445
+ if marker .scope == 'invocation' :
2446
+ for new_scope in scopes :
2447
+ new_fixture_def (name + ':{0}' .format (new_scope ), new_scope )
2411
2448
else :
2412
- # fixturedefs with no location are at the front
2413
- # so this inserts the current fixturedef after the
2414
- # existing fixturedefs from external plugins but
2415
- # before the fixturedefs provided in conftests.
2416
- i = len ([f for f in faclist if not f .has_location ])
2417
- faclist .insert (i , fixturedef )
2418
- if marker .autouse :
2419
- autousenames .append (name )
2449
+ new_fixture_def (name , marker .scope )
2450
+
2420
2451
if autousenames :
2421
2452
self ._nodeid_and_autousenames .append ((nodeid or '' , autousenames ))
2422
2453
@@ -2433,6 +2464,18 @@ def _matchfactories(self, fixturedefs, nodeid):
2433
2464
if nodeid .startswith (fixturedef .baseid ):
2434
2465
yield fixturedef
2435
2466
2467
+ def getfixturedefs_multiple_scopes (self , argname , nodeid ):
2468
+ prefix = argname + ':'
2469
+ fixturedefs_by_argname = dict ((k , v ) for k , v in self ._arg2fixturedefs .items ()
2470
+ if k .startswith (prefix ))
2471
+ if fixturedefs_by_argname :
2472
+ result = {}
2473
+ for argname , fixturedefs in fixturedefs_by_argname .items ():
2474
+ result [argname ] = tuple (self ._matchfactories (fixturedefs , nodeid ))
2475
+ return result
2476
+ else :
2477
+ return None
2478
+
2436
2479
2437
2480
def fail_fixturefunc (fixturefunc , msg ):
2438
2481
fs , lineno = getfslineno (fixturefunc )
@@ -2518,7 +2561,7 @@ def execute(self, request):
2518
2561
assert not hasattr (self , "cached_result" )
2519
2562
2520
2563
ihook = self ._fixturemanager .session .ihook
2521
- ihook .pytest_fixture_setup (fixturedef = self , request = request )
2564
+ return ihook .pytest_fixture_setup (fixturedef = self , request = request )
2522
2565
2523
2566
def __repr__ (self ):
2524
2567
return ("<FixtureDef name=%r scope=%r baseid=%r >" %
0 commit comments