|
11 | 11 | import sys |
12 | 12 | import tempfile |
13 | 13 | import unittest |
14 | | -import warnings |
15 | 14 |
|
16 | 15 | import pytest |
17 | 16 | import _pytest.doctest |
@@ -108,7 +107,15 @@ def __init__(self, stub=None, **attrs): |
108 | 107 |
|
109 | 108 | attrs.setdefault("user_properties", []) |
110 | 109 |
|
111 | | - self.__dict__.update(attrs) |
| 110 | + slots = getattr(type(self), "__slots__", None) |
| 111 | + if slots: |
| 112 | + for name, value in attrs.items(): |
| 113 | + if name in self.__slots__: |
| 114 | + setattr(self, name, value) |
| 115 | + else: |
| 116 | + self.__dict__[name] = value |
| 117 | + else: |
| 118 | + self.__dict__.update(attrs) |
112 | 119 |
|
113 | 120 | if "own_markers" not in attrs: |
114 | 121 | self.own_markers = () |
@@ -130,6 +137,14 @@ def func(*args, **kwargs): |
130 | 137 |
|
131 | 138 |
|
132 | 139 | class StubSubtypedItem(StubPytestItem): |
| 140 | + @classmethod |
| 141 | + def from_args(cls, *args, **kwargs): |
| 142 | + if not hasattr(cls, "from_parent"): |
| 143 | + return cls(*args, **kwargs) |
| 144 | + self = cls.from_parent(None, name=kwargs["name"], runner=None, dtest=None) |
| 145 | + self.__init__(*args, **kwargs) |
| 146 | + return self |
| 147 | + |
133 | 148 | def __init__(self, *args, **kwargs): |
134 | 149 | super(StubSubtypedItem, self).__init__(*args, **kwargs) |
135 | 150 | if "nodeid" in self.__dict__: |
@@ -147,27 +162,15 @@ def function(self): |
147 | 162 |
|
148 | 163 |
|
149 | 164 | def create_stub_function_item(*args, **kwargs): |
150 | | - # StubFunctionItem should not be calling __init__(), but instead from_parent(). |
151 | | - # Unfortunately the detangling is massive due to the complexity of the test |
152 | | - # harness, so we are punting in hopes that we rewrite test discovery before |
153 | | - # pytest removes this functionality. |
154 | | - with warnings.catch_warnings(): |
155 | | - warnings.simplefilter("ignore") |
156 | | - return StubFunctionItem(*args, **kwargs) |
| 165 | + return StubFunctionItem.from_args(*args, **kwargs) |
157 | 166 |
|
158 | 167 |
|
159 | 168 | class StubDoctestItem(StubSubtypedItem, _pytest.doctest.DoctestItem): |
160 | 169 | pass |
161 | 170 |
|
162 | 171 |
|
163 | 172 | def create_stub_doctest_item(*args, **kwargs): |
164 | | - # StubDoctestItem should not be calling __init__(), but instead from_parent(). |
165 | | - # Unfortunately the detangling is massive due to the complexity of the test |
166 | | - # harness, so we are punting in hopes that we rewrite test discovery before |
167 | | - # pytest removes this functionality. |
168 | | - with warnings.catch_warnings(): |
169 | | - warnings.simplefilter("ignore") |
170 | | - return StubDoctestItem(*args, **kwargs) |
| 173 | + return StubDoctestItem.from_args(*args, **kwargs) |
171 | 174 |
|
172 | 175 |
|
173 | 176 | class StubPytestSession(util.StubProxy): |
@@ -210,6 +213,7 @@ def _fix_fileid(*args): |
210 | 213 | return adapter_util.fix_fileid( |
211 | 214 | *args, |
212 | 215 | **dict( |
| 216 | + # dependency injection |
213 | 217 | _normcase=normcase, |
214 | 218 | _pathsep=pathsep, |
215 | 219 | ) |
@@ -219,6 +223,7 @@ def _normalize_test_id(*args): |
219 | 223 | return pytest_item._normalize_test_id( |
220 | 224 | *args, |
221 | 225 | **dict( |
| 226 | + # dependency injection |
222 | 227 | _fix_fileid=_fix_fileid, |
223 | 228 | _pathsep=pathsep, |
224 | 229 | ) |
@@ -228,6 +233,7 @@ def _iter_nodes(*args): |
228 | 233 | return pytest_item._iter_nodes( |
229 | 234 | *args, |
230 | 235 | **dict( |
| 236 | + # dependency injection |
231 | 237 | _normalize_test_id=_normalize_test_id, |
232 | 238 | _normcase=normcase, |
233 | 239 | _pathsep=pathsep, |
@@ -238,6 +244,7 @@ def _parse_node_id(*args): |
238 | 244 | return pytest_item._parse_node_id( |
239 | 245 | *args, |
240 | 246 | **dict( |
| 247 | + # dependency injection |
241 | 248 | _iter_nodes=_iter_nodes, |
242 | 249 | ) |
243 | 250 | ) |
@@ -247,6 +254,7 @@ def _split_fspath(*args): |
247 | 254 | return pytest_item._split_fspath( |
248 | 255 | *args, |
249 | 256 | **dict( |
| 257 | + # dependency injection |
250 | 258 | _normcase=normcase, |
251 | 259 | ) |
252 | 260 | ) |
@@ -256,6 +264,7 @@ def _matches_relfile(*args): |
256 | 264 | return pytest_item._matches_relfile( |
257 | 265 | *args, |
258 | 266 | **dict( |
| 267 | + # dependency injection |
259 | 268 | _normcase=normcase, |
260 | 269 | _pathsep=pathsep, |
261 | 270 | ) |
@@ -265,6 +274,7 @@ def _is_legacy_wrapper(*args): |
265 | 274 | return pytest_item._is_legacy_wrapper( |
266 | 275 | *args, |
267 | 276 | **dict( |
| 277 | + # dependency injection |
268 | 278 | _pathsep=pathsep, |
269 | 279 | ) |
270 | 280 | ) |
@@ -273,6 +283,7 @@ def _get_location(*args): |
273 | 283 | return pytest_item._get_location( |
274 | 284 | *args, |
275 | 285 | **dict( |
| 286 | + # dependency injection |
276 | 287 | _matches_relfile=_matches_relfile, |
277 | 288 | _is_legacy_wrapper=_is_legacy_wrapper, |
278 | 289 | _pathsep=pathsep, |
@@ -284,6 +295,7 @@ def _parse_item(item): |
284 | 295 | return pytest_item.parse_item( |
285 | 296 | item, |
286 | 297 | **dict( |
| 298 | + # dependency injection |
287 | 299 | _parse_node_id=_parse_node_id, |
288 | 300 | _split_fspath=_split_fspath, |
289 | 301 | _get_location=_get_location, |
@@ -352,6 +364,7 @@ def test_failure(self): |
352 | 364 | self.assertEqual( |
353 | 365 | stub.calls, |
354 | 366 | [ |
| 367 | + # There's only one call. |
355 | 368 | ("pytest.main", None, {"args": self.DEFAULT_ARGS, "plugins": [plugin]}), |
356 | 369 | ], |
357 | 370 | ) |
|