Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 052d0cd

Browse files
author
Edward Loper
committed
- Added "testfile" function, a simple function for running & verifying
all examples in a given text file. (analagous to "testmod") - Minor docstring fixes. - Added module_relative parameter to DocTestFile/DocTestSuite, which controls whether paths are module-relative & os-independent, or os-specific.
1 parent 1be1a79 commit 052d0cd

2 files changed

Lines changed: 315 additions & 40 deletions

File tree

Lib/doctest.py

Lines changed: 183 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ def _test():
200200
'DebugRunner',
201201
# 6. Test Functions
202202
'testmod',
203+
'testfile',
203204
'run_docstring_examples',
204205
# 7. Tester
205206
'Tester',
@@ -478,6 +479,30 @@ def trace_dispatch(self, *args):
478479
# Restore stdout.
479480
sys.stdout = save_stdout
480481

482+
def _module_relative_path(module, path):
483+
if not inspect.ismodule(module):
484+
raise TypeError, 'Expected a module: %r' % module
485+
if path.startswith('/'):
486+
raise ValueError, 'Module-relative files may not have absolute paths'
487+
488+
# Find the base directory for the path.
489+
if hasattr(module, '__file__'):
490+
# A normal module/package
491+
basedir = os.path.split(module.__file__)[0]
492+
elif module.__name__ == '__main__':
493+
# An interactive session.
494+
if len(sys.argv)>0 and sys.argv[0] != '':
495+
basedir = os.path.split(sys.argv[0])[0]
496+
else:
497+
basedir = os.curdir
498+
else:
499+
# A module w/o __file__ (this includes builtins)
500+
raise ValueError("Can't resolve paths relative to the module " +
501+
module + " (it has no __file__)")
502+
503+
# Combine the base directory and the path.
504+
return os.path.join(basedir, *(path.split('/')))
505+
481506
######################################################################
482507
## 2. Example & DocTest
483508
######################################################################
@@ -1881,6 +1906,7 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
18811906
DONT_ACCEPT_BLANKLINE
18821907
NORMALIZE_WHITESPACE
18831908
ELLIPSIS
1909+
IGNORE_EXCEPTION_DETAIL
18841910
REPORT_UDIFF
18851911
REPORT_CDIFF
18861912
REPORT_NDIFF
@@ -1896,9 +1922,7 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
18961922
treat all functions as public. Optionally, "isprivate" can be
18971923
set to doctest.is_private to skip over functions marked as private
18981924
using the underscore naming convention; see its docs for details.
1899-
"""
19001925
1901-
""" [XX] This is no longer true:
19021926
Advanced tomfoolery: testmod runs methods of a local instance of
19031927
class doctest.Tester, then merges the results into (or creates)
19041928
global Tester instance doctest.master. Methods of doctest.master
@@ -1950,6 +1974,121 @@ class doctest.Tester, then merges the results into (or creates)
19501974

19511975
return runner.failures, runner.tries
19521976

