mirror of https://github.com/python/cpython
bpo-35753: Fix crash in doctest with unwrap-able functions (#22981)
Ignore objects that inspect.unwrap throws due to too many wrappers. This is a very rare case, however it can easily be surfaced when a module under doctest imports unitest.mock.call into its namespace. We simply skip any object that throws this exception. This should handle the majority of cases.
This commit is contained in:
parent
cf86996a8e
commit
565a31804c
|
@ -973,6 +973,17 @@ class DocTestFinder:
|
||||||
else:
|
else:
|
||||||
raise ValueError("object must be a class or function")
|
raise ValueError("object must be a class or function")
|
||||||
|
|
||||||
|
def _is_routine(self, obj):
|
||||||
|
"""
|
||||||
|
Safely unwrap objects and determine if they are functions.
|
||||||
|
"""
|
||||||
|
maybe_routine = obj
|
||||||
|
try:
|
||||||
|
maybe_routine = inspect.unwrap(maybe_routine)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return inspect.isroutine(maybe_routine)
|
||||||
|
|
||||||
def _find(self, tests, obj, name, module, source_lines, globs, seen):
|
def _find(self, tests, obj, name, module, source_lines, globs, seen):
|
||||||
"""
|
"""
|
||||||
Find tests for the given object and any contained objects, and
|
Find tests for the given object and any contained objects, and
|
||||||
|
@ -995,9 +1006,9 @@ class DocTestFinder:
|
||||||
if inspect.ismodule(obj) and self._recurse:
|
if inspect.ismodule(obj) and self._recurse:
|
||||||
for valname, val in obj.__dict__.items():
|
for valname, val in obj.__dict__.items():
|
||||||
valname = '%s.%s' % (name, valname)
|
valname = '%s.%s' % (name, valname)
|
||||||
|
|
||||||
# Recurse to functions & classes.
|
# Recurse to functions & classes.
|
||||||
if ((inspect.isroutine(inspect.unwrap(val))
|
if ((self._is_routine(val) or inspect.isclass(val)) and
|
||||||
or inspect.isclass(val)) and
|
|
||||||
self._from_module(module, val)):
|
self._from_module(module, val)):
|
||||||
self._find(tests, val, valname, module, source_lines,
|
self._find(tests, val, valname, module, source_lines,
|
||||||
globs, seen)
|
globs, seen)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import importlib.util
|
||||||
import unittest
|
import unittest
|
||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
|
import types
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
# NOTE: There are some additional tests relating to interaction with
|
# NOTE: There are some additional tests relating to interaction with
|
||||||
|
@ -443,7 +444,7 @@ We'll simulate a __file__ attr that ends in pyc:
|
||||||
>>> tests = finder.find(sample_func)
|
>>> tests = finder.find(sample_func)
|
||||||
|
|
||||||
>>> print(tests) # doctest: +ELLIPSIS
|
>>> print(tests) # doctest: +ELLIPSIS
|
||||||
[<DocTest sample_func from ...:27 (1 example)>]
|
[<DocTest sample_func from test_doctest.py:28 (1 example)>]
|
||||||
|
|
||||||
The exact name depends on how test_doctest was invoked, so allow for
|
The exact name depends on how test_doctest was invoked, so allow for
|
||||||
leading path components.
|
leading path components.
|
||||||
|
@ -698,6 +699,18 @@ and 'int' is a type.
|
||||||
|
|
||||||
class TestDocTestFinder(unittest.TestCase):
|
class TestDocTestFinder(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_issue35753(self):
|
||||||
|
# This import of `call` should trigger issue35753 when
|
||||||
|
# `support.run_doctest` is called due to unwrap failing,
|
||||||
|
# however with a patched doctest this should succeed.
|
||||||
|
from unittest.mock import call
|
||||||
|
dummy_module = types.ModuleType("dummy")
|
||||||
|
dummy_module.__dict__['inject_call'] = call
|
||||||
|
try:
|
||||||
|
support.run_doctest(dummy_module, verbosity=True)
|
||||||
|
except ValueError as e:
|
||||||
|
raise support.TestFailed("Doctest unwrap failed") from e
|
||||||
|
|
||||||
def test_empty_namespace_package(self):
|
def test_empty_namespace_package(self):
|
||||||
pkg_name = 'doctest_empty_pkg'
|
pkg_name = 'doctest_empty_pkg'
|
||||||
with tempfile.TemporaryDirectory() as parent_dir:
|
with tempfile.TemporaryDirectory() as parent_dir:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix crash in doctest when doctest parses modules that include unwrappable
|
||||||
|
functions by skipping those functions.
|
Loading…
Reference in New Issue