@@ -27,6 +27,10 @@ def fixture(func=None, /, scope="function", autouse=False):
27
27
teardown. Fixtures may be passed to `@use()` or applied directly as
28
28
a decorator to a test or other fixture.
29
29
30
+ This function also accepts context managers and strings as the first
31
+ argument. A string will create a fixture that looks up the
32
+ `pytest.fixture` with that name.
33
+
30
34
A fixture can be assigned a scope. It will be setup for the first
31
35
test that uses it and torn down at the end of its scope.
32
36
@@ -43,18 +47,11 @@ def fixture(func):
43
47
def use (* fixtures ):
44
48
"""Apply fixture(s) to a function
45
49
46
- Any context manager may be used as a fixture.
47
-
48
- Magic fixtures may be passed to this decorator. Fixture resolution
49
- is done using the ``__name__`` attribute of the magic fixture
50
- function, so the actual fixture that is invoked will follow normal
51
- pytest fixture resolution rules, which looks first in the test
52
- module, then in relevant conftest modules, etc. This means that the
53
- fixture that is resolved may be an override of or even unrelated to
54
- the one that was passed to ``@use(...)``. In the future this may
55
- change to use precisely the passed fixture, so it is safest to pass
56
- the most specific fixture possible (the override rather than the
57
- overridden fixture).
50
+ Accepted fixture types:
51
+
52
+ - `unmagic.fixture`
53
+ - context manager
54
+ - name of a `pytest.fixture` (`str`)
58
55
"""
59
56
if not fixtures :
60
57
raise TypeError ("At least one fixture is required" )
@@ -115,41 +112,37 @@ class UnmagicFixture:
115
112
def create (cls , fixture , scope = "function" , autouse = False ):
116
113
if isinstance (fixture , cls ):
117
114
return fixture
118
- if _api .getfixturemarker (fixture ) is not None :
119
- @wraps (_api .get_real_func (fixture ))
120
- def func ():
121
- yield get_request ().getfixturevalue (fixture .__name__ )
122
- func .__pytest_wrapped__ = fixture .__pytest_wrapped__
123
- func .__unmagic_wrapped__ = fixture
115
+ if isinstance (fixture , str ):
116
+ return PytestFixture (fixture , scope , autouse )
117
+
118
+ outer = fixture
119
+ if (
120
+ callable (fixture )
121
+ and not hasattr (type (fixture ), "__enter__" )
122
+ and not hasattr (fixture , "unmagic_fixtures" )
123
+ ):
124
+ fixture = fixture ()
125
+ if not hasattr (type (fixture ), "__enter__" ):
126
+ raise TypeError (
127
+ f"{ outer !r} is not a fixture. Hint: expected generator "
128
+ "functcion, context manager, or pytest.fixture name."
129
+ )
130
+ if isinstance (fixture , _GeneratorContextManager ):
131
+ # special case for contextmanager
132
+ inner = wrapped = fixture .func
124
133
else :
125
- outer = fixture
126
- if (
127
- callable (fixture )
128
- and not hasattr (type (fixture ), "__enter__" )
129
- and not hasattr (fixture , "unmagic_fixtures" )
130
- ):
131
- fixture = fixture ()
132
- if not hasattr (type (fixture ), "__enter__" ):
133
- raise TypeError (
134
- f"{ outer !r} is not a fixture. Hint: expected generator "
135
- "functcion, context manager, or pytest.fixture."
136
- )
137
- if isinstance (fixture , _GeneratorContextManager ):
138
- # special case for contextmanager
139
- inner = wrapped = fixture .func
134
+ if isinstance (fixture , mock ._patch ):
135
+ inner = _pretty_patch (fixture )
140
136
else :
141
- if isinstance (fixture , mock ._patch ):
142
- inner = _pretty_patch (fixture )
143
- else :
144
- inner = type (fixture )
145
- wrapped = inner .__enter__ # must be a function
146
-
147
- @wraps (inner )
148
- def func ():
149
- with fixture as value :
150
- yield value
151
- func .__pytest_wrapped__ = _api .Wrapper (wrapped )
152
- func .__unmagic_wrapped__ = outer
137
+ inner = type (fixture )
138
+ wrapped = inner .__enter__ # must be a function
139
+
140
+ @wraps (inner )
141
+ def func ():
142
+ with fixture as value :
143
+ yield value
144
+ func .__pytest_wrapped__ = _api .Wrapper (wrapped )
145
+ func .__unmagic_wrapped__ = outer
153
146
# delete __wrapped__ to prevent pytest from
154
147
# introspecting arguments from wrapped function
155
148
del func .__wrapped__
@@ -220,6 +213,30 @@ def _register(self, node):
220
213
)
221
214
222
215
216
+ class PytestFixture (UnmagicFixture ):
217
+
218
+ def __init__ (self , name , scope , autouse ):
219
+ if autouse :
220
+ raise ValueError (f"Cannot autouse pytest.fixture: { name !r} " )
221
+ if scope != "function" :
222
+ raise ValueError (f"Cannot set scope of pytest.fixture: { name !r} " )
223
+
224
+ def func ():
225
+ assert 0 , "should not get here"
226
+ func .__name__ = self ._id = name
227
+ func .__doc__ = f"Unmagic-wrapped pytest.fixture: { name !r} "
228
+ super ().__init__ (func , None , None )
229
+
230
+ def _get_value (self ):
231
+ return get_request ().getfixturevalue (self ._id )
232
+
233
+ def _is_registered_for (self , node ):
234
+ return True
235
+
236
+ def _register (self , node ):
237
+ raise NotImplementedError
238
+
239
+
223
240
_SCOPE_NODE_ID = {
224
241
"function" : lambda n : n ,
225
242
"class" : lambda n : n .rsplit ("::" , 1 )[0 ],
0 commit comments