1977+
def testfile(filename, module_relative=True, name=None, package=None,
1978+
globs=None, verbose=None, report=True, optionflags=0,
1979+
extraglobs=None, raise_on_error=False):
1980+
"""
1981+
Test examples in the given file. Return (#failures, #tests).
1982+
1983+
Optional keyword arg "module_relative" specifies how filenames
1984+
should be interpreted:
1985+
1986+
- If "module_relative" is True (the default), then "filename"
1987+
specifies a module-relative path. By default, this path is
1988+
relative to the calling module's directory; but if the
1989+
"package" argument is specified, then it is relative to that
1990+
package. To ensure os-independence, "filename" should use
1991+
"/" characters to separate path segments, and should not
1992+
be an absolute path (i.e., it may not begin with "/").
1993+
1994+
- If "module_relative" is False, then "filename" specifies an
1995+
os-specific path. The path may be absolute or relative (to
1996+
the current working directory).
1997+
1998+
Optional keyword arg "name" gives the name of the file; by default
1999+
use the file's name.
2000+
2001+
Optional keyword argument "package" is a Python package or the
2002+
name of a Python package whose directory should be used as the
2003+
base directory for a module relative filename. If no package is
2004+
specified, then the calling module's directory is used as the base
2005+
directory for module relative filenames. It is an error to
2006+
specify "package" if "module_relative" is False.
2007+
2008+
Optional keyword arg "globs" gives a dict to be used as the globals
2009+
when executing examples; by default, use {}. A copy of this dict
2010+
is actually used for each docstring, so that each docstring's
2011+
examples start with a clean slate.
2012+
2013+
Optional keyword arg "extraglobs" gives a dictionary that should be
2014+
merged into the globals that are used to execute examples. By
2015+
default, no extra globals are used.
2016+
2017+
Optional keyword arg "verbose" prints lots of stuff if true, prints
2018+
only failures if false; by default, it's true iff "-v" is in sys.argv.
2019+
2020+
Optional keyword arg "report" prints a summary at the end when true,
2021+
else prints nothing at the end. In verbose mode, the summary is
2022+
detailed, else very brief (in fact, empty if all tests passed).
2023+
2024+
Optional keyword arg "optionflags" or's together module constants,
2025+
and defaults to 0. Possible values (see the docs for details):
2026+
2027+
DONT_ACCEPT_TRUE_FOR_1
2028+
DONT_ACCEPT_BLANKLINE
2029+
NORMALIZE_WHITESPACE
2030+
ELLIPSIS
2031+
IGNORE_EXCEPTION_DETAIL
2032+
REPORT_UDIFF
2033+
REPORT_CDIFF
2034+
REPORT_NDIFF
2035+
REPORT_ONLY_FIRST_FAILURE
2036+
2037+
Optional keyword arg "raise_on_error" raises an exception on the
2038+
first unexpected exception or failure. This allows failures to be
2039+
post-mortem debugged.
2040+
2041+
Advanced tomfoolery: testmod runs methods of a local instance of
2042+
class doctest.Tester, then merges the results into (or creates)
2043+
global Tester instance doctest.master. Methods of doctest.master
2044+
can be called directly too, if you want to do something unusual.
2045+
Passing report=0 to testmod is especially useful then, to delay
2046+
displaying a summary. Invoke doctest.master.summarize(verbose)
2047+
when you're done fiddling.
2048+
"""
2049+
global master
2050+
2051+
if package and not module_relative:
2052+
raise ValueError("Package may only be specified for module-"
2053+
"relative paths.")
2054+
2055+
# Relativize the path
2056+
if module_relative:
2057+
package = _normalize_module(package)
2058+
filename = _module_relative_path(package, filename)
2059+
2060+
# If no name was given, then use the file's name.
2061+
if name is None:
2062+
name = os.path.split(filename)[-1]
2063+
2064+
# Assemble the globals.
2065+
if globs is None:
2066+
globs = {}
2067+
else:
2068+
globs = globs.copy()
2069+
if extraglobs is not None:
2070+
globs.update(extraglobs)
2071+
2072+
if raise_on_error:
2073+
runner = DebugRunner(verbose=verbose, optionflags=optionflags)
2074+
else:
2075+
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
2076+
2077+
# Read the file, convert it to a test, and run it.
2078+
s = open(filename).read()
2079+
test = DocTestParser().get_doctest(s, globs, name, filename, 0)
2080+
runner.run(test)
2081+
2082+
if report:
2083+
runner.summarize()
2084+
2085+
if master is None:
2086+
master = runner
2087+
else:
2088+
master.merge(runner)
2089+
2090+
return runner.failures, runner.tries
2091+
19532092
def run_docstring_examples(f, globs, verbose=False, name="NoName",
19542093
compileflags=None, optionflags=0):
19552094
"""
@@ -2311,52 +2450,59 @@ def format_failure(self, err):
23112450
% (self._dt_test.name, self._dt_test.filename, err)
23122451
)
23132452

2314-
def DocFileTest(path, package=None, globs=None, **options):
2315-
name = path.split('/')[-1]
2453+
def DocFileTest(path, module_relative=True, package=None,
2454+
globs=None, **options):
2455+
if globs is None:
2456+
globs = {}
23162457

2317-
# Interpret relative paths as relative to the given package's
2318-
# directory (or the current module, if no package is specified).
2319-
if not os.path.isabs(path):
2458+
if package and not module_relative:
2459+
raise ValueError("Package may only be specified for module-"
2460+
"relative paths.")
2461+
2462+
# Relativize the path.
2463+
if module_relative:
23202464
package = _normalize_module(package)
2321-
if hasattr(package, '__file__'):
2322-
# A normal package/module.
2323-
dir = os.path.split(package.__file__)[0]
2324-
path = os.path.join(dir, *(path.split('/')))
2325-
elif package.__name__ == '__main__':
2326-
# An interactive session.
2327-
if sys.argv[0] != '':
2328-
dir = os.path.split(sys.argv[0])[0]
2329-
path = os.path.join(dir, *(path.split('/')))
2330-
else:
2331-
# A module w/o __file__ (this includes builtins)
2332-
raise ValueError("Can't resolve paths relative to " +
2333-
"the module %s (it has" % package +
2334-
"no __file__)")
2465+
path = _module_relative_path(package, path)
23352466

2336-
doc = open(path).read()
2467+
# Find the file and read it.
2468+
name = os.path.split(path)[-1]
23372469

2338-
if globs is None:
2339-
globs = {}
2470+
doc = open(path).read()
23402471

2472+
# Convert it to a test, and wrap it in a DocFileCase.
23412473
test = DocTestParser().get_doctest(doc, globs, name, path, 0)
2342-
23432474
return DocFileCase(test, **options)
23442475

23452476
def DocFileSuite(*paths, **kw):
2346-
"""Creates a suite of doctest files.
2347-
2348-
One or more text file paths are given as strings. These should
2349-
use "/" characters to separate path segments. Paths are relative
2350-
to the directory of the calling module, or relative to the package
2351-
passed as a keyword argument.
2477+
"""A unittest suite for one or more doctest files.
2478+
2479+
The path to each doctest file is given as a string; the
2480+
interpretation of that string depends on the keyword argument
2481+
"module_relative".
23522482
23532483
A number of options may be provided as keyword arguments:
23542484
2485+
module_relative
2486+
If "module_relative" is True, then the given file paths are
2487+
interpreted as os-independent module-relative paths. By
2488+
default, these paths are relative to the calling module's
2489+
directory; but if the "package" argument is specified, then
2490+
they are relative to that package. To ensure os-independence,
2491+
"filename" should use "/" characters to separate path
2492+
segments, and may not be an absolute path (i.e., it may not
2493+
begin with "/").
2494+
2495+
If "module_relative" is False, then the given file paths are
2496+
interpreted as os-specific paths. These paths may be absolute
2497+
or relative (to the current working directory).
2498+
23552499
package
2356-
The name of a Python package. Text-file paths will be
2357-
interpreted relative to the directory containing this package.
2358-
The package may be supplied as a package object or as a dotted
2359-
package name.
2500+
A Python package or the name of a Python package whose directory
2501+
should be used as the base directory for module relative paths.
2502+
If "package" is not specified, then the calling module's
2503+
directory is used as the base directory for module relative
2504+
filenames. It is an error to specify "package" if
2505+
"module_relative" is False.
23602506
23612507
setUp
23622508
The name of a set-up function. This is called before running the
@@ -2375,14 +2521,14 @@ def DocFileSuite(*paths, **kw):
23752521
23762522
optionflags
23772523
A set of doctest option flags expressed as an integer.
2378-
23792524
"""
23802525
suite = unittest.TestSuite()
23812526

23822527
# We do this here so that _normalize_module is called at the right
23832528
# level. If it were called in DocFileTest, then this function
23842529
# would be the caller and we might guess the package incorrectly.
2385-
kw['package'] = _normalize_module(kw.get('package'))
2530+
if kw.get('module_relative', True):
2531+
kw['package'] = _normalize_module(kw.get('package'))
23862532

23872533
for path in paths:
23882534
suite.addTest(DocFileTest(path, **kw))

0 commit comments

Comments
 (0)