Skip to content

Commit d337129

Browse files
author
Sylvain MARIE
committed
Fixed AttributeError: 'DoctestItem' object has no attribute '_request' when executing doctests. Fixes #156
1 parent 6c515a2 commit d337129

File tree

4 files changed

+55
-15
lines changed

4 files changed

+55
-15
lines changed

pytest_cases/common_pytest_lazy_values.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def get_id(self):
3636
raise NotImplementedError()
3737

3838
# @abstractmethod
39-
def get(self, request):
40-
"""Return the value to use by pytest"""
39+
def get(self, request_or_item):
40+
"""Return the actual value to use by pytest in the given context"""
4141
raise NotImplementedError()
4242

4343
def __str__(self):
@@ -200,7 +200,7 @@ def get_id(self):
200200
else:
201201
return vg.__name__
202202

203-
def get(self, request):
203+
def get(self, request_or_item):
204204
"""
205205
Calls the underlying value getter function `self.valuegetter` and returns the result.
206206
@@ -210,12 +210,16 @@ def get(self, request):
210210
211211
See https://github.com/smarie/python-pytest-cases/issues/149
212212
and https://github.com/smarie/python-pytest-cases/issues/143
213+
214+
:param request_or_item: the context of this call: either a pytest request or test node item.
213215
"""
214-
if self.cached_value_context is None or self.cached_value_context() is not request.node:
216+
node = get_test_node(request_or_item)
217+
218+
if self.cached_value_context is None or self.cached_value_context() is not node:
215219
# retrieve the value by calling the function
216220
self.cached_value = self.valuegetter()
217221
# remember the pytest context of the call with a weak reference to avoir gc issues
218-
self.cached_value_context = weakref.ref(request.node)
222+
self.cached_value_context = weakref.ref(node)
219223

220224
return self.cached_value
221225

@@ -269,8 +273,14 @@ def __repr__(self):
269273
def get_id(self):
270274
return "%s[%s]" % (self.host.get_id(), self.item)
271275

272-
def get(self, request):
273-
return self.host.force_getitem(self.item, request)
276+
def get(self, request_or_item):
277+
""" Call the underlying value getter if needed (cache), then return the result tuple item value (not self).
278+
279+
See _LazyValue.get for details
280+
281+
:param request_or_item: the context of this call: either a pytest request or test node item.
282+
"""
283+
return self.host.force_getitem(self.item, request_or_item)
274284

275285

276286
class LazyTuple(Lazy):
@@ -312,9 +322,13 @@ def get_id(self):
312322
"""return the id to use by pytest"""
313323
return self._lazyvalue.get_id()
314324

315-
def get(self, request):
316-
""" Call the underlying value getter, then return the result tuple value (not self). """
317-
return self._lazyvalue.get(request)
325+
def get(self, request_or_item):
326+
""" Call the underlying value getter if needed (cache), then return the result tuple value (not self).
327+
See _LazyValue.get for details
328+
329+
:param request_or_item: the context of this call: either a pytest request or test node item.
330+
"""
331+
return self._lazyvalue.get(request_or_item)
318332

319333
def has_cached_value(self):
320334
return self._lazyvalue.has_cached_value()
@@ -456,11 +470,13 @@ def is_lazy(argval):
456470
return False
457471

458472

459-
def get_lazy_args(argval, request):
473+
def get_lazy_args(argval, request_or_item):
460474
"""
461475
Possibly calls the lazy values contained in argval if needed, before returning it.
462476
Since the lazy values cache their result to ensure that their underlying function is called only once
463477
per test node, the `request` argument here is mandatory.
478+
479+
:param request_or_item: the context of this call: either a pytest request or item
464480
"""
465481

466482
try:
@@ -469,6 +485,20 @@ def get_lazy_args(argval, request):
469485
return argval
470486
else:
471487
if _is_lazy:
472-
return argval.get(request)
488+
return argval.get(request_or_item)
473489
else:
474490
return argval
491+
492+
493+
def get_test_node(request_or_item):
494+
"""
495+
Return the test node, typically a _pytest.Function.
496+
Provided arg may be the node already, or the pytest request
497+
498+
:param request_or_item:
499+
:return:
500+
"""
501+
try:
502+
return request_or_item.node
503+
except AttributeError:
504+
return request_or_item

pytest_cases/plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def pytest_runtest_setup(item):
6161

6262
# now item.funcargs exists so we can handle it
6363
if hasattr(item, "funcargs"):
64-
item.funcargs = {argname: get_lazy_args(argvalue, item._request)
64+
item.funcargs = {argname: get_lazy_args(argvalue, item)
6565
for argname, argvalue in item.funcargs.items()}
6666

6767

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
def doctestable():
3+
"""Do nothing, but have a doctest so that we check it will be collected and run successfully.
4+
5+
Examples
6+
--------
7+
>>> 1 + 1
8+
2
9+
"""
10+
return

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ description-file = README.md
1818
test=pytest
1919

2020
[tool:pytest]
21-
addopts = --verbose
22-
testpaths = pytest_cases/tests
21+
addopts = --verbose --doctest-modules
22+
testpaths = pytest_cases/tests/

0 commit comments

Comments
 (0)