Kaydet (Commit) 052d0cd2 authored tarafından Edward Loper's avatar Edward Loper

- 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.
üst 1be1a79f
......@@ -200,6 +200,7 @@ __all__ = [
'DebugRunner',
# 6. Test Functions
'testmod',
'testfile',
'run_docstring_examples',
# 7. Tester
'Tester',
......@@ -478,6 +479,30 @@ class _OutputRedirectingPdb(pdb.Pdb):
# Restore stdout.
sys.stdout = save_stdout
def _module_relative_path(module, path):
if not inspect.ismodule(module):
raise TypeError, 'Expected a module: %r' % module
if path.startswith('/'):
raise ValueError, 'Module-relative files may not have absolute paths'
# Find the base directory for the path.
if hasattr(module, '__file__'):
# A normal module/package
basedir = os.path.split(module.__file__)[0]
elif module.__name__ == '__main__':
# An interactive session.
if len(sys.argv)>0 and sys.argv[0] != '':
basedir = os.path.split(sys.argv[0])[0]
else:
basedir = os.curdir
else:
# A module w/o __file__ (this includes builtins)
raise ValueError("Can't resolve paths relative to the module " +
module + " (it has no __file__)")
# Combine the base directory and the path.
return os.path.join(basedir, *(path.split('/')))
######################################################################
## 2. Example & DocTest
######################################################################
......@@ -1881,6 +1906,7 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
DONT_ACCEPT_BLANKLINE
NORMALIZE_WHITESPACE
ELLIPSIS
IGNORE_EXCEPTION_DETAIL
REPORT_UDIFF
REPORT_CDIFF
REPORT_NDIFF
......@@ -1896,9 +1922,7 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
treat all functions as public. Optionally, "isprivate" can be
set to doctest.is_private to skip over functions marked as private
using the underscore naming convention; see its docs for details.
"""
""" [XX] This is no longer true:
Advanced tomfoolery: testmod runs methods of a local instance of
class doctest.Tester, then merges the results into (or creates)
global Tester instance doctest.master. Methods of doctest.master
......@@ -1950,6 +1974,121 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
return runner.failures, runner.tries
def testfile(filename, module_relative=True, name=None, package=None,
globs=None, verbose=None, report=True, optionflags=0,
extraglobs=None, raise_on_error=False):
"""
Test examples in the given file. Return (#failures, #tests).
Optional keyword arg "module_relative" specifies how filenames
should be interpreted:
- If "module_relative" is True (the default), then "filename"
specifies a module-relative path. By default, this path is
relative to the calling module's directory; but if the
"package" argument is specified, then it is relative to that
package. To ensure os-independence, "filename" should use
"/" characters to separate path segments, and should not
be an absolute path (i.e., it may not begin with "/").
- If "module_relative" is False, then "filename" specifies an
os-specific path. The path may be absolute or relative (to
the current working directory).
Optional keyword arg "name" gives the name of the file; by default
use the file's name.
Optional keyword argument "package" is a Python package or the
name of a Python package whose directory should be used as the
base directory for a module relative filename. If no package is
specified, then the calling module's directory is used as the base
directory for module relative filenames. It is an error to
specify "package" if "module_relative" is False.
Optional keyword arg "globs" gives a dict to be used as the globals
when executing examples; by default, use {}. A copy of this dict
is actually used for each docstring, so that each docstring's
examples start with a clean slate.
Optional keyword arg "extraglobs" gives a dictionary that should be
merged into the globals that are used to execute examples. By
default, no extra globals are used.
Optional keyword arg "verbose" prints lots of stuff if true, prints
only failures if false; by default, it's true iff "-v" is in sys.argv.
Optional keyword arg "report" prints a summary at the end when true,
else prints nothing at the end. In verbose mode, the summary is
detailed, else very brief (in fact, empty if all tests passed).
Optional keyword arg "optionflags" or's together module constants,
and defaults to 0. Possible values (see the docs for details):
DONT_ACCEPT_TRUE_FOR_1
DONT_ACCEPT_BLANKLINE
NORMALIZE_WHITESPACE
ELLIPSIS
IGNORE_EXCEPTION_DETAIL
REPORT_UDIFF
REPORT_CDIFF
REPORT_NDIFF
REPORT_ONLY_FIRST_FAILURE
Optional keyword arg "raise_on_error" raises an exception on the
first unexpected exception or failure. This allows failures to be
post-mortem debugged.
Advanced tomfoolery: testmod runs methods of a local instance of
class doctest.Tester, then merges the results into (or creates)
global Tester instance doctest.master. Methods of doctest.master
can be called directly too, if you want to do something unusual.
Passing report=0 to testmod is especially useful then, to delay
displaying a summary. Invoke doctest.master.summarize(verbose)
when you're done fiddling.
"""
global master
if package and not module_relative:
raise ValueError("Package may only be specified for module-"
"relative paths.")
# Relativize the path
if module_relative:
package = _normalize_module(package)
filename = _module_relative_path(package, filename)
# If no name was given, then use the file's name.
if name is None:
name = os.path.split(filename)[-1]
# Assemble the globals.
if globs is None:
globs = {}
else:
globs = globs.copy()
if extraglobs is not None:
globs.update(extraglobs)
if raise_on_error:
runner = DebugRunner(verbose=verbose, optionflags=optionflags)
else:
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
# Read the file, convert it to a test, and run it.
s = open(filename).read()
test = DocTestParser().get_doctest(s, globs, name, filename, 0)
runner.run(test)
if report:
runner.summarize()
if master is None:
master = runner
else:
master.merge(runner)
return runner.failures, runner.tries
def run_docstring_examples(f, globs, verbose=False, name="NoName",
compileflags=None, optionflags=0):
"""
......@@ -2311,52 +2450,59 @@ class DocFileCase(DocTestCase):
% (self._dt_test.name, self._dt_test.filename, err)
)
def DocFileTest(path, package=None, globs=None, **options):
name = path.split('/')[-1]
def DocFileTest(path, module_relative=True, package=None,
globs=None, **options):
if globs is None:
globs = {}
if package and not module_relative:
raise ValueError("Package may only be specified for module-"
"relative paths.")
# Interpret relative paths as relative to the given package's
# directory (or the current module, if no package is specified).
if not os.path.isabs(path):
# Relativize the path.
if module_relative:
package = _normalize_module(package)
if hasattr(package, '__file__'):
# A normal package/module.
dir = os.path.split(package.__file__)[0]
path = os.path.join(dir, *(path.split('/')))
elif package.__name__ == '__main__':
# An interactive session.
if sys.argv[0] != '':
dir = os.path.split(sys.argv[0])[0]
path = os.path.join(dir, *(path.split('/')))
else:
# A module w/o __file__ (this includes builtins)
raise ValueError("Can't resolve paths relative to " +
"the module %s (it has" % package +
"no __file__)")
path = _module_relative_path(package, path)
doc = open(path).read()
# Find the file and read it.
name = os.path.split(path)[-1]
if globs is None:
globs = {}
doc = open(path).read()
# Convert it to a test, and wrap it in a DocFileCase.
test = DocTestParser().get_doctest(doc, globs, name, path, 0)
return DocFileCase(test, **options)
def DocFileSuite(*paths, **kw):
"""Creates a suite of doctest files.
"""A unittest suite for one or more doctest files.
One or more text file paths are given as strings. These should
use "/" characters to separate path segments. Paths are relative
to the directory of the calling module, or relative to the package
passed as a keyword argument.
The path to each doctest file is given as a string; the
interpretation of that string depends on the keyword argument
"module_relative".
A number of options may be provided as keyword arguments:
module_relative
If "module_relative" is True, then the given file paths are
interpreted as os-independent module-relative paths. By
default, these paths are relative to the calling module's
directory; but if the "package" argument is specified, then
they are relative to that package. To ensure os-independence,
"filename" should use "/" characters to separate path
segments, and may not be an absolute path (i.e., it may not
begin with "/").
If "module_relative" is False, then the given file paths are
interpreted as os-specific paths. These paths may be absolute
or relative (to the current working directory).
package
The name of a Python package. Text-file paths will be
interpreted relative to the directory containing this package.
The package may be supplied as a package object or as a dotted
package name.
A Python package or the name of a Python package whose directory
should be used as the base directory for module relative paths.
If "package" is not specified, then the calling module's
directory is used as the base directory for module relative
filenames. It is an error to specify "package" if
"module_relative" is False.
setUp
The name of a set-up function. This is called before running the
......@@ -2375,13 +2521,13 @@ def DocFileSuite(*paths, **kw):
optionflags
A set of doctest option flags expressed as an integer.
"""
suite = unittest.TestSuite()
# We do this here so that _normalize_module is called at the right
# level. If it were called in DocFileTest, then this function
# would be the caller and we might guess the package incorrectly.
if kw.get('module_relative', True):
kw['package'] = _normalize_module(kw.get('package'))
for path in paths:
......
......@@ -1829,8 +1829,9 @@ def test_DocFileSuite():
... package=new.module('__main__'))
>>> sys.argv = save_argv
Absolute paths may also be used; they should use the native
path separator (*not* '/').
By setting `module_relative=False`, os-specific paths may be
used (including absolute paths and paths relative to the
working directory):
>>> # Get the absolute path of the test package.
>>> test_doctest_path = os.path.abspath(test.test_doctest.__file__)
......@@ -1839,10 +1840,17 @@ def test_DocFileSuite():
>>> # Use it to find the absolute path of test_doctest.txt.
>>> test_file = os.path.join(test_pkg_path, 'test_doctest.txt')
>>> suite = doctest.DocFileSuite(test_file)
>>> suite = doctest.DocFileSuite(test_file, module_relative=False)
>>> suite.run(unittest.TestResult())
<unittest.TestResult run=1 errors=0 failures=1>
It is an error to specify `package` when `module_relative=False`:
>>> suite = doctest.DocFileSuite(test_file, module_relative=False,
... package='test')
Traceback (most recent call last):
ValueError: Package may only be specified for module-relative paths.
You can specify initial global variables:
>>> suite = doctest.DocFileSuite('test_doctest.txt',
......@@ -1991,6 +1999,127 @@ def test_unittest_reportflags():
"""
def test_testfile(): r"""
Tests for the `testfile()` function. This function runs all the
doctest examples in a given file. In its simple invokation, it is
called with the name of a file, which is taken to be relative to the
calling module. The return value is (#failures, #tests).
>>> doctest.testfile('test_doctest.txt') # doctest: +ELLIPSIS
**********************************************************************
File "...", line 6, in test_doctest.txt
Failed example:
favorite_color
Exception raised:
...
NameError: name 'favorite_color' is not defined
**********************************************************************
1 items had failures:
1 of 2 in test_doctest.txt
***Test Failed*** 1 failures.
(1, 2)
>>> doctest.master = None # Reset master.
(Note: we'll be clearing doctest.master after each call to
`doctest.testfile`, to supress warnings about multiple tests with the
same name.)
Globals may be specified with the `globs` and `extraglobs` parameters:
>>> globs = {'favorite_color': 'blue'}
>>> doctest.testfile('test_doctest.txt', globs=globs)
(0, 2)
>>> doctest.master = None # Reset master.
>>> extraglobs = {'favorite_color': 'red'}
>>> doctest.testfile('test_doctest.txt', globs=globs,
... extraglobs=extraglobs) # doctest: +ELLIPSIS
**********************************************************************
File "...", line 6, in test_doctest.txt
Failed example:
favorite_color
Expected:
'blue'
Got:
'red'
**********************************************************************
1 items had failures:
1 of 2 in test_doctest.txt
***Test Failed*** 1 failures.
(1, 2)
>>> doctest.master = None # Reset master.
The file may be made relative to a given module or package, using the
optional `module_relative` parameter:
>>> doctest.testfile('test_doctest.txt', globs=globs,
... module_relative='test')
(0, 2)
>>> doctest.master = None # Reset master.
Verbosity can be increased with the optional `verbose` paremter:
>>> doctest.testfile('test_doctest.txt', globs=globs, verbose=True)
Trying:
favorite_color
Expecting:
'blue'
ok
Trying:
if 1:
print 'a'
print
print 'b'
Expecting:
a
<BLANKLINE>
b
ok
1 items passed all tests:
2 tests in test_doctest.txt
2 tests in 1 items.
2 passed and 0 failed.
Test passed.
(0, 2)
>>> doctest.master = None # Reset master.
The name of the test may be specified with the optional `name`
parameter:
>>> doctest.testfile('test_doctest.txt', name='newname')
... # doctest: +ELLIPSIS
**********************************************************************
File "...", line 6, in newname
...
(1, 2)
>>> doctest.master = None # Reset master.
The summary report may be supressed with the optional `report`
parameter:
>>> doctest.testfile('test_doctest.txt', report=False)
... # doctest: +ELLIPSIS
**********************************************************************
File "...", line 6, in test_doctest.txt
Failed example:
favorite_color
Exception raised:
...
NameError: name 'favorite_color' is not defined
(1, 2)
>>> doctest.master = None # Reset master.
The optional keyword argument `raise_on_error` can be used to raise an
exception on the first error (which may be useful for postmortem
debugging):
>>> doctest.testfile('test_doctest.txt', raise_on_error=True)
... # doctest: +ELLIPSIS
Traceback (most recent call last):
UnexpectedException: ...
>>> doctest.master = None # Reset master.
"""
# old_test1, ... used to live in doctest.py, but cluttered it. Note
# that these use the deprecated doctest.Tester, so should go away (or
# be rewritten) someday.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment