Skip to content

Commit

Permalink
bpo-35753: fix doctest on unwrappables funcs 2/2
Browse files Browse the repository at this point in the history
Summary:

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.

This commit adds the fix to the module.

We simply skip any object that throws this exception.

This should handle the majority of cases.

Future thoughts:
1. Should catching this be optional defaulting to true,
but allowing for folks to rethrow the exception to insist
on truly clean code?
2. Should we break out _find() into more sub functions sp
that we can more easily derive our own versions for each
sub function that _find does making it easier to fix
future issues?
3. Should we include a way to denylist by `id` some
objects so that future objects that cause problems
with doctest can be passed in to ignore?

Test Plan:

Reviewers:
  • Loading branch information
splbio committed Nov 7, 2020
1 parent 042d246 commit 33958e7
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 2 deletions.
15 changes: 13 additions & 2 deletions Lib/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,17 @@ def _from_module(self, module, object):
else:
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):
"""
Find tests for the given object and any contained objects, and
Expand All @@ -994,9 +1005,9 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
if inspect.ismodule(obj) and self._recurse:
for valname, val in obj.__dict__.items():
valname = '%s.%s' % (name, valname)

# Recurse to functions & classes.
if ((inspect.isroutine(inspect.unwrap(val))
or inspect.isclass(val)) and
if ((self._is_routine(val) or inspect.isclass(val)) and
self._from_module(module, val)):
self._find(tests, val, valname, module, source_lines,
globs, seen)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash in doctest when doctest parses modules that include unwrappable
functions by skipping those functions.

0 comments on commit 33958e7

Please sign in to comment.