diff --git a/bin-for-dist/makepluginlist.py b/bin-for-dist/makepluginlist.py index 2be5637a9..6b3d1a49a 100644 --- a/bin-for-dist/makepluginlist.py +++ b/bin-for-dist/makepluginlist.py @@ -261,7 +261,7 @@ class PluginDoc(RestWriter): return self.h2("command line options") self.Print() - formatter = py.compat.optparse.IndentedHelpFormatter() + formatter = py.std.optparse.IndentedHelpFormatter() for opt in options: switches = formatter.format_option_strings(opt) self.Print("``%s``" % switches) diff --git a/contrib/sysinfo.py b/contrib/sysinfo.py index b136a5666..0c360c423 100644 --- a/contrib/sysinfo.py +++ b/contrib/sysinfo.py @@ -7,9 +7,8 @@ obtain system info from remote machine. import py import sys -optparse = py.compat.optparse -parser = optparse.OptionParser(usage=__doc__) +parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("-f", "--sshconfig", action="store", dest="ssh_config", default=None, help="use given ssh config file, and add info all contained hosts for getting info") parser.add_option("-i", "--ignore", action="store", dest="ignores", default=None, diff --git a/doc/changelog.txt b/doc/changelog.txt index 037cd49fd..b9b994fe0 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,6 +1,8 @@ Changes between 1.0.x and 'trunk' ===================================== +* deprecate py.compat.doctest|subprocess|textwrap|optparse + * deprecate py.magic.autopath, remove py/magic directory * move pytest assertion handling to py/code and a pytest_assertion diff --git a/doc/misc.txt b/doc/misc.txt index df835d84f..7d122bec5 100644 --- a/doc/misc.txt +++ b/doc/misc.txt @@ -93,26 +93,10 @@ Cross-Python Version compatibility helpers sources: - * :source:`py/compat/` * :source:`py/builtin/` The compat and builtin namespaces help to write code using newer python features on older python interpreters. -:api:`py.compat` ----------------- - -:api:`py.compat` provides fixed versions (currently taken from Python 2.4.4) of -a few selected modules to be able to use them across python versions. Currently these are: - - * doctest - * optparse - * subprocess - * textwrap - -Note that for example ``import doctest`` and ``from py.compat import doctest`` result -into two different module objects no matter what Python version you are using. -So you should only use exactly one of these to avoid confusion in your program. - :api:`py.builtin` ----------------- diff --git a/example/execnet/sysinfo.py b/example/execnet/sysinfo.py index b136a5666..0c360c423 100644 --- a/example/execnet/sysinfo.py +++ b/example/execnet/sysinfo.py @@ -7,9 +7,8 @@ obtain system info from remote machine. import py import sys -optparse = py.compat.optparse -parser = optparse.OptionParser(usage=__doc__) +parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("-f", "--sshconfig", action="store", dest="ssh_config", default=None, help="use given ssh config file, and add info all contained hosts for getting info") parser.add_option("-i", "--ignore", action="store", dest="ignores", default=None, diff --git a/py/__init__.py b/py/__init__.py index 5f2103e67..3e436b021 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -191,12 +191,12 @@ initpkg(__name__, 'log.STDERR' : ('./log/log.py', 'STDERR'), 'log.Syslog' : ('./log/log.py', 'Syslog'), - # compatibility modules (taken from 2.4.4) + # compatibility modules (deprecated) 'compat.__doc__' : ('./compat/__init__.py', '__doc__'), - 'compat.doctest' : ('./compat/doctest.py', '*'), - 'compat.optparse' : ('./compat/optparse.py', '*'), - 'compat.textwrap' : ('./compat/textwrap.py', '*'), - 'compat.subprocess' : ('./compat/subprocess.py', '*'), + 'compat.doctest' : ('./compat/dep_doctest.py', 'doctest'), + 'compat.optparse' : ('./compat/dep_optparse.py', 'optparse'), + 'compat.textwrap' : ('./compat/dep_textwrap.py', 'textwrap'), + 'compat.subprocess' : ('./compat/dep_subprocess.py', 'subprocess'), }) diff --git a/py/cmdline/pycleanup.py b/py/cmdline/pycleanup.py index c5a137899..3e249977a 100755 --- a/py/cmdline/pycleanup.py +++ b/py/cmdline/pycleanup.py @@ -10,7 +10,7 @@ a ".". import py def main(): - parser = py.compat.optparse.OptionParser(usage=__doc__) + parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("-e", "--remove", dest="ext", default=".pyc", action="store", help="remove files with the given comma-separated list of extensions" ) diff --git a/py/cmdline/pyconvert_unittest.py b/py/cmdline/pyconvert_unittest.py index 752db7044..a975a478e 100644 --- a/py/cmdline/pyconvert_unittest.py +++ b/py/cmdline/pyconvert_unittest.py @@ -191,7 +191,7 @@ def main(): import py usage = "usage: %prog [-s [filename ...] | [-i | -c filename ...]]" - optparser = py.compat.optparse.OptionParser(usage) + optparser = py.std.optparse.OptionParser(usage) def select_output (option, opt, value, optparser, **kw): if hasattr(optparser, 'output'): diff --git a/py/cmdline/pycountloc.py b/py/cmdline/pycountloc.py index 71786ce29..ee909c8d5 100755 --- a/py/cmdline/pycountloc.py +++ b/py/cmdline/pycountloc.py @@ -12,10 +12,9 @@ current working directory). Distinguish between test files and normal ones and report them separately. """ import py -from py.compat import optparse def main(): - parser = optparse.OptionParser(usage=__doc__) + parser = py.std.optparse.OptionParser(usage=__doc__) (options, args) = parser.parse_args() countloc(args) diff --git a/py/cmdline/pylookup.py b/py/cmdline/pylookup.py index 8b4562609..e00b57acf 100755 --- a/py/cmdline/pylookup.py +++ b/py/cmdline/pylookup.py @@ -16,9 +16,7 @@ curdir = py.path.local() def rec(p): return p.check(dotfile=0) -optparse = py.compat.optparse - -parser = optparse.OptionParser(usage=__doc__) +parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("-i", "--ignore-case", action="store_true", dest="ignorecase", help="ignore case distinctions") parser.add_option("-C", "--context", action="store", type="int", dest="context", diff --git a/py/cmdline/pyrest.py b/py/cmdline/pyrest.py index 40a12d819..9c639b2be 100755 --- a/py/cmdline/pyrest.py +++ b/py/cmdline/pyrest.py @@ -22,9 +22,8 @@ else: def log(msg): pass -optparse = py.compat.optparse -parser = optparse.OptionParser(usage=__doc__) +parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("--topdf", action="store_true", dest="topdf", default=False, help="generate pdf files") parser.add_option("--stylesheet", dest="stylesheet", default=None, diff --git a/py/cmdline/pysvnwcrevert.py b/py/cmdline/pysvnwcrevert.py index 4080b2c76..bb394a9ac 100755 --- a/py/cmdline/pysvnwcrevert.py +++ b/py/cmdline/pysvnwcrevert.py @@ -41,9 +41,8 @@ def svnwcrevert(path, root=None, precious=[]): svnwcrevert(p, root) # XXX add a functional test -optparse = py.compat.optparse -parser = optparse.OptionParser(usage=__doc__) +parser = py.std.optparse.OptionParser(usage=__doc__) parser.add_option("-p", "--precious", action="append", dest="precious", default=[], help="preserve files with this name") diff --git a/py/compat/LICENSE b/py/compat/LICENSE deleted file mode 100644 index 881562cd3..000000000 --- a/py/compat/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -License for modules in py/compat directory -============================================================== - -The "*.py" files in py/compat/ and subdirectories are all -- except when otherwise stated at the beginning of the file - -copyrighted by the Python Software Foundation and licensed -under the Python Software License of which you can find a copy -here: http://www.python.org/doc/Copyright.html diff --git a/py/compat/conftest.py b/py/compat/conftest.py deleted file mode 100644 index 444089a72..000000000 --- a/py/compat/conftest.py +++ /dev/null @@ -1,5 +0,0 @@ -import py - -class Directory(py.test.collect.Directory): - def collect(self): - py.test.skip("compat tests need to be run manually") diff --git a/py/compat/dep_doctest.py b/py/compat/dep_doctest.py new file mode 100644 index 000000000..e3a247d1e --- /dev/null +++ b/py/compat/dep_doctest.py @@ -0,0 +1,4 @@ +import py + +py.log._apiwarn("1.1", "py.compat.doctest deprecated, use standard library version.", stacklevel="initpkg") +doctest = py.std.doctest diff --git a/py/compat/dep_optparse.py b/py/compat/dep_optparse.py new file mode 100644 index 000000000..79c7402a2 --- /dev/null +++ b/py/compat/dep_optparse.py @@ -0,0 +1,4 @@ +import py +py.log._apiwarn("1.1", "py.compat.optparse deprecated, use standard library version.", stacklevel="initpkg") + +optparse = py.std.optparse diff --git a/py/compat/dep_subprocess.py b/py/compat/dep_subprocess.py new file mode 100644 index 000000000..2289c111c --- /dev/null +++ b/py/compat/dep_subprocess.py @@ -0,0 +1,4 @@ + +import py +py.log._apiwarn("1.1", "py.compat.subprocess deprecated, use standard library version.", stacklevel="initpkg") +subprocess = py.std.subprocess diff --git a/py/compat/dep_textwrap.py b/py/compat/dep_textwrap.py new file mode 100644 index 000000000..bed9d6f32 --- /dev/null +++ b/py/compat/dep_textwrap.py @@ -0,0 +1,4 @@ +import py + +py.log._apiwarn("1.1", "py.compat.textwrap deprecated, use standard library version.", stacklevel="initpkg") +textwrap = py.std.textwrap diff --git a/py/compat/doctest.py b/py/compat/doctest.py deleted file mode 100644 index 7e48f7358..000000000 --- a/py/compat/doctest.py +++ /dev/null @@ -1,2672 +0,0 @@ -# Module doctest. -# Released to the public domain 16-Jan-2001, by Tim Peters (tim@python.org). -# Major enhancements and refactoring by: -# Jim Fulton -# Edward Loper - -# Provided as-is; use at your own risk; no warranty; no promises; enjoy! - -r"""Module doctest -- a framework for running examples in docstrings. - -In simplest use, end each module M to be tested with: - -def _test(): - import doctest - doctest.testmod() - -if __name__ == "__main__": - _test() - -Then running the module as a script will cause the examples in the -docstrings to get executed and verified: - -python M.py - -This won't display anything unless an example fails, in which case the -failing example(s) and the cause(s) of the failure(s) are printed to stdout -(why not stderr? because stderr is a lame hack <0.2 wink>), and the final -line of output is "Test failed.". - -Run it with the -v switch instead: - -python M.py -v - -and a detailed report of all examples tried is printed to stdout, along -with assorted summaries at the end. - -You can force verbose mode by passing "verbose=True" to testmod, or prohibit -it by passing "verbose=False". In either of those cases, sys.argv is not -examined by testmod. - -There are a variety of other ways to run doctests, including integration -with the unittest framework, and support for running non-Python text -files containing doctests. There are also many ways to override parts -of doctest's default behaviors. See the Library Reference Manual for -details. -""" - -__docformat__ = 'reStructuredText en' - -__all__ = [ - # 0, Option Flags - 'register_optionflag', - 'DONT_ACCEPT_TRUE_FOR_1', - 'DONT_ACCEPT_BLANKLINE', - 'NORMALIZE_WHITESPACE', - 'ELLIPSIS', - 'IGNORE_EXCEPTION_DETAIL', - 'COMPARISON_FLAGS', - 'REPORT_UDIFF', - 'REPORT_CDIFF', - 'REPORT_NDIFF', - 'REPORT_ONLY_FIRST_FAILURE', - 'REPORTING_FLAGS', - # 1. Utility Functions - 'is_private', - # 2. Example & DocTest - 'Example', - 'DocTest', - # 3. Doctest Parser - 'DocTestParser', - # 4. Doctest Finder - 'DocTestFinder', - # 5. Doctest Runner - 'DocTestRunner', - 'OutputChecker', - 'DocTestFailure', - 'UnexpectedException', - 'DebugRunner', - # 6. Test Functions - 'testmod', - 'testfile', - 'run_docstring_examples', - # 7. Tester - 'Tester', - # 8. Unittest Support - 'DocTestSuite', - 'DocFileSuite', - 'set_unittest_reportflags', - # 9. Debugging Support - 'script_from_examples', - 'testsource', - 'debug_src', - 'debug', -] - -import __future__ - -import sys, traceback, inspect, linecache, os, re, types -import unittest, difflib, pdb, tempfile -import warnings -from StringIO import StringIO - -# Don't whine about the deprecated is_private function in this -# module's tests. -warnings.filterwarnings("ignore", "is_private", DeprecationWarning, - __name__, 0) - -# There are 4 basic classes: -# - Example: a pair, plus an intra-docstring line number. -# - DocTest: a collection of examples, parsed from a docstring, plus -# info about where the docstring came from (name, filename, lineno). -# - DocTestFinder: extracts DocTests from a given object's docstring and -# its contained objects' docstrings. -# - DocTestRunner: runs DocTest cases, and accumulates statistics. -# -# So the basic picture is: -# -# list of: -# +------+ +---------+ +-------+ -# |object| --DocTestFinder-> | DocTest | --DocTestRunner-> |results| -# +------+ +---------+ +-------+ -# | Example | -# | ... | -# | Example | -# +---------+ - -# Option constants. - -OPTIONFLAGS_BY_NAME = {} -def register_optionflag(name): - # Create a new flag unless `name` is already known. - return OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(OPTIONFLAGS_BY_NAME)) - -DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1') -DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE') -NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE') -ELLIPSIS = register_optionflag('ELLIPSIS') -IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL') - -COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 | - DONT_ACCEPT_BLANKLINE | - NORMALIZE_WHITESPACE | - ELLIPSIS | - IGNORE_EXCEPTION_DETAIL) - -REPORT_UDIFF = register_optionflag('REPORT_UDIFF') -REPORT_CDIFF = register_optionflag('REPORT_CDIFF') -REPORT_NDIFF = register_optionflag('REPORT_NDIFF') -REPORT_ONLY_FIRST_FAILURE = register_optionflag('REPORT_ONLY_FIRST_FAILURE') - -REPORTING_FLAGS = (REPORT_UDIFF | - REPORT_CDIFF | - REPORT_NDIFF | - REPORT_ONLY_FIRST_FAILURE) - -# Special string markers for use in `want` strings: -BLANKLINE_MARKER = '' -ELLIPSIS_MARKER = '...' - -###################################################################### -## Table of Contents -###################################################################### -# 1. Utility Functions -# 2. Example & DocTest -- store test cases -# 3. DocTest Parser -- extracts examples from strings -# 4. DocTest Finder -- extracts test cases from objects -# 5. DocTest Runner -- runs test cases -# 6. Test Functions -- convenient wrappers for testing -# 7. Tester Class -- for backwards compatibility -# 8. Unittest Support -# 9. Debugging Support -# 10. Example Usage - -###################################################################### -## 1. Utility Functions -###################################################################### - -def is_private(prefix, base): - """prefix, base -> true iff name prefix + "." + base is "private". - - Prefix may be an empty string, and base does not contain a period. - Prefix is ignored (although functions you write conforming to this - protocol may make use of it). - Return true iff base begins with an (at least one) underscore, but - does not both begin and end with (at least) two underscores. - - >>> is_private("a.b", "my_func") - False - >>> is_private("____", "_my_func") - True - >>> is_private("someclass", "__init__") - False - >>> is_private("sometypo", "__init_") - True - >>> is_private("x.y.z", "_") - True - >>> is_private("_x.y.z", "__") - False - >>> is_private("", "") # senseless but consistent - False - """ - warnings.warn("is_private is deprecated; it wasn't useful; " - "examine DocTestFinder.find() lists instead", - DeprecationWarning, stacklevel=2) - return base[:1] == "_" and not base[:2] == "__" == base[-2:] - -def _extract_future_flags(globs): - """ - Return the compiler-flags associated with the future features that - have been imported into the given namespace (globs). - """ - flags = 0 - for fname in __future__.all_feature_names: - feature = globs.get(fname, None) - if feature is getattr(__future__, fname): - flags |= feature.compiler_flag - return flags - -def _normalize_module(module, depth=2): - """ - Return the module specified by `module`. In particular: - - If `module` is a module, then return module. - - If `module` is a string, then import and return the - module with that name. - - If `module` is None, then return the calling module. - The calling module is assumed to be the module of - the stack frame at the given depth in the call stack. - """ - if inspect.ismodule(module): - return module - elif isinstance(module, (str, unicode)): - return __import__(module, globals(), locals(), ["*"]) - elif module is None: - return sys.modules[sys._getframe(depth).f_globals['__name__']] - else: - raise TypeError("Expected a module, string, or None") - -def _indent(s, indent=4): - """ - Add the given number of space characters to the beginning every - non-blank line in `s`, and return the result. - """ - # This regexp matches the start of non-blank lines: - return re.sub('(?m)^(?!$)', indent*' ', s) - -def _exception_traceback(exc_info): - """ - Return a string containing a traceback message for the given - exc_info tuple (as returned by sys.exc_info()). - """ - # Get a traceback message. - excout = StringIO() - exc_type, exc_val, exc_tb = exc_info - traceback.print_exception(exc_type, exc_val, exc_tb, file=excout) - return excout.getvalue() - -# Override some StringIO methods. -class _SpoofOut(StringIO): - def getvalue(self): - result = StringIO.getvalue(self) - # If anything at all was written, make sure there's a trailing - # newline. There's no way for the expected output to indicate - # that a trailing newline is missing. - if result and not result.endswith("\n"): - result += "\n" - # Prevent softspace from screwing up the next test case, in - # case they used print with a trailing comma in an example. - if hasattr(self, "softspace"): - del self.softspace - return result - - def truncate(self, size=None): - StringIO.truncate(self, size) - if hasattr(self, "softspace"): - del self.softspace - -# Worst-case linear-time ellipsis matching. -def _ellipsis_match(want, got): - """ - Essentially the only subtle case: - >>> _ellipsis_match('aa...aa', 'aaa') - False - """ - if ELLIPSIS_MARKER not in want: - return want == got - - # Find "the real" strings. - ws = want.split(ELLIPSIS_MARKER) - assert len(ws) >= 2 - - # Deal with exact matches possibly needed at one or both ends. - startpos, endpos = 0, len(got) - w = ws[0] - if w: # starts with exact match - if got.startswith(w): - startpos = len(w) - del ws[0] - else: - return False - w = ws[-1] - if w: # ends with exact match - if got.endswith(w): - endpos -= len(w) - del ws[-1] - else: - return False - - if startpos > endpos: - # Exact end matches required more characters than we have, as in - # _ellipsis_match('aa...aa', 'aaa') - return False - - # For the rest, we only need to find the leftmost non-overlapping - # match for each piece. If there's no overall match that way alone, - # there's no overall match period. - for w in ws: - # w may be '' at times, if there are consecutive ellipses, or - # due to an ellipsis at the start or end of `want`. That's OK. - # Search for an empty string succeeds, and doesn't change startpos. - startpos = got.find(w, startpos, endpos) - if startpos < 0: - return False - startpos += len(w) - - return True - -def _comment_line(line): - "Return a commented form of the given line" - line = line.rstrip() - if line: - return '# '+line - else: - return '#' - -class _OutputRedirectingPdb(pdb.Pdb): - """ - A specialized version of the python debugger that redirects stdout - to a given stream when interacting with the user. Stdout is *not* - redirected when traced code is executed. - """ - def __init__(self, out): - self.__out = out - pdb.Pdb.__init__(self) - - def trace_dispatch(self, *args): - # Redirect stdout to the given stream. - save_stdout = sys.stdout - sys.stdout = self.__out - # Call Pdb's trace dispatch method. - try: - return pdb.Pdb.trace_dispatch(self, *args) - finally: - sys.stdout = save_stdout - -# [XX] Normalize with respect to os.path.pardir? -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 -###################################################################### -## - An "example" is a pair, where "source" is a -## fragment of source code, and "want" is the expected output for -## "source." The Example class also includes information about -## where the example was extracted from. -## -## - A "doctest" is a collection of examples, typically extracted from -## a string (such as an object's docstring). The DocTest class also -## includes information about where the string was extracted from. - -class Example: - """ - A single doctest example, consisting of source code and expected - output. `Example` defines the following attributes: - - - source: A single Python statement, always ending with a newline. - The constructor adds a newline if needed. - - - want: The expected output from running the source code (either - from stdout, or a traceback in case of exception). `want` ends - with a newline unless it's empty, in which case it's an empty - string. The constructor adds a newline if needed. - - - exc_msg: The exception message generated by the example, if - the example is expected to generate an exception; or `None` if - it is not expected to generate an exception. This exception - message is compared against the return value of - `traceback.format_exception_only()`. `exc_msg` ends with a - newline unless it's `None`. The constructor adds a newline - if needed. - - - lineno: The line number within the DocTest string containing - this Example where the Example begins. This line number is - zero-based, with respect to the beginning of the DocTest. - - - indent: The example's indentation in the DocTest string. - I.e., the number of space characters that preceed the - example's first prompt. - - - options: A dictionary mapping from option flags to True or - False, which is used to override default options for this - example. Any option flags not contained in this dictionary - are left at their default value (as specified by the - DocTestRunner's optionflags). By default, no options are set. - """ - def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, - options=None): - # Normalize inputs. - if not source.endswith('\n'): - source += '\n' - if want and not want.endswith('\n'): - want += '\n' - if exc_msg is not None and not exc_msg.endswith('\n'): - exc_msg += '\n' - # Store properties. - self.source = source - self.want = want - self.lineno = lineno - self.indent = indent - if options is None: options = {} - self.options = options - self.exc_msg = exc_msg - -class DocTest: - """ - A collection of doctest examples that should be run in a single - namespace. Each `DocTest` defines the following attributes: - - - examples: the list of examples. - - - globs: The namespace (aka globals) that the examples should - be run in. - - - name: A name identifying the DocTest (typically, the name of - the object whose docstring this DocTest was extracted from). - - - filename: The name of the file that this DocTest was extracted - from, or `None` if the filename is unknown. - - - lineno: The line number within filename where this DocTest - begins, or `None` if the line number is unavailable. This - line number is zero-based, with respect to the beginning of - the file. - - - docstring: The string that the examples were extracted from, - or `None` if the string is unavailable. - """ - def __init__(self, examples, globs, name, filename, lineno, docstring): - """ - Create a new DocTest containing the given examples. The - DocTest's globals are initialized with a copy of `globs`. - """ - assert not isinstance(examples, basestring), \ - "DocTest no longer accepts str; use DocTestParser instead" - self.examples = examples - self.docstring = docstring - self.globs = globs.copy() - self.name = name - self.filename = filename - self.lineno = lineno - - def __repr__(self): - if len(self.examples) == 0: - examples = 'no examples' - elif len(self.examples) == 1: - examples = '1 example' - else: - examples = '%d examples' % len(self.examples) - return ('' % - (self.name, self.filename, self.lineno, examples)) - - - # This lets us sort tests by name: - def __cmp__(self, other): - if not isinstance(other, DocTest): - return -1 - return cmp((self.name, self.filename, self.lineno, id(self)), - (other.name, other.filename, other.lineno, id(other))) - -###################################################################### -## 3. DocTestParser -###################################################################### - -class DocTestParser: - """ - A class used to parse strings containing doctest examples. - """ - # This regular expression is used to find doctest examples in a - # string. It defines three groups: `source` is the source code - # (including leading indentation and prompts); `indent` is the - # indentation of the first (PS1) line of the source code; and - # `want` is the expected output (including leading indentation). - _EXAMPLE_RE = re.compile(r''' - # Source consists of a PS1 line followed by zero or more PS2 lines. - (?P - (?:^(?P [ ]*) >>> .*) # PS1 line - (?:\n [ ]* \.\.\. .*)*) # PS2 lines - \n? - # Want consists of any non-blank lines that do not start with PS1. - (?P (?:(?![ ]*$) # Not a blank line - (?![ ]*>>>) # Not a line starting with PS1 - .*$\n? # But any other line - )*) - ''', re.MULTILINE | re.VERBOSE) - - # A regular expression for handling `want` strings that contain - # expected exceptions. It divides `want` into three pieces: - # - the traceback header line (`hdr`) - # - the traceback stack (`stack`) - # - the exception message (`msg`), as generated by - # traceback.format_exception_only() - # `msg` may have multiple lines. We assume/require that the - # exception message is the first non-indented line starting with a word - # character following the traceback header line. - _EXCEPTION_RE = re.compile(r""" - # Grab the traceback header. Different versions of Python have - # said different things on the first traceback line. - ^(?P Traceback\ \( - (?: most\ recent\ call\ last - | innermost\ last - ) \) : - ) - \s* $ # toss trailing whitespace on the header. - (?P .*?) # don't blink: absorb stuff until... - ^ (?P \w+ .*) # a line *starts* with alphanum. - """, re.VERBOSE | re.MULTILINE | re.DOTALL) - - # A callable returning a true value iff its argument is a blank line - # or contains a single comment. - _IS_BLANK_OR_COMMENT = re.compile(r'^[ ]*(#.*)?$').match - - def parse(self, string, name=''): - """ - Divide the given string into examples and intervening text, - and return them as a list of alternating Examples and strings. - Line numbers for the Examples are 0-based. The optional - argument `name` is a name identifying this string, and is only - used for error messages. - """ - string = string.expandtabs() - # If all lines begin with the same indentation, then strip it. - min_indent = self._min_indent(string) - if min_indent > 0: - string = '\n'.join([l[min_indent:] for l in string.split('\n')]) - - output = [] - charno, lineno = 0, 0 - # Find all doctest examples in the string: - for m in self._EXAMPLE_RE.finditer(string): - # Add the pre-example text to `output`. - output.append(string[charno:m.start()]) - # Update lineno (lines before this example) - lineno += string.count('\n', charno, m.start()) - # Extract info from the regexp match. - (source, options, want, exc_msg) = \ - self._parse_example(m, name, lineno) - # Create an Example, and add it to the list. - if not self._IS_BLANK_OR_COMMENT(source): - output.append( Example(source, want, exc_msg, - lineno=lineno, - indent=min_indent+len(m.group('indent')), - options=options) ) - # Update lineno (lines inside this example) - lineno += string.count('\n', m.start(), m.end()) - # Update charno. - charno = m.end() - # Add any remaining post-example text to `output`. - output.append(string[charno:]) - return output - - def get_doctest(self, string, globs, name, filename, lineno): - """ - Extract all doctest examples from the given string, and - collect them into a `DocTest` object. - - `globs`, `name`, `filename`, and `lineno` are attributes for - the new `DocTest` object. See the documentation for `DocTest` - for more information. - """ - return DocTest(self.get_examples(string, name), globs, - name, filename, lineno, string) - - def get_examples(self, string, name=''): - """ - Extract all doctest examples from the given string, and return - them as a list of `Example` objects. Line numbers are - 0-based, because it's most common in doctests that nothing - interesting appears on the same line as opening triple-quote, - and so the first interesting line is called \"line 1\" then. - - The optional argument `name` is a name identifying this - string, and is only used for error messages. - """ - return [x for x in self.parse(string, name) - if isinstance(x, Example)] - - def _parse_example(self, m, name, lineno): - """ - Given a regular expression match from `_EXAMPLE_RE` (`m`), - return a pair `(source, want)`, where `source` is the matched - example's source code (with prompts and indentation stripped); - and `want` is the example's expected output (with indentation - stripped). - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - """ - # Get the example's indentation level. - indent = len(m.group('indent')) - - # Divide source into lines; check that they're properly - # indented; and then strip their indentation & prompts. - source_lines = m.group('source').split('\n') - self._check_prompt_blank(source_lines, indent, name, lineno) - self._check_prefix(source_lines[1:], ' '*indent + '.', name, lineno) - source = '\n'.join([sl[indent+4:] for sl in source_lines]) - - # Divide want into lines; check that it's properly indented; and - # then strip the indentation. Spaces before the last newline should - # be preserved, so plain rstrip() isn't good enough. - want = m.group('want') - want_lines = want.split('\n') - if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): - del want_lines[-1] # forget final newline & spaces after it - self._check_prefix(want_lines, ' '*indent, name, - lineno + len(source_lines)) - want = '\n'.join([wl[indent:] for wl in want_lines]) - - # If `want` contains a traceback message, then extract it. - m = self._EXCEPTION_RE.match(want) - if m: - exc_msg = m.group('msg') - else: - exc_msg = None - - # Extract options from the source. - options = self._find_options(source, name, lineno) - - return source, options, want, exc_msg - - # This regular expression looks for option directives in the - # source code of an example. Option directives are comments - # starting with "doctest:". Warning: this may give false - # positives for string-literals that contain the string - # "#doctest:". Eliminating these false positives would require - # actually parsing the string; but we limit them by ignoring any - # line containing "#doctest:" that is *followed* by a quote mark. - _OPTION_DIRECTIVE_RE = re.compile(r'#\s*doctest:\s*([^\n\'"]*)$', - re.MULTILINE) - - def _find_options(self, source, name, lineno): - """ - Return a dictionary containing option overrides extracted from - option directives in the given source string. - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - """ - options = {} - # (note: with the current regexp, this will match at most once:) - for m in self._OPTION_DIRECTIVE_RE.finditer(source): - option_strings = m.group(1).replace(',', ' ').split() - for option in option_strings: - if (option[0] not in '+-' or - option[1:] not in OPTIONFLAGS_BY_NAME): - raise ValueError('line %r of the doctest for %s ' - 'has an invalid option: %r' % - (lineno+1, name, option)) - flag = OPTIONFLAGS_BY_NAME[option[1:]] - options[flag] = (option[0] == '+') - if options and self._IS_BLANK_OR_COMMENT(source): - raise ValueError('line %r of the doctest for %s has an option ' - 'directive on a line with no example: %r' % - (lineno, name, source)) - return options - - # This regular expression finds the indentation of every non-blank - # line in a string. - _INDENT_RE = re.compile('^([ ]*)(?=\S)', re.MULTILINE) - - def _min_indent(self, s): - "Return the minimum indentation of any non-blank line in `s`" - indents = [len(indent) for indent in self._INDENT_RE.findall(s)] - if len(indents) > 0: - return min(indents) - else: - return 0 - - def _check_prompt_blank(self, lines, indent, name, lineno): - """ - Given the lines of a source string (including prompts and - leading indentation), check to make sure that every prompt is - followed by a space character. If any line is not followed by - a space character, then raise ValueError. - """ - for i, line in enumerate(lines): - if len(line) >= indent+4 and line[indent+3] != ' ': - raise ValueError('line %r of the docstring for %s ' - 'lacks blank after %s: %r' % - (lineno+i+1, name, - line[indent:indent+3], line)) - - def _check_prefix(self, lines, prefix, name, lineno): - """ - Check that every line in the given list starts with the given - prefix; if any line does not, then raise a ValueError. - """ - for i, line in enumerate(lines): - if line and not line.startswith(prefix): - raise ValueError('line %r of the docstring for %s has ' - 'inconsistent leading whitespace: %r' % - (lineno+i+1, name, line)) - - -###################################################################### -## 4. DocTest Finder -###################################################################### - -class DocTestFinder: - """ - A class used to extract the DocTests that are relevant to a given - object, from its docstring and the docstrings of its contained - objects. Doctests can currently be extracted from the following - object types: modules, functions, classes, methods, staticmethods, - classmethods, and properties. - """ - - def __init__(self, verbose=False, parser=DocTestParser(), - recurse=True, _namefilter=None, exclude_empty=True): - """ - Create a new doctest finder. - - The optional argument `parser` specifies a class or - function that should be used to create new DocTest objects (or - objects that implement the same interface as DocTest). The - signature for this factory function should match the signature - of the DocTest constructor. - - If the optional argument `recurse` is false, then `find` will - only examine the given object, and not any contained objects. - - If the optional argument `exclude_empty` is false, then `find` - will include tests for objects with empty docstrings. - """ - self._parser = parser - self._verbose = verbose - self._recurse = recurse - self._exclude_empty = exclude_empty - # _namefilter is undocumented, and exists only for temporary backward- - # compatibility support of testmod's deprecated isprivate mess. - self._namefilter = _namefilter - - def find(self, obj, name=None, module=None, globs=None, - extraglobs=None): - """ - Return a list of the DocTests that are defined by the given - object's docstring, or by any of its contained objects' - docstrings. - - The optional parameter `module` is the module that contains - the given object. If the module is not specified or is None, then - the test finder will attempt to automatically determine the - correct module. The object's module is used: - - - As a default namespace, if `globs` is not specified. - - To prevent the DocTestFinder from extracting DocTests - from objects that are imported from other modules. - - To find the name of the file containing the object. - - To help find the line number of the object within its - file. - - Contained objects whose module does not match `module` are ignored. - - If `module` is False, no attempt to find the module will be made. - This is obscure, of use mostly in tests: if `module` is False, or - is None but cannot be found automatically, then all objects are - considered to belong to the (non-existent) module, so all contained - objects will (recursively) be searched for doctests. - - The globals for each DocTest is formed by combining `globs` - and `extraglobs` (bindings in `extraglobs` override bindings - in `globs`). A new copy of the globals dictionary is created - for each DocTest. If `globs` is not specified, then it - defaults to the module's `__dict__`, if specified, or {} - otherwise. If `extraglobs` is not specified, then it defaults - to {}. - - """ - # If name was not specified, then extract it from the object. - if name is None: - name = getattr(obj, '__name__', None) - if name is None: - raise ValueError("DocTestFinder.find: name must be given " - "when obj.__name__ doesn't exist: %r" % - (type(obj),)) - - # Find the module that contains the given object (if obj is - # a module, then module=obj.). Note: this may fail, in which - # case module will be None. - if module is False: - module = None - elif module is None: - module = inspect.getmodule(obj) - - # Read the module's source code. This is used by - # DocTestFinder._find_lineno to find the line number for a - # given object's docstring. - try: - file = inspect.getsourcefile(obj) or inspect.getfile(obj) - source_lines = linecache.getlines(file) - if not source_lines: - source_lines = None - except TypeError: - source_lines = None - - # Initialize globals, and merge in extraglobs. - if globs is None: - if module is None: - globs = {} - else: - globs = module.__dict__.copy() - else: - globs = globs.copy() - if extraglobs is not None: - globs.update(extraglobs) - - # Recursively expore `obj`, extracting DocTests. - tests = [] - self._find(tests, obj, name, module, source_lines, globs, {}) - # Sort the tests by alpha order of names, for consistency in - # verbose-mode output. This was a feature of doctest in Pythons - # <= 2.3 that got lost by accident in 2.4. It was repaired in - # 2.4.4 and 2.5. - tests.sort() - return tests - - def _filter(self, obj, prefix, base): - """ - Return true if the given object should not be examined. - """ - return (self._namefilter is not None and - self._namefilter(prefix, base)) - - def _from_module(self, module, object): - """ - Return true if the given object is defined in the given - module. - """ - if module is None: - return True - elif inspect.isfunction(object): - return module.__dict__ is object.func_globals - elif inspect.isclass(object): - return module.__name__ == object.__module__ - elif inspect.getmodule(object) is not None: - return module is inspect.getmodule(object) - elif hasattr(object, '__module__'): - return module.__name__ == object.__module__ - elif isinstance(object, property): - return True # [XX] no way not be sure. - else: - raise ValueError("object must be a class or function") - - def _find(self, tests, obj, name, module, source_lines, globs, seen): - """ - Find tests for the given object and any contained objects, and - add them to `tests`. - """ - if self._verbose: - print 'Finding tests in %s' % name - - # If we've already processed this object, then ignore it. - if id(obj) in seen: - return - seen[id(obj)] = 1 - - # Find a test for this object, and add it to the list of tests. - test = self._get_test(obj, name, module, globs, source_lines) - if test is not None: - tests.append(test) - - # Look for tests in a module's contained objects. - if inspect.ismodule(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - # Check if this contained object should be ignored. - if self._filter(val, name, valname): - continue - valname = '%s.%s' % (name, valname) - # Recurse to functions & classes. - if ((inspect.isfunction(val) or inspect.isclass(val)) and - self._from_module(module, val)): - self._find(tests, val, valname, module, source_lines, - globs, seen) - - # Look for tests in a module's __test__ dictionary. - if inspect.ismodule(obj) and self._recurse: - for valname, val in getattr(obj, '__test__', {}).items(): - if not isinstance(valname, basestring): - raise ValueError("DocTestFinder.find: __test__ keys " - "must be strings: %r" % - (type(valname),)) - if not (inspect.isfunction(val) or inspect.isclass(val) or - inspect.ismethod(val) or inspect.ismodule(val) or - isinstance(val, basestring)): - raise ValueError("DocTestFinder.find: __test__ values " - "must be strings, functions, methods, " - "classes, or modules: %r" % - (type(val),)) - valname = '%s.__test__.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - # Look for tests in a class's contained objects. - if inspect.isclass(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - # Check if this contained object should be ignored. - if self._filter(val, name, valname): - continue - # Special handling for staticmethod/classmethod. - if isinstance(val, staticmethod): - val = getattr(obj, valname) - if isinstance(val, classmethod): - val = getattr(obj, valname).im_func - - # Recurse to methods, properties, and nested classes. - if ((inspect.isfunction(val) or inspect.isclass(val) or - isinstance(val, property)) and - self._from_module(module, val)): - valname = '%s.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - def _get_test(self, obj, name, module, globs, source_lines): - """ - Return a DocTest for the given object, if it defines a docstring; - otherwise, return None. - """ - # Extract the object's docstring. If it doesn't have one, - # then return None (no test for this object). - if isinstance(obj, basestring): - docstring = obj - else: - try: - if obj.__doc__ is None: - docstring = '' - else: - docstring = obj.__doc__ - if not isinstance(docstring, basestring): - docstring = str(docstring) - except (TypeError, AttributeError): - docstring = '' - - # Find the docstring's location in the file. - lineno = self._find_lineno(obj, source_lines) - - # Don't bother if the docstring is empty. - if self._exclude_empty and not docstring: - return None - - # Return a DocTest for this object. - if module is None: - filename = None - else: - filename = getattr(module, '__file__', module.__name__) - if filename[-4:] in (".pyc", ".pyo"): - filename = filename[:-1] - return self._parser.get_doctest(docstring, globs, name, - filename, lineno) - - def _find_lineno(self, obj, source_lines): - """ - Return a line number of the given object's docstring. Note: - this method assumes that the object has a docstring. - """ - lineno = None - - # Find the line number for modules. - if inspect.ismodule(obj): - lineno = 0 - - # Find the line number for classes. - # Note: this could be fooled if a class is defined multiple - # times in a single file. - if inspect.isclass(obj): - if source_lines is None: - return None - pat = re.compile(r'^\s*class\s*%s\b' % - getattr(obj, '__name__', '-')) - for i, line in enumerate(source_lines): - if pat.match(line): - lineno = i - break - - # Find the line number for functions & methods. - if inspect.ismethod(obj): obj = obj.im_func - if inspect.isfunction(obj): obj = obj.func_code - if inspect.istraceback(obj): obj = obj.tb_frame - if inspect.isframe(obj): obj = obj.f_code - if inspect.iscode(obj): - lineno = getattr(obj, 'co_firstlineno', None)-1 - - # Find the line number where the docstring starts. Assume - # that it's the first line that begins with a quote mark. - # Note: this could be fooled by a multiline function - # signature, where a continuation line begins with a quote - # mark. - if lineno is not None: - if source_lines is None: - return lineno+1 - pat = re.compile('(^|.*:)\s*\w*("|\')') - for lineno in range(lineno, len(source_lines)): - if pat.match(source_lines[lineno]): - return lineno - - # We couldn't find the line number. - return None - -###################################################################### -## 5. DocTest Runner -###################################################################### - -class DocTestRunner: - """ - A class used to run DocTest test cases, and accumulate statistics. - The `run` method is used to process a single DocTest case. It - returns a tuple `(f, t)`, where `t` is the number of test cases - tried, and `f` is the number of test cases that failed. - - >>> tests = DocTestFinder().find(_TestClass) - >>> runner = DocTestRunner(verbose=False) - >>> tests.sort(key = lambda test: test.name) - >>> for test in tests: - ... print test.name, '->', runner.run(test) - _TestClass -> (0, 2) - _TestClass.__init__ -> (0, 2) - _TestClass.get -> (0, 2) - _TestClass.square -> (0, 1) - - The `summarize` method prints a summary of all the test cases that - have been run by the runner, and returns an aggregated `(f, t)` - tuple: - - >>> runner.summarize(verbose=1) - 4 items passed all tests: - 2 tests in _TestClass - 2 tests in _TestClass.__init__ - 2 tests in _TestClass.get - 1 tests in _TestClass.square - 7 tests in 4 items. - 7 passed and 0 failed. - Test passed. - (0, 7) - - The aggregated number of tried examples and failed examples is - also available via the `tries` and `failures` attributes: - - >>> runner.tries - 7 - >>> runner.failures - 0 - - The comparison between expected outputs and actual outputs is done - by an `OutputChecker`. This comparison may be customized with a - number of option flags; see the documentation for `testmod` for - more information. If the option flags are insufficient, then the - comparison may also be customized by passing a subclass of - `OutputChecker` to the constructor. - - The test runner's display output can be controlled in two ways. - First, an output function (`out) can be passed to - `TestRunner.run`; this function will be called with strings that - should be displayed. It defaults to `sys.stdout.write`. If - capturing the output is not sufficient, then the display output - can be also customized by subclassing DocTestRunner, and - overriding the methods `report_start`, `report_success`, - `report_unexpected_exception`, and `report_failure`. - """ - # This divider string is used to separate failure messages, and to - # separate sections of the summary. - DIVIDER = "*" * 70 - - def __init__(self, checker=None, verbose=None, optionflags=0): - """ - Create a new test runner. - - Optional keyword arg `checker` is the `OutputChecker` that - should be used to compare the expected outputs and actual - outputs of doctest examples. - - Optional keyword arg 'verbose' prints lots of stuff if true, - only failures if false; by default, it's true iff '-v' is in - sys.argv. - - Optional argument `optionflags` can be used to control how the - test runner compares expected output to actual output, and how - it displays failures. See the documentation for `testmod` for - more information. - """ - self._checker = checker or OutputChecker() - if verbose is None: - verbose = '-v' in sys.argv - self._verbose = verbose - self.optionflags = optionflags - self.original_optionflags = optionflags - - # Keep track of the examples we've run. - self.tries = 0 - self.failures = 0 - self._name2ft = {} - - # Create a fake output target for capturing doctest output. - self._fakeout = _SpoofOut() - - #///////////////////////////////////////////////////////////////// - # Reporting methods - #///////////////////////////////////////////////////////////////// - - def report_start(self, out, test, example): - """ - Report that the test runner is about to process the given - example. (Only displays a message if verbose=True) - """ - if self._verbose: - if example.want: - out('Trying:\n' + _indent(example.source) + - 'Expecting:\n' + _indent(example.want)) - else: - out('Trying:\n' + _indent(example.source) + - 'Expecting nothing\n') - - def report_success(self, out, test, example, got): - """ - Report that the given example ran successfully. (Only - displays a message if verbose=True) - """ - if self._verbose: - out("ok\n") - - def report_failure(self, out, test, example, got): - """ - Report that the given example failed. - """ - out(self._failure_header(test, example) + - self._checker.output_difference(example, got, self.optionflags)) - - def report_unexpected_exception(self, out, test, example, exc_info): - """ - Report that the given example raised an unexpected exception. - """ - out(self._failure_header(test, example) + - 'Exception raised:\n' + _indent(_exception_traceback(exc_info))) - - def _failure_header(self, test, example): - out = [self.DIVIDER] - if test.filename: - if test.lineno is not None and example.lineno is not None: - lineno = test.lineno + example.lineno + 1 - else: - lineno = '?' - out.append('File "%s", line %s, in %s' % - (test.filename, lineno, test.name)) - else: - out.append('Line %s, in %s' % (example.lineno+1, test.name)) - out.append('Failed example:') - source = example.source - out.append(_indent(source)) - return '\n'.join(out) - - #///////////////////////////////////////////////////////////////// - # DocTest Running - #///////////////////////////////////////////////////////////////// - - def __run(self, test, compileflags, out): - """ - Run the examples in `test`. Write the outcome of each example - with one of the `DocTestRunner.report_*` methods, using the - writer function `out`. `compileflags` is the set of compiler - flags that should be used to execute examples. Return a tuple - `(f, t)`, where `t` is the number of examples tried, and `f` - is the number of examples that failed. The examples are run - in the namespace `test.globs`. - """ - # Keep track of the number of failures and tries. - failures = tries = 0 - - # Save the option flags (since option directives can be used - # to modify them). - original_optionflags = self.optionflags - - SUCCESS, FAILURE, BOOM = range(3) # `outcome` state - - check = self._checker.check_output - - # Process each example. - for examplenum, example in enumerate(test.examples): - - # If REPORT_ONLY_FIRST_FAILURE is set, then supress - # reporting after the first failure. - quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and - failures > 0) - - # Merge in the example's options. - self.optionflags = original_optionflags - if example.options: - for (optionflag, val) in example.options.items(): - if val: - self.optionflags |= optionflag - else: - self.optionflags &= ~optionflag - - # Record that we started this example. - tries += 1 - if not quiet: - self.report_start(out, test, example) - - # Use a special filename for compile(), so we can retrieve - # the source code during interactive debugging (see - # __patched_linecache_getlines). - filename = '' % (test.name, examplenum) - - # Run the example in the given context (globs), and record - # any exception that gets raised. (But don't intercept - # keyboard interrupts.) - try: - # Don't blink! This is where the user's code gets run. - exec compile(example.source, filename, "single", - compileflags, 1) in test.globs - self.debugger.set_continue() # ==== Example Finished ==== - exception = None - except KeyboardInterrupt: - raise - except: - exception = sys.exc_info() - self.debugger.set_continue() # ==== Example Finished ==== - - got = self._fakeout.getvalue() # the actual output - self._fakeout.truncate(0) - outcome = FAILURE # guilty until proved innocent or insane - - # If the example executed without raising any exceptions, - # verify its output. - if exception is None: - if check(example.want, got, self.optionflags): - outcome = SUCCESS - - # The example raised an exception: check if it was expected. - else: - exc_info = sys.exc_info() - exc_msg = traceback.format_exception_only(*exc_info[:2])[-1] - if not quiet: - got += _exception_traceback(exc_info) - - # If `example.exc_msg` is None, then we weren't expecting - # an exception. - if example.exc_msg is None: - outcome = BOOM - - # We expected an exception: see whether it matches. - elif check(example.exc_msg, exc_msg, self.optionflags): - outcome = SUCCESS - - # Another chance if they didn't care about the detail. - elif self.optionflags & IGNORE_EXCEPTION_DETAIL: - m1 = re.match(r'[^:]*:', example.exc_msg) - m2 = re.match(r'[^:]*:', exc_msg) - if m1 and m2 and check(m1.group(0), m2.group(0), - self.optionflags): - outcome = SUCCESS - - # Report the outcome. - if outcome is SUCCESS: - if not quiet: - self.report_success(out, test, example, got) - elif outcome is FAILURE: - if not quiet: - self.report_failure(out, test, example, got) - failures += 1 - elif outcome is BOOM: - if not quiet: - self.report_unexpected_exception(out, test, example, - exc_info) - failures += 1 - else: - assert False, ("unknown outcome", outcome) - - # Restore the option flags (in case they were modified) - self.optionflags = original_optionflags - - # Record and return the number of failures and tries. - self.__record_outcome(test, failures, tries) - return failures, tries - - def __record_outcome(self, test, f, t): - """ - Record the fact that the given DocTest (`test`) generated `f` - failures out of `t` tried examples. - """ - f2, t2 = self._name2ft.get(test.name, (0,0)) - self._name2ft[test.name] = (f+f2, t+t2) - self.failures += f - self.tries += t - - __LINECACHE_FILENAME_RE = re.compile(r'[\w\.]+)' - r'\[(?P\d+)\]>$') - def __patched_linecache_getlines(self, filename, additional_arg=None): - m = self.__LINECACHE_FILENAME_RE.match(filename) - if m and m.group('name') == self.test.name: - example = self.test.examples[int(m.group('examplenum'))] - return example.source.splitlines(True) - else: - return self.save_linecache_getlines(filename) - - def run(self, test, compileflags=None, out=None, clear_globs=True): - """ - Run the examples in `test`, and display the results using the - writer function `out`. - - The examples are run in the namespace `test.globs`. If - `clear_globs` is true (the default), then this namespace will - be cleared after the test runs, to help with garbage - collection. If you would like to examine the namespace after - the test completes, then use `clear_globs=False`. - - `compileflags` gives the set of flags that should be used by - the Python compiler when running the examples. If not - specified, then it will default to the set of future-import - flags that apply to `globs`. - - The output of each example is checked using - `DocTestRunner.check_output`, and the results are formatted by - the `DocTestRunner.report_*` methods. - """ - self.test = test - - if compileflags is None: - compileflags = _extract_future_flags(test.globs) - - save_stdout = sys.stdout - if out is None: - out = save_stdout.write - sys.stdout = self._fakeout - - # Patch pdb.set_trace to restore sys.stdout during interactive - # debugging (so it's not still redirected to self._fakeout). - # Note that the interactive output will go to *our* - # save_stdout, even if that's not the real sys.stdout; this - # allows us to write test cases for the set_trace behavior. - save_set_trace = pdb.set_trace - self.debugger = _OutputRedirectingPdb(save_stdout) - self.debugger.reset() - pdb.set_trace = self.debugger.set_trace - - # Patch linecache.getlines, so we can see the example's source - # when we're inside the debugger. - self.save_linecache_getlines = linecache.getlines - linecache.getlines = self.__patched_linecache_getlines - - try: - return self.__run(test, compileflags, out) - finally: - sys.stdout = save_stdout - pdb.set_trace = save_set_trace - linecache.getlines = self.save_linecache_getlines - if clear_globs: - test.globs.clear() - - #///////////////////////////////////////////////////////////////// - # Summarization - #///////////////////////////////////////////////////////////////// - def summarize(self, verbose=None): - """ - Print a summary of all the test cases that have been run by - this DocTestRunner, and return a tuple `(f, t)`, where `f` is - the total number of failed examples, and `t` is the total - number of tried examples. - - The optional `verbose` argument controls how detailed the - summary is. If the verbosity is not specified, then the - DocTestRunner's verbosity is used. - """ - if verbose is None: - verbose = self._verbose - notests = [] - passed = [] - failed = [] - totalt = totalf = 0 - for x in self._name2ft.items(): - name, (f, t) = x - assert f <= t - totalt += t - totalf += f - if t == 0: - notests.append(name) - elif f == 0: - passed.append( (name, t) ) - else: - failed.append(x) - if verbose: - if notests: - print len(notests), "items had no tests:" - notests.sort() - for thing in notests: - print " ", thing - if passed: - print len(passed), "items passed all tests:" - passed.sort() - for thing, count in passed: - print " %3d tests in %s" % (count, thing) - if failed: - print self.DIVIDER - print len(failed), "items had failures:" - failed.sort() - for thing, (f, t) in failed: - print " %3d of %3d in %s" % (f, t, thing) - if verbose: - print totalt, "tests in", len(self._name2ft), "items." - print totalt - totalf, "passed and", totalf, "failed." - if totalf: - print "***Test Failed***", totalf, "failures." - elif verbose: - print "Test passed." - return totalf, totalt - - #///////////////////////////////////////////////////////////////// - # Backward compatibility cruft to maintain doctest.master. - #///////////////////////////////////////////////////////////////// - def merge(self, other): - d = self._name2ft - for name, (f, t) in other._name2ft.items(): - if name in d: - print "*** DocTestRunner.merge: '" + name + "' in both" \ - " testers; summing outcomes." - f2, t2 = d[name] - f = f + f2 - t = t + t2 - d[name] = f, t - -class OutputChecker: - """ - A class used to check the whether the actual output from a doctest - example matches the expected output. `OutputChecker` defines two - methods: `check_output`, which compares a given pair of outputs, - and returns true if they match; and `output_difference`, which - returns a string describing the differences between two outputs. - """ - def check_output(self, want, got, optionflags): - """ - Return True iff the actual output from an example (`got`) - matches the expected output (`want`). These strings are - always considered to match if they are identical; but - depending on what option flags the test runner is using, - several non-exact match types are also possible. See the - documentation for `TestRunner` for more information about - option flags. - """ - # Handle the common case first, for efficiency: - # if they're string-identical, always return true. - if got == want: - return True - - # The values True and False replaced 1 and 0 as the return - # value for boolean comparisons in Python 2.3. - if not (optionflags & DONT_ACCEPT_TRUE_FOR_1): - if (got,want) == ("True\n", "1\n"): - return True - if (got,want) == ("False\n", "0\n"): - return True - - # can be used as a special sequence to signify a - # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. - if not (optionflags & DONT_ACCEPT_BLANKLINE): - # Replace in want with a blank line. - want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), - '', want) - # If a line in got contains only spaces, then remove the - # spaces. - got = re.sub('(?m)^\s*?$', '', got) - if got == want: - return True - - # This flag causes doctest to ignore any differences in the - # contents of whitespace strings. Note that this can be used - # in conjunction with the ELLIPSIS flag. - if optionflags & NORMALIZE_WHITESPACE: - got = ' '.join(got.split()) - want = ' '.join(want.split()) - if got == want: - return True - - # The ELLIPSIS flag says to let the sequence "..." in `want` - # match any substring in `got`. - if optionflags & ELLIPSIS: - if _ellipsis_match(want, got): - return True - - # We didn't find any match; return false. - return False - - # Should we do a fancy diff? - def _do_a_fancy_diff(self, want, got, optionflags): - # Not unless they asked for a fancy diff. - if not optionflags & (REPORT_UDIFF | - REPORT_CDIFF | - REPORT_NDIFF): - return False - - # If expected output uses ellipsis, a meaningful fancy diff is - # too hard ... or maybe not. In two real-life failures Tim saw, - # a diff was a major help anyway, so this is commented out. - # [todo] _ellipsis_match() knows which pieces do and don't match, - # and could be the basis for a kick-ass diff in this case. - ##if optionflags & ELLIPSIS and ELLIPSIS_MARKER in want: - ## return False - - # ndiff does intraline difference marking, so can be useful even - # for 1-line differences. - if optionflags & REPORT_NDIFF: - return True - - # The other diff types need at least a few lines to be helpful. - return want.count('\n') > 2 and got.count('\n') > 2 - - def output_difference(self, example, got, optionflags): - """ - Return a string describing the differences between the - expected output for a given example (`example`) and the actual - output (`got`). `optionflags` is the set of option flags used - to compare `want` and `got`. - """ - want = example.want - # If s are being used, then replace blank lines - # with in the actual output string. - if not (optionflags & DONT_ACCEPT_BLANKLINE): - got = re.sub('(?m)^[ ]*(?=\n)', BLANKLINE_MARKER, got) - - # Check if we should use diff. - if self._do_a_fancy_diff(want, got, optionflags): - # Split want & got into lines. - want_lines = want.splitlines(True) # True == keep line ends - got_lines = got.splitlines(True) - # Use difflib to find their differences. - if optionflags & REPORT_UDIFF: - diff = difflib.unified_diff(want_lines, got_lines, n=2) - diff = list(diff)[2:] # strip the diff header - kind = 'unified diff with -expected +actual' - elif optionflags & REPORT_CDIFF: - diff = difflib.context_diff(want_lines, got_lines, n=2) - diff = list(diff)[2:] # strip the diff header - kind = 'context diff with expected followed by actual' - elif optionflags & REPORT_NDIFF: - engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - diff = list(engine.compare(want_lines, got_lines)) - kind = 'ndiff with -expected +actual' - else: - assert 0, 'Bad diff option' - # Remove trailing whitespace on diff output. - diff = [line.rstrip() + '\n' for line in diff] - return 'Differences (%s):\n' % kind + _indent(''.join(diff)) - - # If we're not using diff, then simply list the expected - # output followed by the actual output. - if want and got: - return 'Expected:\n%sGot:\n%s' % (_indent(want), _indent(got)) - elif want: - return 'Expected:\n%sGot nothing\n' % _indent(want) - elif got: - return 'Expected nothing\nGot:\n%s' % _indent(got) - else: - return 'Expected nothing\nGot nothing\n' - -class DocTestFailure(Exception): - """A DocTest example has failed in debugging mode. - - The exception instance has variables: - - - test: the DocTest object being run - - - excample: the Example object that failed - - - got: the actual output - """ - def __init__(self, test, example, got): - self.test = test - self.example = example - self.got = got - - def __str__(self): - return str(self.test) - -class UnexpectedException(Exception): - """A DocTest example has encountered an unexpected exception - - The exception instance has variables: - - - test: the DocTest object being run - - - excample: the Example object that failed - - - exc_info: the exception info - """ - def __init__(self, test, example, exc_info): - self.test = test - self.example = example - self.exc_info = exc_info - - def __str__(self): - return str(self.test) - -class DebugRunner(DocTestRunner): - r"""Run doc tests but raise an exception as soon as there is a failure. - - If an unexpected exception occurs, an UnexpectedException is raised. - It contains the test, the example, and the original exception: - - >>> runner = DebugRunner(verbose=False) - >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', - ... {}, 'foo', 'foo.py', 0) - >>> try: - ... runner.run(test) - ... except UnexpectedException, failure: - ... pass - - >>> failure.test is test - True - - >>> failure.example.want - '42\n' - - >>> exc_info = failure.exc_info - >>> raise exc_info[0], exc_info[1], exc_info[2] - Traceback (most recent call last): - ... - KeyError - - We wrap the original exception to give the calling application - access to the test and example information. - - If the output doesn't match, then a DocTestFailure is raised: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 1 - ... >>> x - ... 2 - ... ''', {}, 'foo', 'foo.py', 0) - - >>> try: - ... runner.run(test) - ... except DocTestFailure, failure: - ... pass - - DocTestFailure objects provide access to the test: - - >>> failure.test is test - True - - As well as to the example: - - >>> failure.example.want - '2\n' - - and the actual output: - - >>> failure.got - '1\n' - - If a failure or error occurs, the globals are left intact: - - >>> del test.globs['__builtins__'] - >>> test.globs - {'x': 1} - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 2 - ... >>> raise KeyError - ... ''', {}, 'foo', 'foo.py', 0) - - >>> runner.run(test) - Traceback (most recent call last): - ... - UnexpectedException: - - >>> del test.globs['__builtins__'] - >>> test.globs - {'x': 2} - - But the globals are cleared if there is no error: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 2 - ... ''', {}, 'foo', 'foo.py', 0) - - >>> runner.run(test) - (0, 1) - - >>> test.globs - {} - - """ - - def run(self, test, compileflags=None, out=None, clear_globs=True): - r = DocTestRunner.run(self, test, compileflags, out, False) - if clear_globs: - test.globs.clear() - return r - - def report_unexpected_exception(self, out, test, example, exc_info): - raise UnexpectedException(test, example, exc_info) - - def report_failure(self, out, test, example, got): - raise DocTestFailure(test, example, got) - -###################################################################### -## 6. Test Functions -###################################################################### -# These should be backwards compatible. - -# For backward compatibility, a global instance of a DocTestRunner -# class, updated by testmod. -master = None - -def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, - report=True, optionflags=0, extraglobs=None, - raise_on_error=False, exclude_empty=False): - """m=None, name=None, globs=None, verbose=None, isprivate=None, - report=True, optionflags=0, extraglobs=None, raise_on_error=False, - exclude_empty=False - - Test examples in docstrings in functions and classes reachable - from module m (or the current module if m is not supplied), starting - with m.__doc__. Unless isprivate is specified, private names - are not skipped. - - Also test examples reachable from dict m.__test__ if it exists and is - not None. m.__test__ maps names to functions, classes and strings; - function and class docstrings are tested even if the name is private; - strings are tested directly, as if they were docstrings. - - Return (#failures, #tests). - - See doctest.__doc__ for an overview. - - Optional keyword arg "name" gives the name of the module; by default - use m.__name__. - - Optional keyword arg "globs" gives a dict to be used as the globals - when executing examples; by default, use m.__dict__. 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. This is new in 2.4. - - 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. This is new in 2.3. 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. - - Deprecated in Python 2.4: - Optional keyword arg "isprivate" specifies a function used to - determine whether a name is private. The default function is - 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. - - 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 isprivate is not None: - warnings.warn("the isprivate argument is deprecated; " - "examine DocTestFinder.find() lists instead", - DeprecationWarning) - - # If no module was given, then use __main__. - if m is None: - # DWA - m will still be None if this wasn't invoked from the command - # line, in which case the following TypeError is about as good an error - # as we should expect - m = sys.modules.get('__main__') - - # Check that we were actually given a module. - if not inspect.ismodule(m): - raise TypeError("testmod: module required; %r" % (m,)) - - # If no name was given, then use the module's name. - if name is None: - name = m.__name__ - - # Find, parse, and run all tests in the given module. - finder = DocTestFinder(_namefilter=isprivate, exclude_empty=exclude_empty) - - if raise_on_error: - runner = DebugRunner(verbose=verbose, optionflags=optionflags) - else: - runner = DocTestRunner(verbose=verbose, optionflags=optionflags) - - for test in finder.find(m, name, globs=globs, extraglobs=extraglobs): - runner.run(test) - - if report: - runner.summarize() - - if master is None: - master = runner - else: - master.merge(runner) - - 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, parser=DocTestParser()): - """ - 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 test; by default - use the file's basename. - - 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. - - Optional keyword arg "parser" specifies a DocTestParser (or - subclass) that should be used to extract tests from the files. - - 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.basename(filename) - - # 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 = parser.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): - """ - Test examples in the given object's docstring (`f`), using `globs` - as globals. Optional argument `name` is used in failure messages. - If the optional argument `verbose` is true, then generate output - even if there are no failures. - - `compileflags` gives the set of flags that should be used by the - Python compiler when running the examples. If not specified, then - it will default to the set of future-import flags that apply to - `globs`. - - Optional keyword arg `optionflags` specifies options for the - testing and output. See the documentation for `testmod` for more - information. - """ - # Find, parse, and run all tests in the given module. - finder = DocTestFinder(verbose=verbose, recurse=False) - runner = DocTestRunner(verbose=verbose, optionflags=optionflags) - for test in finder.find(f, name, globs=globs): - runner.run(test, compileflags=compileflags) - -###################################################################### -## 7. Tester -###################################################################### -# This is provided only for backwards compatibility. It's not -# actually used in any way. - -class Tester: - def __init__(self, mod=None, globs=None, verbose=None, - isprivate=None, optionflags=0): - - warnings.warn("class Tester is deprecated; " - "use class doctest.DocTestRunner instead", - DeprecationWarning, stacklevel=2) - if mod is None and globs is None: - raise TypeError("Tester.__init__: must specify mod or globs") - if mod is not None and not inspect.ismodule(mod): - raise TypeError("Tester.__init__: mod must be a module; %r" % - (mod,)) - if globs is None: - globs = mod.__dict__ - self.globs = globs - - self.verbose = verbose - self.isprivate = isprivate - self.optionflags = optionflags - self.testfinder = DocTestFinder(_namefilter=isprivate) - self.testrunner = DocTestRunner(verbose=verbose, - optionflags=optionflags) - - def runstring(self, s, name): - test = DocTestParser().get_doctest(s, self.globs, name, None, None) - if self.verbose: - print "Running string", name - (f,t) = self.testrunner.run(test) - if self.verbose: - print f, "of", t, "examples failed in string", name - return (f,t) - - def rundoc(self, object, name=None, module=None): - f = t = 0 - tests = self.testfinder.find(object, name, module=module, - globs=self.globs) - for test in tests: - (f2, t2) = self.testrunner.run(test) - (f,t) = (f+f2, t+t2) - return (f,t) - - def rundict(self, d, name, module=None): - import new - m = new.module(name) - m.__dict__.update(d) - if module is None: - module = False - return self.rundoc(m, name, module) - - def run__test__(self, d, name): - import new - m = new.module(name) - m.__test__ = d - return self.rundoc(m, name) - - def summarize(self, verbose=None): - return self.testrunner.summarize(verbose) - - def merge(self, other): - self.testrunner.merge(other.testrunner) - -###################################################################### -## 8. Unittest Support -###################################################################### - -_unittest_reportflags = 0 - -def set_unittest_reportflags(flags): - """Sets the unittest option flags. - - The old flag is returned so that a runner could restore the old - value if it wished to: - - >>> import doctest - >>> old = doctest._unittest_reportflags - >>> doctest.set_unittest_reportflags(REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) == old - True - - >>> doctest._unittest_reportflags == (REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) - True - - Only reporting flags can be set: - - >>> doctest.set_unittest_reportflags(ELLIPSIS) - Traceback (most recent call last): - ... - ValueError: ('Only reporting flags allowed', 8) - - >>> doctest.set_unittest_reportflags(old) == (REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) - True - """ - global _unittest_reportflags - - if (flags & REPORTING_FLAGS) != flags: - raise ValueError("Only reporting flags allowed", flags) - old = _unittest_reportflags - _unittest_reportflags = flags - return old - - -class DocTestCase(unittest.TestCase): - - def __init__(self, test, optionflags=0, setUp=None, tearDown=None, - checker=None): - - unittest.TestCase.__init__(self) - self._dt_optionflags = optionflags - self._dt_checker = checker - self._dt_test = test - self._dt_setUp = setUp - self._dt_tearDown = tearDown - - def setUp(self): - test = self._dt_test - - if self._dt_setUp is not None: - self._dt_setUp(test) - - def tearDown(self): - test = self._dt_test - - if self._dt_tearDown is not None: - self._dt_tearDown(test) - - test.globs.clear() - - def runTest(self): - test = self._dt_test - old = sys.stdout - new = StringIO() - optionflags = self._dt_optionflags - - if not (optionflags & REPORTING_FLAGS): - # The option flags don't include any reporting flags, - # so add the default reporting flags - optionflags |= _unittest_reportflags - - runner = DocTestRunner(optionflags=optionflags, - checker=self._dt_checker, verbose=False) - - try: - runner.DIVIDER = "-"*70 - failures, tries = runner.run( - test, out=new.write, clear_globs=False) - finally: - sys.stdout = old - - if failures: - raise self.failureException(self.format_failure(new.getvalue())) - - def format_failure(self, err): - test = self._dt_test - if test.lineno is None: - lineno = 'unknown line number' - else: - lineno = '%s' % test.lineno - lname = '.'.join(test.name.split('.')[-1:]) - return ('Failed doctest test for %s\n' - ' File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def debug(self): - r"""Run the test case without results and without catching exceptions - - The unit test framework includes a debug method on test cases - and test suites to support post-mortem debugging. The test code - is run in such a way that errors are not caught. This way a - caller can catch the errors and initiate post-mortem debugging. - - The DocTestCase provides a debug method that raises - UnexpectedException errors if there is an unexepcted - exception: - - >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', - ... {}, 'foo', 'foo.py', 0) - >>> case = DocTestCase(test) - >>> try: - ... case.debug() - ... except UnexpectedException, failure: - ... pass - - The UnexpectedException contains the test, the example, and - the original exception: - - >>> failure.test is test - True - - >>> failure.example.want - '42\n' - - >>> exc_info = failure.exc_info - >>> raise exc_info[0], exc_info[1], exc_info[2] - Traceback (most recent call last): - ... - KeyError - - If the output doesn't match, then a DocTestFailure is raised: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 1 - ... >>> x - ... 2 - ... ''', {}, 'foo', 'foo.py', 0) - >>> case = DocTestCase(test) - - >>> try: - ... case.debug() - ... except DocTestFailure, failure: - ... pass - - DocTestFailure objects provide access to the test: - - >>> failure.test is test - True - - As well as to the example: - - >>> failure.example.want - '2\n' - - and the actual output: - - >>> failure.got - '1\n' - - """ - - self.setUp() - runner = DebugRunner(optionflags=self._dt_optionflags, - checker=self._dt_checker, verbose=False) - runner.run(self._dt_test) - self.tearDown() - - def id(self): - return self._dt_test.name - - def __repr__(self): - name = self._dt_test.name.split('.') - return "%s (%s)" % (name[-1], '.'.join(name[:-1])) - - __str__ = __repr__ - - def shortDescription(self): - return "Doctest: " + self._dt_test.name - -def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, - **options): - """ - Convert doctest tests for a module to a unittest test suite. - - This converts each documentation string in a module that - contains doctest tests to a unittest test case. If any of the - tests in a doc string fail, then the test case fails. An exception - is raised showing the name of the file containing the test and a - (sometimes approximate) line number. - - The `module` argument provides the module to be tested. The argument - can be either a module or a module name. - - If no argument is given, the calling module is used. - - A number of options may be provided as keyword arguments: - - setUp - A set-up function. This is called before running the - tests in each file. The setUp function will be passed a DocTest - object. The setUp function can access the test globals as the - globs attribute of the test passed. - - tearDown - A tear-down function. This is called after running the - tests in each file. The tearDown function will be passed a DocTest - object. The tearDown function can access the test globals as the - globs attribute of the test passed. - - globs - A dictionary containing initial global variables for the tests. - - optionflags - A set of doctest option flags expressed as an integer. - """ - - if test_finder is None: - test_finder = DocTestFinder() - - module = _normalize_module(module) - tests = test_finder.find(module, globs=globs, extraglobs=extraglobs) - if globs is None: - globs = module.__dict__ - if not tests: - # Why do we want to do this? Because it reveals a bug that might - # otherwise be hidden. - raise ValueError(module, "has no tests") - - tests.sort() - suite = unittest.TestSuite() - for test in tests: - if len(test.examples) == 0: - continue - if not test.filename: - filename = module.__file__ - if filename[-4:] in (".pyc", ".pyo"): - filename = filename[:-1] - test.filename = filename - suite.addTest(DocTestCase(test, **options)) - - return suite - -class DocFileCase(DocTestCase): - - def id(self): - return '_'.join(self._dt_test.name.split('.')) - - def __repr__(self): - return self._dt_test.filename - __str__ = __repr__ - - def format_failure(self, err): - return ('Failed doctest test for %s\n File "%s", line 0\n\n%s' - % (self._dt_test.name, self._dt_test.filename, err) - ) - -def DocFileTest(path, module_relative=True, package=None, - globs=None, parser=DocTestParser(), **options): - if globs is None: - globs = {} - - 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) - path = _module_relative_path(package, path) - - # Find the file and read it. - name = os.path.basename(path) - doc = open(path).read() - - # Convert it to a test, and wrap it in a DocFileCase. - test = parser.get_doctest(doc, globs, name, path, 0) - return DocFileCase(test, **options) - -def DocFileSuite(*paths, **kw): - """A unittest suite for one or more doctest files. - - 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 - 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 - A set-up function. This is called before running the - tests in each file. The setUp function will be passed a DocTest - object. The setUp function can access the test globals as the - globs attribute of the test passed. - - tearDown - A tear-down function. This is called after running the - tests in each file. The tearDown function will be passed a DocTest - object. The tearDown function can access the test globals as the - globs attribute of the test passed. - - globs - A dictionary containing initial global variables for the tests. - - optionflags - A set of doctest option flags expressed as an integer. - - parser - A DocTestParser (or subclass) that should be used to extract - tests from the files. - """ - 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: - suite.addTest(DocFileTest(path, **kw)) - - return suite - -###################################################################### -## 9. Debugging Support -###################################################################### - -def script_from_examples(s): - r"""Extract script from text with examples. - - Converts text with examples to a Python script. Example input is - converted to regular code. Example output and all other words - are converted to comments: - - >>> text = ''' - ... Here are examples of simple math. - ... - ... Python has super accurate integer addition - ... - ... >>> 2 + 2 - ... 5 - ... - ... And very friendly error messages: - ... - ... >>> 1/0 - ... To Infinity - ... And - ... Beyond - ... - ... You can use logic if you want: - ... - ... >>> if 0: - ... ... blah - ... ... blah - ... ... - ... - ... Ho hum - ... ''' - - >>> print script_from_examples(text) - # Here are examples of simple math. - # - # Python has super accurate integer addition - # - 2 + 2 - # Expected: - ## 5 - # - # And very friendly error messages: - # - 1/0 - # Expected: - ## To Infinity - ## And - ## Beyond - # - # You can use logic if you want: - # - if 0: - blah - blah - # - # Ho hum - - """ - output = [] - for piece in DocTestParser().parse(s): - if isinstance(piece, Example): - # Add the example's source code (strip trailing NL) - output.append(piece.source[:-1]) - # Add the expected output: - want = piece.want - if want: - output.append('# Expected:') - output += ['## '+l for l in want.split('\n')[:-1]] - else: - # Add non-example text. - output += [_comment_line(l) - for l in piece.split('\n')[:-1]] - - # Trim junk on both ends. - while output and output[-1] == '#': - output.pop() - while output and output[0] == '#': - output.pop(0) - # Combine the output, and return it. - # Add a courtesy newline to prevent exec from choking (see bug #1172785) - return '\n'.join(output) + '\n' - -def testsource(module, name): - """Extract the test sources from a doctest docstring as a script. - - Provide the module (or dotted name of the module) containing the - test to be debugged and the name (within the module) of the object - with the doc string with tests to be debugged. - """ - module = _normalize_module(module) - tests = DocTestFinder().find(module) - test = [t for t in tests if t.name == name] - if not test: - raise ValueError(name, "not found in tests") - test = test[0] - testsrc = script_from_examples(test.docstring) - return testsrc - -def debug_src(src, pm=False, globs=None): - """Debug a single doctest docstring, in argument `src`'""" - testsrc = script_from_examples(src) - debug_script(testsrc, pm, globs) - -def debug_script(src, pm=False, globs=None): - "Debug a test script. `src` is the script, as a string." - import pdb - - # Note that tempfile.NameTemporaryFile() cannot be used. As the - # docs say, a file so created cannot be opened by name a second time - # on modern Windows boxes, and execfile() needs to open it. - srcfilename = tempfile.mktemp(".py", "doctestdebug") - f = open(srcfilename, 'w') - f.write(src) - f.close() - - try: - if globs: - globs = globs.copy() - else: - globs = {} - - if pm: - try: - execfile(srcfilename, globs, globs) - except: - print sys.exc_info()[1] - pdb.post_mortem(sys.exc_info()[2]) - else: - # Note that %r is vital here. '%s' instead can, e.g., cause - # backslashes to get treated as metacharacters on Windows. - pdb.run("execfile(%r)" % srcfilename, globs, globs) - - finally: - os.remove(srcfilename) - -def debug(module, name, pm=False): - """Debug a single doctest docstring. - - Provide the module (or dotted name of the module) containing the - test to be debugged and the name (within the module) of the object - with the docstring with tests to be debugged. - """ - module = _normalize_module(module) - testsrc = testsource(module, name) - debug_script(testsrc, pm, module.__dict__) - -###################################################################### -## 10. Example Usage -###################################################################### -class _TestClass: - """ - A pointless class, for sanity-checking of docstring testing. - - Methods: - square() - get() - - >>> _TestClass(13).get() + _TestClass(-12).get() - 1 - >>> hex(_TestClass(13).square().get()) - '0xa9' - """ - - def __init__(self, val): - """val -> _TestClass object with associated value val. - - >>> t = _TestClass(123) - >>> print t.get() - 123 - """ - - self.val = val - - def square(self): - """square() -> square TestClass's associated value - - >>> _TestClass(13).square().get() - 169 - """ - - self.val = self.val ** 2 - return self - - def get(self): - """get() -> return TestClass's associated value. - - >>> x = _TestClass(-42) - >>> print x.get() - -42 - """ - - return self.val - -__test__ = {"_TestClass": _TestClass, - "string": r""" - Example of a string object, searched as-is. - >>> x = 1; y = 2 - >>> x + y, x * y - (3, 2) - """, - - "bool-int equivalence": r""" - In 2.2, boolean expressions displayed - 0 or 1. By default, we still accept - them. This can be disabled by passing - DONT_ACCEPT_TRUE_FOR_1 to the new - optionflags argument. - >>> 4 == 4 - 1 - >>> 4 == 4 - True - >>> 4 > 4 - 0 - >>> 4 > 4 - False - """, - - "blank lines": r""" - Blank lines can be marked with : - >>> print 'foo\n\nbar\n' - foo - - bar - - """, - - "ellipsis": r""" - If the ellipsis flag is used, then '...' can be used to - elide substrings in the desired output: - >>> print range(1000) #doctest: +ELLIPSIS - [0, 1, 2, ..., 999] - """, - - "whitespace normalization": r""" - If the whitespace normalization flag is used, then - differences in whitespace are ignored. - >>> print range(30) #doctest: +NORMALIZE_WHITESPACE - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29] - """, - } - -def _test(): - r = unittest.TextTestRunner() - r.run(DocTestSuite()) - -if __name__ == "__main__": - _test() diff --git a/py/compat/optparse.py b/py/compat/optparse.py deleted file mode 100644 index 6fff1bc3e..000000000 --- a/py/compat/optparse.py +++ /dev/null @@ -1,1570 +0,0 @@ -"""optparse - a powerful, extensible, and easy-to-use option parser. - -By Greg Ward - -Originally distributed as Optik; see http://optik.sourceforge.net/ . - -If you have problems with this module, please do not file bugs, -patches, or feature requests with Python; instead, use Optik's -SourceForge project page: - http://sourceforge.net/projects/optik - -For support, use the optik-users@lists.sourceforge.net mailing list -(http://lists.sourceforge.net/lists/listinfo/optik-users). -""" - -# Python developers: please do not make changes to this file, since -# it is automatically generated from the Optik source code. - -__version__ = "1.5a2" - -__all__ = ['Option', - 'SUPPRESS_HELP', - 'SUPPRESS_USAGE', - 'Values', - 'OptionContainer', - 'OptionGroup', - 'OptionParser', - 'HelpFormatter', - 'IndentedHelpFormatter', - 'TitledHelpFormatter', - 'OptParseError', - 'OptionError', - 'OptionConflictError', - 'OptionValueError', - 'BadOptionError'] - -__copyright__ = """ -Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved. -Copyright (c) 2002-2004 Python Software Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import sys, os -import types -import textwrap -from gettext import gettext as _ - -def _repr(self): - return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) - - -# This file was generated from: -# Id: option_parser.py 421 2004-10-26 00:45:16Z greg -# Id: option.py 422 2004-10-26 00:53:47Z greg -# Id: help.py 367 2004-07-24 23:21:21Z gward -# Id: errors.py 367 2004-07-24 23:21:21Z gward - -class OptParseError (Exception): - def __init__(self, msg): - self.msg = msg - - def __str__(self): - return self.msg - - -class OptionError (OptParseError): - """ - Raised if an Option instance is created with invalid or - inconsistent arguments. - """ - - def __init__(self, msg, option): - self.msg = msg - self.option_id = str(option) - - def __str__(self): - if self.option_id: - return "option %s: %s" % (self.option_id, self.msg) - else: - return self.msg - -class OptionConflictError (OptionError): - """ - Raised if conflicting options are added to an OptionParser. - """ - -class OptionValueError (OptParseError): - """ - Raised if an invalid option value is encountered on the command - line. - """ - -class BadOptionError (OptParseError): - """ - Raised if an invalid or ambiguous option is seen on the command-line. - """ - - -class HelpFormatter: - - """ - Abstract base class for formatting option help. OptionParser - instances should use one of the HelpFormatter subclasses for - formatting help; by default IndentedHelpFormatter is used. - - Instance attributes: - parser : OptionParser - the controlling OptionParser instance - indent_increment : int - the number of columns to indent per nesting level - max_help_position : int - the maximum starting column for option help text - help_position : int - the calculated starting column for option help text; - initially the same as the maximum - width : int - total number of columns for output (pass None to constructor for - this value to be taken from the $COLUMNS environment variable) - level : int - current indentation level - current_indent : int - current indentation level (in columns) - help_width : int - number of columns available for option help text (calculated) - default_tag : str - text to replace with each option's default value, "%default" - by default. Set to false value to disable default value expansion. - option_strings : { Option : str } - maps Option instances to the snippet of help text explaining - the syntax of that option, e.g. "-h, --help" or - "-fFILE, --file=FILE" - _short_opt_fmt : str - format string controlling how short options with values are - printed in help text. Must be either "%s%s" ("-fFILE") or - "%s %s" ("-f FILE"), because those are the two syntaxes that - Optik supports. - _long_opt_fmt : str - similar but for long options; must be either "%s %s" ("--file FILE") - or "%s=%s" ("--file=FILE"). - """ - - NO_DEFAULT_VALUE = "none" - - def __init__(self, - indent_increment, - max_help_position, - width, - short_first): - self.parser = None - self.indent_increment = indent_increment - self.help_position = self.max_help_position = max_help_position - if width is None: - try: - width = int(os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - self.width = width - self.current_indent = 0 - self.level = 0 - self.help_width = None # computed later - self.short_first = short_first - self.default_tag = "%default" - self.option_strings = {} - self._short_opt_fmt = "%s %s" - self._long_opt_fmt = "%s=%s" - - def set_parser(self, parser): - self.parser = parser - - def set_short_opt_delimiter(self, delim): - if delim not in ("", " "): - raise ValueError( - "invalid metavar delimiter for short options: %r" % delim) - self._short_opt_fmt = "%s" + delim + "%s" - - def set_long_opt_delimiter(self, delim): - if delim not in ("=", " "): - raise ValueError( - "invalid metavar delimiter for long options: %r" % delim) - self._long_opt_fmt = "%s" + delim + "%s" - - def indent(self): - self.current_indent += self.indent_increment - self.level += 1 - - def dedent(self): - self.current_indent -= self.indent_increment - assert self.current_indent >= 0, "Indent decreased below 0." - self.level -= 1 - - def format_usage(self, usage): - raise NotImplementedError, "subclasses must implement" - - def format_heading(self, heading): - raise NotImplementedError, "subclasses must implement" - - def format_description(self, description): - if not description: - return "" - desc_width = self.width - self.current_indent - indent = " "*self.current_indent - return textwrap.fill(description, - desc_width, - initial_indent=indent, - subsequent_indent=indent) + "\n" - - def expand_default(self, option): - if self.parser is None or not self.default_tag: - return option.help - - default_value = self.parser.defaults.get(option.dest) - if default_value is NO_DEFAULT or default_value is None: - default_value = self.NO_DEFAULT_VALUE - - return option.help.replace(self.default_tag, str(default_value)) - - def format_option(self, option): - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("-x", or "-fFILENAME, --file=FILENAME") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # -x turn on expert mode - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # -fFILENAME, --file=FILENAME - # read data from FILENAME - result = [] - opts = self.option_strings[option] - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - opts = "%*s%s\n" % (self.current_indent, "", opts) - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - help_text = self.expand_default(option) - help_lines = textwrap.wrap(help_text, self.help_width) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - result.extend(["%*s%s\n" % (self.help_position, "", line) - for line in help_lines[1:]]) - elif opts[-1] != "\n": - result.append("\n") - return "".join(result) - - def store_option_strings(self, parser): - self.indent() - max_len = 0 - for opt in parser.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.indent() - for group in parser.option_groups: - for opt in group.option_list: - strings = self.format_option_strings(opt) - self.option_strings[opt] = strings - max_len = max(max_len, len(strings) + self.current_indent) - self.dedent() - self.dedent() - self.help_position = min(max_len + 2, self.max_help_position) - self.help_width = self.width - self.help_position - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - if option.takes_value(): - metavar = option.metavar or option.dest.upper() - short_opts = [self._short_opt_fmt % (sopt, metavar) - for sopt in option._short_opts] - long_opts = [self._long_opt_fmt % (lopt, metavar) - for lopt in option._long_opts] - else: - short_opts = option._short_opts - long_opts = option._long_opts - - if self.short_first: - opts = short_opts + long_opts - else: - opts = long_opts + short_opts - - return ", ".join(opts) - -class IndentedHelpFormatter (HelpFormatter): - """Format help with indented section bodies. - """ - - def __init__(self, - indent_increment=2, - max_help_position=24, - width=None, - short_first=1): - HelpFormatter.__init__( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return _("usage: %s\n") % usage - - def format_heading(self, heading): - return "%*s%s:\n" % (self.current_indent, "", heading) - - -class TitledHelpFormatter (HelpFormatter): - """Format help with underlined section headers. - """ - - def __init__(self, - indent_increment=0, - max_help_position=24, - width=None, - short_first=0): - HelpFormatter.__init__ ( - self, indent_increment, max_help_position, width, short_first) - - def format_usage(self, usage): - return "%s %s\n" % (self.format_heading(_("Usage")), usage) - - def format_heading(self, heading): - return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading)) - - -_builtin_cvt = { "int" : (int, _("integer")), - "long" : (long, _("long integer")), - "float" : (float, _("floating-point")), - "complex" : (complex, _("complex")) } - -def check_builtin(option, opt, value): - (cvt, what) = _builtin_cvt[option.type] - try: - return cvt(value) - except ValueError: - raise OptionValueError( - _("option %s: invalid %s value: %r") % (opt, what, value)) - -def check_choice(option, opt, value): - if value in option.choices: - return value - else: - choices = ", ".join(map(repr, option.choices)) - raise OptionValueError( - _("option %s: invalid choice: %r (choose from %s)") - % (opt, value, choices)) - -# Not supplying a default is different from a default of None, -# so we need an explicit "not supplied" value. -NO_DEFAULT = ("NO", "DEFAULT") - - -class Option: - """ - Instance attributes: - _short_opts : [string] - _long_opts : [string] - - action : string - type : string - dest : string - default : any - nargs : int - const : any - choices : [string] - callback : function - callback_args : (any*) - callback_kwargs : { string : any } - help : string - metavar : string - """ - - # The list of instance attributes that may be set through - # keyword args to the constructor. - ATTRS = ['action', - 'type', - 'dest', - 'default', - 'nargs', - 'const', - 'choices', - 'callback', - 'callback_args', - 'callback_kwargs', - 'help', - 'metavar'] - - # The set of actions allowed by option parsers. Explicitly listed - # here so the constructor can validate its arguments. - ACTIONS = ("store", - "store_const", - "store_true", - "store_false", - "append", - "count", - "callback", - "help", - "version") - - # The set of actions that involve storing a value somewhere; - # also listed just for constructor argument validation. (If - # the action is one of these, there must be a destination.) - STORE_ACTIONS = ("store", - "store_const", - "store_true", - "store_false", - "append", - "count") - - # The set of actions for which it makes sense to supply a value - # type, ie. which may consume an argument from the command line. - TYPED_ACTIONS = ("store", - "append", - "callback") - - # The set of actions which *require* a value type, ie. that - # always consume an argument from the command line. - ALWAYS_TYPED_ACTIONS = ("store", - "append") - - # The set of known types for option parsers. Again, listed here for - # constructor argument validation. - TYPES = ("string", "int", "long", "float", "complex", "choice") - - # Dictionary of argument checking functions, which convert and - # validate option arguments according to the option type. - # - # Signature of checking functions is: - # check(option : Option, opt : string, value : string) -> any - # where - # option is the Option instance calling the checker - # opt is the actual option seen on the command-line - # (eg. "-a", "--file") - # value is the option argument seen on the command-line - # - # The return value should be in the appropriate Python type - # for option.type -- eg. an integer if option.type == "int". - # - # If no checker is defined for a type, arguments will be - # unchecked and remain strings. - TYPE_CHECKER = { "int" : check_builtin, - "long" : check_builtin, - "float" : check_builtin, - "complex": check_builtin, - "choice" : check_choice, - } - - - # CHECK_METHODS is a list of unbound method objects; they are called - # by the constructor, in order, after all attributes are - # initialized. The list is created and filled in later, after all - # the methods are actually defined. (I just put it here because I - # like to define and document all class attributes in the same - # place.) Subclasses that add another _check_*() method should - # define their own CHECK_METHODS list that adds their check method - # to those from this class. - CHECK_METHODS = None - - - # -- Constructor/initialization methods ---------------------------- - - def __init__(self, *opts, **attrs): - # Set _short_opts, _long_opts attrs from 'opts' tuple. - # Have to be set now, in case no option strings are supplied. - self._short_opts = [] - self._long_opts = [] - opts = self._check_opt_strings(opts) - self._set_opt_strings(opts) - - # Set all other attrs (action, type, etc.) from 'attrs' dict - self._set_attrs(attrs) - - # Check all the attributes we just set. There are lots of - # complicated interdependencies, but luckily they can be farmed - # out to the _check_*() methods listed in CHECK_METHODS -- which - # could be handy for subclasses! The one thing these all share - # is that they raise OptionError if they discover a problem. - for checker in self.CHECK_METHODS: - checker(self) - - def _check_opt_strings(self, opts): - # Filter out None because early versions of Optik had exactly - # one short option and one long option, either of which - # could be None. - opts = filter(None, opts) - if not opts: - raise TypeError("at least one option string must be supplied") - return opts - - def _set_opt_strings(self, opts): - for opt in opts: - if len(opt) < 2: - raise OptionError( - "invalid option string %r: " - "must be at least two characters long" % opt, self) - elif len(opt) == 2: - if not (opt[0] == "-" and opt[1] != "-"): - raise OptionError( - "invalid short option string %r: " - "must be of the form -x, (x any non-dash char)" % opt, - self) - self._short_opts.append(opt) - else: - if not (opt[0:2] == "--" and opt[2] != "-"): - raise OptionError( - "invalid long option string %r: " - "must start with --, followed by non-dash" % opt, - self) - self._long_opts.append(opt) - - def _set_attrs(self, attrs): - for attr in self.ATTRS: - if attrs.has_key(attr): - setattr(self, attr, attrs[attr]) - del attrs[attr] - else: - if attr == 'default': - setattr(self, attr, NO_DEFAULT) - else: - setattr(self, attr, None) - if attrs: - attrs = attrs.keys() - attrs.sort() - raise OptionError( - "invalid keyword arguments: %s" % ", ".join(attrs), - self) - - - # -- Constructor validation methods -------------------------------- - - def _check_action(self): - if self.action is None: - self.action = "store" - elif self.action not in self.ACTIONS: - raise OptionError("invalid action: %r" % self.action, self) - - def _check_type(self): - if self.type is None: - if self.action in self.ALWAYS_TYPED_ACTIONS: - if self.choices is not None: - # The "choices" attribute implies "choice" type. - self.type = "choice" - else: - # No type given? "string" is the most sensible default. - self.type = "string" - else: - # Allow type objects as an alternative to their names. - if type(self.type) is type: - self.type = self.type.__name__ - if self.type == "str": - self.type = "string" - - if self.type not in self.TYPES: - raise OptionError("invalid option type: %r" % self.type, self) - if self.action not in self.TYPED_ACTIONS: - raise OptionError( - "must not supply a type for action %r" % self.action, self) - - def _check_choice(self): - if self.type == "choice": - if self.choices is None: - raise OptionError( - "must supply a list of choices for type 'choice'", self) - elif type(self.choices) not in (types.TupleType, types.ListType): - raise OptionError( - "choices must be a list of strings ('%s' supplied)" - % str(type(self.choices)).split("'")[1], self) - elif self.choices is not None: - raise OptionError( - "must not supply choices for type %r" % self.type, self) - - def _check_dest(self): - # No destination given, and we need one for this action. The - # self.type check is for callbacks that take a value. - takes_value = (self.action in self.STORE_ACTIONS or - self.type is not None) - if self.dest is None and takes_value: - - # Glean a destination from the first long option string, - # or from the first short option string if no long options. - if self._long_opts: - # eg. "--foo-bar" -> "foo_bar" - self.dest = self._long_opts[0][2:].replace('-', '_') - else: - self.dest = self._short_opts[0][1] - - def _check_const(self): - if self.action != "store_const" and self.const is not None: - raise OptionError( - "'const' must not be supplied for action %r" % self.action, - self) - - def _check_nargs(self): - if self.action in self.TYPED_ACTIONS: - if self.nargs is None: - self.nargs = 1 - elif self.nargs is not None: - raise OptionError( - "'nargs' must not be supplied for action %r" % self.action, - self) - - def _check_callback(self): - if self.action == "callback": - if not callable(self.callback): - raise OptionError( - "callback not callable: %r" % self.callback, self) - if (self.callback_args is not None and - type(self.callback_args) is not types.TupleType): - raise OptionError( - "callback_args, if supplied, must be a tuple: not %r" - % self.callback_args, self) - if (self.callback_kwargs is not None and - type(self.callback_kwargs) is not types.DictType): - raise OptionError( - "callback_kwargs, if supplied, must be a dict: not %r" - % self.callback_kwargs, self) - else: - if self.callback is not None: - raise OptionError( - "callback supplied (%r) for non-callback option" - % self.callback, self) - if self.callback_args is not None: - raise OptionError( - "callback_args supplied for non-callback option", self) - if self.callback_kwargs is not None: - raise OptionError( - "callback_kwargs supplied for non-callback option", self) - - - CHECK_METHODS = [_check_action, - _check_type, - _check_choice, - _check_dest, - _check_const, - _check_nargs, - _check_callback] - - - # -- Miscellaneous methods ----------------------------------------- - - def __str__(self): - return "/".join(self._short_opts + self._long_opts) - - __repr__ = _repr - - def takes_value(self): - return self.type is not None - - def get_opt_string(self): - if self._long_opts: - return self._long_opts[0] - else: - return self._short_opts[0] - - - # -- Processing methods -------------------------------------------- - - def check_value(self, opt, value): - checker = self.TYPE_CHECKER.get(self.type) - if checker is None: - return value - else: - return checker(self, opt, value) - - def convert_value(self, opt, value): - if value is not None: - if self.nargs == 1: - return self.check_value(opt, value) - else: - return tuple([self.check_value(opt, v) for v in value]) - - def process(self, opt, value, values, parser): - - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - def take_action(self, action, dest, opt, value, values, parser): - if action == "store": - setattr(values, dest, value) - elif action == "store_const": - setattr(values, dest, self.const) - elif action == "store_true": - setattr(values, dest, True) - elif action == "store_false": - setattr(values, dest, False) - elif action == "append": - values.ensure_value(dest, []).append(value) - elif action == "count": - setattr(values, dest, values.ensure_value(dest, 0) + 1) - elif action == "callback": - args = self.callback_args or () - kwargs = self.callback_kwargs or {} - self.callback(self, opt, value, parser, *args, **kwargs) - elif action == "help": - parser.print_help() - parser.exit() - elif action == "version": - parser.print_version() - parser.exit() - else: - raise RuntimeError, "unknown action %r" % self.action - - return 1 - -# class Option - - -SUPPRESS_HELP = "SUPPRESS"+"HELP" -SUPPRESS_USAGE = "SUPPRESS"+"USAGE" - -# For compatibility with Python 2.2 -try: - True, False -except NameError: - (True, False) = (1, 0) -try: - basestring -except NameError: - basestring = (str, unicode) - - -class Values: - - def __init__(self, defaults=None): - if defaults: - for (attr, val) in defaults.items(): - setattr(self, attr, val) - - def __str__(self): - return str(self.__dict__) - - __repr__ = _repr - - def __eq__(self, other): - if isinstance(other, Values): - return self.__dict__ == other.__dict__ - elif isinstance(other, dict): - return self.__dict__ == other - else: - return False - - def __ne__(self, other): - return not (self == other) - - def _update_careful(self, dict): - """ - Update the option values from an arbitrary dictionary, but only - use keys from dict that already have a corresponding attribute - in self. Any keys in dict without a corresponding attribute - are silently ignored. - """ - for attr in dir(self): - if dict.has_key(attr): - dval = dict[attr] - if dval is not None: - setattr(self, attr, dval) - - def _update_loose(self, dict): - """ - Update the option values from an arbitrary dictionary, - using all keys from the dictionary regardless of whether - they have a corresponding attribute in self or not. - """ - self.__dict__.update(dict) - - def _update(self, dict, mode): - if mode == "careful": - self._update_careful(dict) - elif mode == "loose": - self._update_loose(dict) - else: - raise ValueError, "invalid update mode: %r" % mode - - def read_module(self, modname, mode="careful"): - __import__(modname) - mod = sys.modules[modname] - self._update(vars(mod), mode) - - def read_file(self, filename, mode="careful"): - vars = {} - execfile(filename, vars) - self._update(vars, mode) - - def ensure_value(self, attr, value): - if not hasattr(self, attr) or getattr(self, attr) is None: - setattr(self, attr, value) - return getattr(self, attr) - - -class OptionContainer: - - """ - Abstract base class. - - Class attributes: - standard_option_list : [Option] - list of standard options that will be accepted by all instances - of this parser class (intended to be overridden by subclasses). - - Instance attributes: - option_list : [Option] - the list of Option objects contained by this OptionContainer - _short_opt : { string : Option } - dictionary mapping short option strings, eg. "-f" or "-X", - to the Option instances that implement them. If an Option - has multiple short option strings, it will appears in this - dictionary multiple times. [1] - _long_opt : { string : Option } - dictionary mapping long option strings, eg. "--file" or - "--exclude", to the Option instances that implement them. - Again, a given Option can occur multiple times in this - dictionary. [1] - defaults : { string : any } - dictionary mapping option destination names to default - values for each destination [1] - - [1] These mappings are common to (shared by) all components of the - controlling OptionParser, where they are initially created. - - """ - - def __init__(self, option_class, conflict_handler, description): - # Initialize the option list and related data structures. - # This method must be provided by subclasses, and it must - # initialize at least the following instance attributes: - # option_list, _short_opt, _long_opt, defaults. - self._create_option_list() - - self.option_class = option_class - self.set_conflict_handler(conflict_handler) - self.set_description(description) - - def _create_option_mappings(self): - # For use by OptionParser constructor -- create the master - # option mappings used by this OptionParser and all - # OptionGroups that it owns. - self._short_opt = {} # single letter -> Option instance - self._long_opt = {} # long option -> Option instance - self.defaults = {} # maps option dest -> default value - - - def _share_option_mappings(self, parser): - # For use by OptionGroup constructor -- use shared option - # mappings from the OptionParser that owns this OptionGroup. - self._short_opt = parser._short_opt - self._long_opt = parser._long_opt - self.defaults = parser.defaults - - def set_conflict_handler(self, handler): - if handler not in ("error", "resolve"): - raise ValueError, "invalid conflict_resolution value %r" % handler - self.conflict_handler = handler - - def set_description(self, description): - self.description = description - - def get_description(self): - return self.description - - - # -- Option-adding methods ----------------------------------------- - - def _check_conflict(self, option): - conflict_opts = [] - for opt in option._short_opts: - if self._short_opt.has_key(opt): - conflict_opts.append((opt, self._short_opt[opt])) - for opt in option._long_opts: - if self._long_opt.has_key(opt): - conflict_opts.append((opt, self._long_opt[opt])) - - if conflict_opts: - handler = self.conflict_handler - if handler == "error": - raise OptionConflictError( - "conflicting option string(s): %s" - % ", ".join([co[0] for co in conflict_opts]), - option) - elif handler == "resolve": - for (opt, c_option) in conflict_opts: - if opt.startswith("--"): - c_option._long_opts.remove(opt) - del self._long_opt[opt] - else: - c_option._short_opts.remove(opt) - del self._short_opt[opt] - if not (c_option._short_opts or c_option._long_opts): - c_option.container.option_list.remove(c_option) - - def add_option(self, *args, **kwargs): - """add_option(Option) - add_option(opt_str, ..., kwarg=val, ...) - """ - if type(args[0]) is types.StringType: - option = self.option_class(*args, **kwargs) - elif len(args) == 1 and not kwargs: - option = args[0] - if not isinstance(option, Option): - raise TypeError, "not an Option instance: %r" % option - else: - raise TypeError, "invalid arguments" - - self._check_conflict(option) - - self.option_list.append(option) - option.container = self - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - if option.dest is not None: # option has a dest, we need a default - if option.default is not NO_DEFAULT: - self.defaults[option.dest] = option.default - elif not self.defaults.has_key(option.dest): - self.defaults[option.dest] = None - - return option - - def add_options(self, option_list): - for option in option_list: - self.add_option(option) - - # -- Option query/removal methods ---------------------------------- - - def get_option(self, opt_str): - return (self._short_opt.get(opt_str) or - self._long_opt.get(opt_str)) - - def has_option(self, opt_str): - return (self._short_opt.has_key(opt_str) or - self._long_opt.has_key(opt_str)) - - def remove_option(self, opt_str): - option = self._short_opt.get(opt_str) - if option is None: - option = self._long_opt.get(opt_str) - if option is None: - raise ValueError("no such option %r" % opt_str) - - for opt in option._short_opts: - del self._short_opt[opt] - for opt in option._long_opts: - del self._long_opt[opt] - option.container.option_list.remove(option) - - - # -- Help-formatting methods --------------------------------------- - - def format_option_help(self, formatter): - if not self.option_list: - return "" - result = [] - for option in self.option_list: - if not option.help is SUPPRESS_HELP: - result.append(formatter.format_option(option)) - return "".join(result) - - def format_description(self, formatter): - return formatter.format_description(self.get_description()) - - def format_help(self, formatter): - result = [] - if self.description: - result.append(self.format_description(formatter)) - if self.option_list: - result.append(self.format_option_help(formatter)) - return "\n".join(result) - - -class OptionGroup (OptionContainer): - - def __init__(self, parser, title, description=None): - self.parser = parser - OptionContainer.__init__( - self, parser.option_class, parser.conflict_handler, description) - self.title = title - - def _create_option_list(self): - self.option_list = [] - self._share_option_mappings(self.parser) - - def set_title(self, title): - self.title = title - - # -- Help-formatting methods --------------------------------------- - - def format_help(self, formatter): - result = formatter.format_heading(self.title) - formatter.indent() - result += OptionContainer.format_help(self, formatter) - formatter.dedent() - return result - - -class OptionParser (OptionContainer): - - """ - Class attributes: - standard_option_list : [Option] - list of standard options that will be accepted by all instances - of this parser class (intended to be overridden by subclasses). - - Instance attributes: - usage : string - a usage string for your program. Before it is displayed - to the user, "%prog" will be expanded to the name of - your program (self.prog or os.path.basename(sys.argv[0])). - prog : string - the name of the current program (to override - os.path.basename(sys.argv[0])). - - option_groups : [OptionGroup] - list of option groups in this parser (option groups are - irrelevant for parsing the command-line, but very useful - for generating help) - - allow_interspersed_args : bool = true - if true, positional arguments may be interspersed with options. - Assuming -a and -b each take a single argument, the command-line - -ablah foo bar -bboo baz - will be interpreted the same as - -ablah -bboo -- foo bar baz - If this flag were false, that command line would be interpreted as - -ablah -- foo bar -bboo baz - -- ie. we stop processing options as soon as we see the first - non-option argument. (This is the tradition followed by - Python's getopt module, Perl's Getopt::Std, and other argument- - parsing libraries, but it is generally annoying to users.) - - process_default_values : bool = true - if true, option default values are processed similarly to option - values from the command line: that is, they are passed to the - type-checking function for the option's type (as long as the - default value is a string). (This really only matters if you - have defined custom types; see SF bug #955889.) Set it to false - to restore the behaviour of Optik 1.4.1 and earlier. - - rargs : [string] - the argument list currently being parsed. Only set when - parse_args() is active, and continually trimmed down as - we consume arguments. Mainly there for the benefit of - callback options. - largs : [string] - the list of leftover arguments that we have skipped while - parsing options. If allow_interspersed_args is false, this - list is always empty. - values : Values - the set of option values currently being accumulated. Only - set when parse_args() is active. Also mainly for callbacks. - - Because of the 'rargs', 'largs', and 'values' attributes, - OptionParser is not thread-safe. If, for some perverse reason, you - need to parse command-line arguments simultaneously in different - threads, use different OptionParser instances. - - """ - - standard_option_list = [] - - def __init__(self, - usage=None, - option_list=None, - option_class=Option, - version=None, - conflict_handler="error", - description=None, - formatter=None, - add_help_option=True, - prog=None): - OptionContainer.__init__( - self, option_class, conflict_handler, description) - self.set_usage(usage) - self.prog = prog - self.version = version - self.allow_interspersed_args = True - self.process_default_values = True - if formatter is None: - formatter = IndentedHelpFormatter() - self.formatter = formatter - self.formatter.set_parser(self) - - # Populate the option list; initial sources are the - # standard_option_list class attribute, the 'option_list' - # argument, and (if applicable) the _add_version_option() and - # _add_help_option() methods. - self._populate_option_list(option_list, - add_help=add_help_option) - - self._init_parsing_state() - - # -- Private methods ----------------------------------------------- - # (used by our or OptionContainer's constructor) - - def _create_option_list(self): - self.option_list = [] - self.option_groups = [] - self._create_option_mappings() - - def _add_help_option(self): - self.add_option("-h", "--help", - action="help", - help=_("show this help message and exit")) - - def _add_version_option(self): - self.add_option("--version", - action="version", - help=_("show program's version number and exit")) - - def _populate_option_list(self, option_list, add_help=True): - if self.standard_option_list: - self.add_options(self.standard_option_list) - if option_list: - self.add_options(option_list) - if self.version: - self._add_version_option() - if add_help: - self._add_help_option() - - def _init_parsing_state(self): - # These are set in parse_args() for the convenience of callbacks. - self.rargs = None - self.largs = None - self.values = None - - - # -- Simple modifier methods --------------------------------------- - - def set_usage(self, usage): - if usage is None: - self.usage = _("%prog [options]") - elif usage is SUPPRESS_USAGE: - self.usage = None - # For backwards compatibility with Optik 1.3 and earlier. - elif usage.startswith("usage:" + " "): - self.usage = usage[7:] - else: - self.usage = usage - - def enable_interspersed_args(self): - self.allow_interspersed_args = True - - def disable_interspersed_args(self): - self.allow_interspersed_args = False - - def set_process_default_values(self, process): - self.process_default_values = process - - def set_default(self, dest, value): - self.defaults[dest] = value - - def set_defaults(self, **kwargs): - self.defaults.update(kwargs) - - def _get_all_options(self): - options = self.option_list[:] - for group in self.option_groups: - options.extend(group.option_list) - return options - - def get_default_values(self): - if not self.process_default_values: - # Old, pre-Optik 1.5 behaviour. - return Values(self.defaults) - - defaults = self.defaults.copy() - for option in self._get_all_options(): - default = defaults.get(option.dest) - if isinstance(default, basestring): - opt_str = option.get_opt_string() - defaults[option.dest] = option.check_value(opt_str, default) - - return Values(defaults) - - - # -- OptionGroup methods ------------------------------------------- - - def add_option_group(self, *args, **kwargs): - # XXX lots of overlap with OptionContainer.add_option() - if type(args[0]) is types.StringType: - group = OptionGroup(self, *args, **kwargs) - elif len(args) == 1 and not kwargs: - group = args[0] - if not isinstance(group, OptionGroup): - raise TypeError, "not an OptionGroup instance: %r" % group - if group.parser is not self: - raise ValueError, "invalid OptionGroup (wrong parser)" - else: - raise TypeError, "invalid arguments" - - self.option_groups.append(group) - return group - - def get_option_group(self, opt_str): - option = (self._short_opt.get(opt_str) or - self._long_opt.get(opt_str)) - if option and option.container is not self: - return option.container - return None - - - # -- Option-parsing methods ---------------------------------------- - - def _get_args(self, args): - if args is None: - return sys.argv[1:] - else: - return args[:] # don't modify caller's list - - def parse_args(self, args=None, values=None): - """ - parse_args(args : [string] = sys.argv[1:], - values : Values = None) - -> (values : Values, args : [string]) - - Parse the command-line options found in 'args' (default: - sys.argv[1:]). Any errors result in a call to 'error()', which - by default prints the usage message to stderr and calls - sys.exit() with an error message. On success returns a pair - (values, args) where 'values' is an Values instance (with all - your option values) and 'args' is the list of arguments left - over after parsing options. - """ - rargs = self._get_args(args) - if values is None: - values = self.get_default_values() - - # Store the halves of the argument list as attributes for the - # convenience of callbacks: - # rargs - # the rest of the command-line (the "r" stands for - # "remaining" or "right-hand") - # largs - # the leftover arguments -- ie. what's left after removing - # options and their arguments (the "l" stands for "leftover" - # or "left-hand") - self.rargs = rargs - self.largs = largs = [] - self.values = values - - try: - stop = self._process_args(largs, rargs, values) - except (BadOptionError, OptionValueError), err: - self.error(err.msg) - - args = largs + rargs - return self.check_values(values, args) - - def check_values(self, values, args): - """ - check_values(values : Values, args : [string]) - -> (values : Values, args : [string]) - - Check that the supplied option values and leftover arguments are - valid. Returns the option values and leftover arguments - (possibly adjusted, possibly completely new -- whatever you - like). Default implementation just returns the passed-in - values; subclasses may override as desired. - """ - return (values, args) - - def _process_args(self, largs, rargs, values): - """_process_args(largs : [string], - rargs : [string], - values : Values) - - Process command-line arguments and populate 'values', consuming - options and arguments from 'rargs'. If 'allow_interspersed_args' is - false, stop at the first non-option argument. If true, accumulate any - interspersed non-option arguments in 'largs'. - """ - while rargs: - arg = rargs[0] - # We handle bare "--" explicitly, and bare "-" is handled by the - # standard arg handler since the short arg case ensures that the - # len of the opt string is greater than 1. - if arg == "--": - del rargs[0] - return - elif arg[0:2] == "--": - # process a single long option (possibly with value(s)) - self._process_long_opt(rargs, values) - elif arg[:1] == "-" and len(arg) > 1: - # process a cluster of short options (possibly with - # value(s) for the last one only) - self._process_short_opts(rargs, values) - elif self.allow_interspersed_args: - largs.append(arg) - del rargs[0] - else: - return # stop now, leave this arg in rargs - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt(self, opt): - """_match_long_opt(opt : string) -> string - - Determine which long option string 'opt' matches, ie. which one - it is an unambiguous abbrevation for. Raises BadOptionError if - 'opt' doesn't unambiguously match any long option string. - """ - return _match_abbrev(opt, self._long_opt) - - def _process_long_opt(self, rargs, values): - arg = rargs.pop(0) - - # Value explicitly attached to arg? Pretend it's the next - # argument. - if "=" in arg: - (opt, next_arg) = arg.split("=", 1) - rargs.insert(0, next_arg) - had_explicit_value = True - else: - opt = arg - had_explicit_value = False - - opt = self._match_long_opt(opt) - option = self._long_opt[opt] - if option.takes_value(): - nargs = option.nargs - if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - elif had_explicit_value: - self.error(_("%s option does not take a value") % opt) - - else: - value = None - - option.process(opt, value, values, self) - - def _process_short_opts(self, rargs, values): - arg = rargs.pop(0) - stop = False - i = 1 - for ch in arg[1:]: - opt = "-" + ch - option = self._short_opt.get(opt) - i += 1 # we have consumed a character - - if not option: - self.error(_("no such option: %s") % opt) - if option.takes_value(): - # Any characters left in arg? Pretend they're the - # next arg, and stop consuming characters of arg. - if i < len(arg): - rargs.insert(0, arg[i:]) - stop = True - - nargs = option.nargs - if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - else: # option doesn't take a value - value = None - - option.process(opt, value, values, self) - - if stop: - break - - - # -- Feedback methods ---------------------------------------------- - - def get_prog_name(self): - if self.prog is None: - return os.path.basename(sys.argv[0]) - else: - return self.prog - - def expand_prog_name(self, s): - return s.replace("%prog", self.get_prog_name()) - - def get_description(self): - return self.expand_prog_name(self.description) - - def exit(self, status=0, msg=None): - if msg: - sys.stderr.write(msg) - sys.exit(status) - - def error(self, msg): - """error(msg : string) - - Print a usage message incorporating 'msg' to stderr and exit. - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(sys.stderr) - self.exit(2, "%s: error: %s\n" % (self.get_prog_name(), msg)) - - def get_usage(self): - if self.usage: - return self.formatter.format_usage( - self.expand_prog_name(self.usage)) - else: - return "" - - def print_usage(self, file=None): - """print_usage(file : file = stdout) - - Print the usage message for the current program (self.usage) to - 'file' (default stdout). Any occurence of the string "%prog" in - self.usage is replaced with the name of the current program - (basename of sys.argv[0]). Does nothing if self.usage is empty - or not defined. - """ - if self.usage: - print >>file, self.get_usage() - - def get_version(self): - if self.version: - return self.expand_prog_name(self.version) - else: - return "" - - def print_version(self, file=None): - """print_version(file : file = stdout) - - Print the version message for this program (self.version) to - 'file' (default stdout). As with print_usage(), any occurence - of "%prog" in self.version is replaced by the current program's - name. Does nothing if self.version is empty or undefined. - """ - if self.version: - print >>file, self.get_version() - - def format_option_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - formatter.store_option_strings(self) - result = [] - result.append(formatter.format_heading(_("options"))) - formatter.indent() - if self.option_list: - result.append(OptionContainer.format_option_help(self, formatter)) - result.append("\n") - for group in self.option_groups: - result.append(group.format_help(formatter)) - result.append("\n") - formatter.dedent() - # Drop the last "\n", or the header if no options or option groups: - return "".join(result[:-1]) - - def format_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - result = [] - if self.usage: - result.append(self.get_usage() + "\n") - if self.description: - result.append(self.format_description(formatter) + "\n") - result.append(self.format_option_help(formatter)) - return "".join(result) - - def print_help(self, file=None): - """print_help(file : file = stdout) - - Print an extended help message, listing all options and any - help text provided with them, to 'file' (default stdout). - """ - if file is None: - file = sys.stdout - file.write(self.format_help()) - -# class OptionParser - - -def _match_abbrev(s, wordmap): - """_match_abbrev(s : string, wordmap : {string : Option}) -> string - - Return the string key in 'wordmap' for which 's' is an unambiguous - abbreviation. If 's' is found to be ambiguous or doesn't match any of - 'words', raise BadOptionError. - """ - # Is there an exact match? - if wordmap.has_key(s): - return s - else: - # Isolate all words with s as a prefix. - possibilities = [word for word in wordmap.keys() - if word.startswith(s)] - # No exact match, so there had better be just one possibility. - if len(possibilities) == 1: - return possibilities[0] - elif not possibilities: - raise BadOptionError(_("no such option: %s") % s) - else: - # More than one possible completion: ambiguous prefix. - possibilities.sort() - raise BadOptionError(_("ambiguous option: %s (%s?)") - % (s, ", ".join(possibilities))) - - -# Some day, there might be many Option classes. As of Optik 1.3, the -# preferred way to instantiate Options is indirectly, via make_option(), -# which will become a factory function when there are many Option -# classes. -make_option = Option diff --git a/py/compat/subprocess.py b/py/compat/subprocess.py deleted file mode 100644 index e1b0c1f43..000000000 --- a/py/compat/subprocess.py +++ /dev/null @@ -1,1149 +0,0 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# This module should remain compatible with Python 2.2, see PEP 291. -# -# Copyright (c) 2003-2005 by Peter Astrand -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""subprocess - Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. This module -intends to replace several other, older modules and functions, like: - -os.system -os.spawn* -os.popen* -popen2.* -commands.* - -Information about how the subprocess module can be used to replace these -modules and functions can be found below. - - - -Using the subprocess module -=========================== -This module defines one class called Popen: - -class Popen(args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - - -Arguments are: - -args should be a string, or a sequence of program arguments. The -program to execute is normally the first item in the args sequence or -string, but can be explicitly set by using the executable argument. - -On UNIX, with shell=False (default): In this case, the Popen class -uses os.execvp() to execute the child program. args should normally -be a sequence. A string will be treated as a sequence with the string -as the only item (the program to execute). - -On UNIX, with shell=True: If args is a string, it specifies the -command string to execute through the shell. If args is a sequence, -the first item specifies the command string, and any additional items -will be treated as additional shell arguments. - -On Windows: the Popen class uses CreateProcess() to execute the child -program, which operates on strings. If args is a sequence, it will be -converted to a string using the list2cmdline method. Please note that -not all MS Windows applications interpret the command line the same -way: The list2cmdline is designed for applications using the same -rules as the MS C runtime. - -bufsize, if given, has the same meaning as the corresponding argument -to the built-in open() function: 0 means unbuffered, 1 means line -buffered, any other positive value means use a buffer of -(approximately) that size. A negative bufsize means to use the system -default, which usually means fully buffered. The default value for -bufsize is 0 (unbuffered). - -stdin, stdout and stderr specify the executed programs' standard -input, standard output and standard error file handles, respectively. -Valid values are PIPE, an existing file descriptor (a positive -integer), an existing file object, and None. PIPE indicates that a -new pipe to the child should be created. With None, no redirection -will occur; the child's file handles will be inherited from the -parent. Additionally, stderr can be STDOUT, which indicates that the -stderr data from the applications should be captured into the same -file handle as for stdout. - -If preexec_fn is set to a callable object, this object will be called -in the child process just before the child is executed. - -If close_fds is true, all file descriptors except 0, 1 and 2 will be -closed before the child process is executed. - -if shell is true, the specified command will be executed through the -shell. - -If cwd is not None, the current directory will be changed to cwd -before the child is executed. - -If env is not None, it defines the environment variables for the new -process. - -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Note: This feature is only -available if Python is built with universal newline support (the -default). Also, the newlines attribute of the file objects stdout, -stdin and stderr are not updated by the communicate() method. - -The startupinfo and creationflags, if given, will be passed to the -underlying CreateProcess() function. They can specify things such as -appearance of the main window and priority for the new process. -(Windows only) - - -This module also defines two shortcut functions: - -call(*args, **kwargs): - Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - - -Exceptions ----------- -Exceptions raised in the child process, before the new program has -started to execute, will be re-raised in the parent. Additionally, -the exception object will have one extra attribute called -'child_traceback', which is a string containing traceback information -from the childs point of view. - -The most common exception raised is OSError. This occurs, for -example, when trying to execute a non-existent file. Applications -should prepare for OSErrors. - -A ValueError will be raised if Popen is called with invalid arguments. - - -Security --------- -Unlike some other popen functions, this implementation will never call -/bin/sh implicitly. This means that all characters, including shell -metacharacters, can safely be passed to child processes. - - -Popen objects -============= -Instances of the Popen class have the following methods: - -poll() - Check if child process has terminated. Returns returncode - attribute. - -wait() - Wait for child process to terminate. Returns returncode attribute. - -communicate(input=None) - Interact with process: Send data to stdin. Read data from stdout - and stderr, until end-of-file is reached. Wait for process to - terminate. The optional stdin argument should be a string to be - sent to the child process, or None, if no data should be sent to - the child. - - communicate() returns a tuple (stdout, stderr). - - Note: The data read is buffered in memory, so do not use this - method if the data size is large or unlimited. - -The following attributes are also available: - -stdin - If the stdin argument is PIPE, this attribute is a file object - that provides input to the child process. Otherwise, it is None. - -stdout - If the stdout argument is PIPE, this attribute is a file object - that provides output from the child process. Otherwise, it is - None. - -stderr - If the stderr argument is PIPE, this attribute is file object that - provides error output from the child process. Otherwise, it is - None. - -pid - The process ID of the child process. - -returncode - The child return code. A None value indicates that the process - hasn't terminated yet. A negative value -N indicates that the - child was terminated by signal N (UNIX only). - - -Replacing older functions with the subprocess module -==================================================== -In this section, "a ==> b" means that b can be used as a replacement -for a. - -Note: All functions in this section fail (more or less) silently if -the executed program cannot be found; this module raises an OSError -exception. - -In the following examples, we assume that the subprocess module is -imported with "from subprocess import *". - - -Replacing /bin/sh shell backquote ---------------------------------- -output=`mycmd myarg` -==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] - - -Replacing shell pipe line -------------------------- -output=`dmesg | grep hda` -==> -p1 = Popen(["dmesg"], stdout=PIPE) -p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) -output = p2.communicate()[0] - - -Replacing os.system() ---------------------- -sts = os.system("mycmd" + " myarg") -==> -p = Popen("mycmd" + " myarg", shell=True) -sts = os.waitpid(p.pid, 0) - -Note: - -* Calling the program through the shell is usually not required. - -* It's easier to look at the returncode attribute than the - exitstatus. - -A more real-world example would look like this: - -try: - retcode = call("mycmd" + " myarg", shell=True) - if retcode < 0: - print >>sys.stderr, "Child was terminated by signal", -retcode - else: - print >>sys.stderr, "Child returned", retcode -except OSError, e: - print >>sys.stderr, "Execution failed:", e - - -Replacing os.spawn* -------------------- -P_NOWAIT example: - -pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") -==> -pid = Popen(["/bin/mycmd", "myarg"]).pid - - -P_WAIT example: - -retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") -==> -retcode = call(["/bin/mycmd", "myarg"]) - - -Vector example: - -os.spawnvp(os.P_NOWAIT, path, args) -==> -Popen([path] + args[1:]) - - -Environment example: - -os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) -==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) - - -Replacing os.popen* -------------------- -pipe = os.popen(cmd, mode='r', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout - -pipe = os.popen(cmd, mode='w', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin - - -(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdin, child_stdout) = (p.stdin, p.stdout) - - -(child_stdin, - child_stdout, - child_stderr) = os.popen3(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -(child_stdin, - child_stdout, - child_stderr) = (p.stdin, p.stdout, p.stderr) - - -(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) -(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) - - -Replacing popen2.* ------------------- -Note: If the cmd argument to popen2 functions is a string, the command -is executed through /bin/sh. If it is a list, the command is directly -executed. - -(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) -==> -p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - - -(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) -==> -p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - -The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, -except that: - -* subprocess.Popen raises an exception if the execution fails -* the capturestderr argument is replaced with the stderr argument. -* stdin=PIPE and stdout=PIPE must be specified. -* popen2 closes all filedescriptors by default, but you have to specify - close_fds=True with subprocess.Popen. - - -""" - -import sys -mswindows = (sys.platform == "win32") - -import os -import types -import traceback - -if mswindows: - import threading - import msvcrt - if 0: # <-- change this to use pywin32 instead of the _subprocess driver - import pywintypes - from win32api import GetStdHandle, STD_INPUT_HANDLE, \ - STD_OUTPUT_HANDLE, STD_ERROR_HANDLE - from win32api import GetCurrentProcess, DuplicateHandle, \ - GetModuleFileName, GetVersion - from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE - from win32pipe import CreatePipe - from win32process import CreateProcess, STARTUPINFO, \ - GetExitCodeProcess, STARTF_USESTDHANDLES, \ - STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE - from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 - else: - from _subprocess import * - class STARTUPINFO: - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes: - error = IOError -else: - import select - import errno - import fcntl - import pickle - -__all__ = ["Popen", "PIPE", "STDOUT", "call"] - -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except: - MAXFD = 256 - -# True/False does not exist on 2.2.0 -try: - False -except NameError: - False = 0 - True = 1 - -_active = [] - -def _cleanup(): - for inst in _active[:]: - inst.poll() - -PIPE = -1 -STDOUT = -2 - - -def call(*args, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - return Popen(*args, **kwargs).wait() - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp - result = [] - needquote = False - for arg in seq: - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backspaces. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backspaces, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return ''.join(result) - - -class Popen(object): - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - """Create new Popen instance.""" - _cleanup() - - if not isinstance(bufsize, (int, long)): - raise TypeError("bufsize must be an integer") - - if mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - if close_fds: - raise ValueError("close_fds is not supported on Windows " - "platforms") - else: - # POSIX - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.universal_newlines = universal_newlines - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are None when not using PIPEs. The child objects are None - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - self._execute_child(args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) - if c2pread: - if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) - else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) - if errread: - if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) - else: - self.stderr = os.fdopen(errread, 'rb', bufsize) - - _active.append(self) - - - def _translate_newlines(self, data): - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") - return data - - - if mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin == None and stdout == None and stderr == None: - return (None, None, None, None, None, None) - - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin == None: - p2cread = GetStdHandle(STD_INPUT_HANDLE) - elif stdin == PIPE: - p2cread, p2cwrite = CreatePipe(None, 0) - # Detach and turn into fd - p2cwrite = p2cwrite.Detach() - p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) - elif type(stdin) == types.IntType: - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout == None: - c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) - elif stdout == PIPE: - c2pread, c2pwrite = CreatePipe(None, 0) - # Detach and turn into fd - c2pread = c2pread.Detach() - c2pread = msvcrt.open_osfhandle(c2pread, 0) - elif type(stdout) == types.IntType: - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr == None: - errwrite = GetStdHandle(STD_ERROR_HANDLE) - elif stderr == PIPE: - errread, errwrite = CreatePipe(None, 0) - # Detach and turn into fd - errread = errread.Detach() - errread = msvcrt.open_osfhandle(errread, 0) - elif stderr == STDOUT: - errwrite = c2pwrite - elif type(stderr) == types.IntType: - errwrite = msvcrt.get_osfhandle(stderr) - else: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - return DuplicateHandle(GetCurrentProcess(), handle, - GetCurrentProcess(), 0, 1, - DUPLICATE_SAME_ACCESS) - - - def _find_w9xpopen(self): - """Find and return absolut path to w9xpopen.exe""" - w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - # Eeek - file-not-found - possibly an embedding - # situation - see if we can locate it in sys.exec_prefix - w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - raise RuntimeError("Cannot locate w9xpopen.exe, which is " - "needed for Popen to work with your " - "shell or platform.") - return w9xpopen - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (MS Windows version)""" - - if not isinstance(args, types.StringTypes): - args = list2cmdline(args) - - # Process startup details - if startupinfo == None: - startupinfo = STARTUPINFO() - if None not in (p2cread, c2pwrite, errwrite): - startupinfo.dwFlags |= STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - if shell: - startupinfo.dwFlags |= STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = comspec + " /c " + args - if (GetVersion() >= 0x80000000L or - os.path.basename(comspec).lower() == "command.com"): - # Win9x, or using command.com on NT. We need to - # use the w9xpopen intermediate program. For more - # information, see KB Q150956 - # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) - w9xpopen = self._find_w9xpopen() - args = '"%s" %s' % (w9xpopen, args) - # Not passing CREATE_NEW_CONSOLE has been known to - # cause random failures on win9x. Specifically a - # dialog: "Your program accessed mem currently in - # use at xxx" and a hopeful warning about the - # stability of your system. Cost is Ctrl+C wont - # kill children. - creationflags |= CREATE_NEW_CONSOLE - - # Start the process - try: - hp, ht, pid, tid = CreateProcess(executable, args, - # no special security - None, None, - # must inherit handles to pass std - # handles - 1, - creationflags, - env, - cwd, - startupinfo) - except pywintypes.error, e: - # Translate pywintypes.error to WindowsError, which is - # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but - # how can this be done from Python? - raise WindowsError(*e.args) - - # Retain the process handle, but close the thread handle - self._handle = hp - self.pid = pid - ht.Close() - - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. - if p2cread != None: - p2cread.Close() - if c2pwrite != None: - c2pwrite.Close() - if errwrite != None: - errwrite.Close() - - - def poll(self): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode == None: - if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: - self.returncode = GetExitCodeProcess(self._handle) - _active.remove(self) - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode == None: - obj = WaitForSingleObject(self._handle, INFINITE) - self.returncode = GetExitCodeProcess(self._handle) - _active.remove(self) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - stdout = None # Return - stderr = None # Return - - if self.stdout: - stdout = [] - stdout_thread = threading.Thread(target=self._readerthread, - args=(self.stdout, stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() - if self.stderr: - stderr = [] - stderr_thread = threading.Thread(target=self._readerthread, - args=(self.stderr, stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() - - if self.stdin: - if input != None: - self.stdin.write(input) - self.stdin.close() - - if self.stdout: - stdout_thread.join() - if self.stderr: - stderr_thread.join() - - # All data exchanged. Translate lists into strings. - if stdout != None: - stdout = stdout[0] - if stderr != None: - stderr = stderr[0] - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(open, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin == None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif type(stdin) == types.IntType: - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout == None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif type(stdout) == types.IntType: - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr == None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - errwrite = c2pwrite - elif type(stderr) == types.IntType: - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _set_cloexec_flag(self, fd): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - - old = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) - - - def _close_fds(self, but): - for i in xrange(3, MAXFD): - if i == but: - continue - try: - os.close(i) - except: - pass - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (POSIX version)""" - - if isinstance(args, types.StringTypes): - args = [args] - - if shell: - args = ["/bin/sh", "-c"] + args - - if executable == None: - executable = args[0] - - # For transferring possible exec failure from child to parent - # The first char specifies the exception type: 0 means - # OSError, 1 means some other error. - errpipe_read, errpipe_write = os.pipe() - self._set_cloexec_flag(errpipe_write) - - self.pid = os.fork() - if self.pid == 0: - # Child - try: - # Close parent's pipe ends - if p2cwrite: - os.close(p2cwrite) - if c2pread: - os.close(c2pread) - if errread: - os.close(errread) - os.close(errpipe_read) - - # Dup fds for child - if p2cread: - os.dup2(p2cread, 0) - if c2pwrite: - os.dup2(c2pwrite, 1) - if errwrite: - os.dup2(errwrite, 2) - - # Close pipe fds. Make sure we doesn't close the same - # fd more than once. - if p2cread: - os.close(p2cread) - if c2pwrite and c2pwrite not in (p2cread,): - os.close(c2pwrite) - if errwrite and errwrite not in (p2cread, c2pwrite): - os.close(errwrite) - - # Close all other fds, if asked for - if close_fds: - self._close_fds(but=errpipe_write) - - if cwd != None: - os.chdir(cwd) - - if preexec_fn: - apply(preexec_fn) - - if env == None: - os.execvp(executable, args) - else: - os.execvpe(executable, args, env) - - except: - exc_type, exc_value, tb = sys.exc_info() - # Save the traceback and attach it to the exception object - exc_lines = traceback.format_exception(exc_type, - exc_value, - tb) - exc_value.child_traceback = ''.join(exc_lines) - os.write(errpipe_write, pickle.dumps(exc_value)) - - # This exitcode won't be reported to applications, so it - # really doesn't matter what we return. - os._exit(255) - - # Parent - os.close(errpipe_write) - if p2cread and p2cwrite: - os.close(p2cread) - if c2pwrite and c2pread: - os.close(c2pwrite) - if errwrite and errread: - os.close(errwrite) - - # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB - os.close(errpipe_read) - if data != "": - os.waitpid(self.pid, 0) - child_exception = pickle.loads(data) - raise child_exception - - - def _handle_exitstatus(self, sts): - if os.WIFSIGNALED(sts): - self.returncode = -os.WTERMSIG(sts) - elif os.WIFEXITED(sts): - self.returncode = os.WEXITSTATUS(sts) - else: - # Should never happen - raise RuntimeError("Unknown child exit status!") - - _active.remove(self) - - - def poll(self): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode == None: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except os.error: - pass - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode == None: - pid, sts = os.waitpid(self.pid, 0) - self._handle_exitstatus(sts) - return self.returncode - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - read_set = [] - write_set = [] - stdout = None # Return - stderr = None # Return - - if self.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - self.stdin.flush() - if input: - write_set.append(self.stdin) - else: - self.stdin.close() - if self.stdout: - read_set.append(self.stdout) - stdout = [] - if self.stderr: - read_set.append(self.stderr) - stderr = [] - - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if self.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - bytes_written = os.write(self.stdin.fileno(), input[:512]) - input = input[bytes_written:] - if not input: - self.stdin.close() - write_set.remove(self.stdin) - - if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) - if data == "": - self.stdout.close() - read_set.remove(self.stdout) - stdout.append(data) - - if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) - if data == "": - self.stderr.close() - read_set.remove(self.stderr) - stderr.append(data) - - # All data exchanged. Translate lists into strings. - if stdout != None: - stdout = ''.join(stdout) - if stderr != None: - stderr = ''.join(stderr) - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(open, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - -def _demo_posix(): - # - # Example 1: Simple redirection: Get process list - # - plist = Popen(["ps"], stdout=PIPE).communicate()[0] - print "Process list:" - print plist - - # - # Example 2: Change uid before executing child - # - if os.getuid() == 0: - p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) - p.wait() - - # - # Example 3: Connecting several subprocesses - # - print "Looking for 'hda'..." - p1 = Popen(["dmesg"], stdout=PIPE) - p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 4: Catch execution error - # - print - print "Trying a weird file..." - try: - print Popen(["/this/path/does/not/exist"]).communicate() - except OSError, e: - if e.errno == errno.ENOENT: - print "The file didn't exist. I thought so..." - print "Child traceback:" - print e.child_traceback - else: - print "Error", e.errno - else: - print >>sys.stderr, "Gosh. No error." - - -def _demo_windows(): - # - # Example 1: Connecting several subprocesses - # - print "Looking for 'PROMPT' in set output..." - p1 = Popen("set", stdout=PIPE, shell=True) - p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 2: Simple execution of program - # - print "Executing calc..." - p = Popen("calc") - p.wait() - - -if __name__ == "__main__": - if mswindows: - _demo_windows() - else: - _demo_posix() diff --git a/py/compat/testing/test_deprecation.py b/py/compat/testing/test_deprecation.py new file mode 100644 index 000000000..62ca26aa4 --- /dev/null +++ b/py/compat/testing/test_deprecation.py @@ -0,0 +1,17 @@ + +def test_functional_deprecation(testdir): + testdir.makepyfile(""" + import py + def test_compat_deprecations(recwarn): + for name in 'subprocess optparse textwrap doctest'.split(): + check(recwarn, name) + def check(recwarn, name): + x = getattr(py.compat, name) + recwarn.pop(DeprecationWarning) + recwarn.clear() + assert x == getattr(py.std, name) + """) + result = testdir.runpytest() + assert result.ret == 0 + + diff --git a/py/compat/testing/test_doctest.py b/py/compat/testing/test_doctest.py deleted file mode 100644 index 008762cd2..000000000 --- a/py/compat/testing/test_doctest.py +++ /dev/null @@ -1,2394 +0,0 @@ -""" -Test script for doctest. -""" - -from test import test_support -import warnings - -import py -doctest = py.compat.doctest - -import sys -sys.modules['doctest'] = py.compat.doctest - - -###################################################################### -## Sample Objects (used by test cases) -###################################################################### - -def sample_func(v): - """ - Blah blah - - >>> print sample_func(22) - 44 - - Yee ha! - """ - return v+v - -class SampleClass: - """ - >>> print 1 - 1 - - >>> # comments get ignored. so are empty PS1 and PS2 prompts: - >>> - ... - - Multiline example: - >>> sc = SampleClass(3) - >>> for i in range(10): - ... sc = sc.double() - ... print sc.get(), - 6 12 24 48 96 192 384 768 1536 3072 - """ - def __init__(self, val): - """ - >>> print SampleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleClass(12).double().get() - 24 - """ - return SampleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleClass(-5).get() - -5 - """ - return self.val - - def a_staticmethod(v): - """ - >>> print SampleClass.a_staticmethod(10) - 11 - """ - return v+1 - a_staticmethod = staticmethod(a_staticmethod) - - def a_classmethod(cls, v): - """ - >>> print SampleClass.a_classmethod(10) - 12 - >>> print SampleClass(0).a_classmethod(10) - 12 - """ - return v+2 - a_classmethod = classmethod(a_classmethod) - - a_property = property(get, doc=""" - >>> print SampleClass(22).a_property - 22 - """) - - class NestedClass: - """ - >>> x = SampleClass.NestedClass(5) - >>> y = x.square() - >>> print y.get() - 25 - """ - def __init__(self, val=0): - """ - >>> print SampleClass.NestedClass().get() - 0 - """ - self.val = val - def square(self): - return SampleClass.NestedClass(self.val*self.val) - def get(self): - return self.val - -class SampleNewStyleClass(object): - r""" - >>> print '1\n2\n3' - 1 - 2 - 3 - """ - def __init__(self, val): - """ - >>> print SampleNewStyleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleNewStyleClass(12).double().get() - 24 - """ - return SampleNewStyleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleNewStyleClass(-5).get() - -5 - """ - return self.val - -###################################################################### -## Fake stdin (for testing interactive debugging) -###################################################################### - -class _FakeInput: - """ - A fake input stream for pdb's interactive debugger. Whenever a - line is read, print it (to simulate the user typing it), and then - return it. The set of lines to return is specified in the - constructor; they should not have trailing newlines. - """ - def __init__(self, lines): - self.lines = lines - - def readline(self): - line = self.lines.pop(0) - print line - return line+'\n' - -###################################################################### -## Test Cases -###################################################################### - -def test_Example(): r""" -Unit tests for the `Example` class. - -Example is a simple container class that holds: - - `source`: A source string. - - `want`: An expected output string. - - `exc_msg`: An expected exception message string (or None if no - exception is expected). - - `lineno`: A line number (within the docstring). - - `indent`: The example's indentation in the input string. - - `options`: An option dictionary, mapping option flags to True or - False. - -These attributes are set by the constructor. `source` and `want` are -required; the other attributes all have default values: - - >>> example = doctest.Example('print 1', '1\n') - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('print 1\n', '1\n', None, 0, 0, {}) - -The first three attributes (`source`, `want`, and `exc_msg`) may be -specified positionally; the remaining arguments should be specified as -keyword arguments: - - >>> exc_msg = 'IndexError: pop from an empty list' - >>> example = doctest.Example('[].pop()', '', exc_msg, - ... lineno=5, indent=4, - ... options={doctest.ELLIPSIS: True}) - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {8: True}) - -The constructor normalizes the `source` string to end in a newline: - - Source spans a single line: no terminating newline. - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1\n', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - Source spans multiple lines: require terminating newline. - >>> e = doctest.Example('print 1;\nprint 2\n', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - >>> e = doctest.Example('print 1;\nprint 2', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - Empty source string (which should never appear in real examples) - >>> e = doctest.Example('', '') - >>> e.source, e.want - ('\n', '') - -The constructor normalizes the `want` string to end in a newline, -unless it's the empty string: - - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1', '1') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print', '') - >>> e.source, e.want - ('print\n', '') - -The constructor normalizes the `exc_msg` string to end in a newline, -unless it's `None`: - - Message spans one line - >>> exc_msg = 'IndexError: pop from an empty list' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - >>> exc_msg = 'IndexError: pop from an empty list\n' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - Message spans multiple lines - >>> exc_msg = 'ValueError: 1\n 2' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - >>> exc_msg = 'ValueError: 1\n 2\n' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - Empty (but non-None) exception message (which should never appear - in real examples) - >>> exc_msg = '' - >>> e = doctest.Example('raise X()', '', exc_msg) - >>> e.exc_msg - '\n' -""" - -def test_DocTest(): r""" -Unit tests for the `DocTest` class. - -DocTest is a collection of examples, extracted from a docstring, along -with information about where the docstring comes from (a name, -filename, and line number). The docstring is parsed by the `DocTest` -constructor: - - >>> docstring = ''' - ... >>> print 12 - ... 12 - ... - ... Non-example text. - ... - ... >>> print 'another\example' - ... another - ... example - ... ''' - >>> globs = {} # globals to run the test in. - >>> parser = doctest.DocTestParser() - >>> test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_file', 20) - >>> print test - - >>> len(test.examples) - 2 - >>> e1, e2 = test.examples - >>> (e1.source, e1.want, e1.lineno) - ('print 12\n', '12\n', 1) - >>> (e2.source, e2.want, e2.lineno) - ("print 'another\\example'\n", 'another\nexample\n', 6) - -Source information (name, filename, and line number) is available as -attributes on the doctest object: - - >>> (test.name, test.filename, test.lineno) - ('some_test', 'some_file', 20) - -The line number of an example within its containing file is found by -adding the line number of the example and the line number of its -containing test: - - >>> test.lineno + e1.lineno - 21 - >>> test.lineno + e2.lineno - 26 - -If the docstring contains inconsistant leading whitespace in the -expected output of an example, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print 'bad\nindentation' - ... bad - ... indentation - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 4 of the docstring for some_test has inconsistent leading whitespace: 'indentation' - -If the docstring contains inconsistent leading whitespace on -continuation lines, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print ('bad indentation', - ... ... 2) - ... ('bad', 'indentation') - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test has inconsistent leading whitespace: '... 2)' - -If there's no blank space after a PS1 prompt ('>>>'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>>print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 1 of the docstring for some_test lacks blank after >>>: '>>>print 1' - -If there's no blank space after a PS2 prompt ('...'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>> if 1:\n...print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test lacks blank after ...: '...print 1' - -""" - -def test_DocTestFinder(): r""" -Unit tests for the `DocTestFinder` class. - -DocTestFinder is used to extract DocTests from an object's docstring -and the docstrings of its contained objects. It can be used with -modules, functions, classes, methods, staticmethods, classmethods, and -properties. - -Finding Tests in Functions -~~~~~~~~~~~~~~~~~~~~~~~~~~ -For a function whose docstring contains examples, DocTestFinder.find() -will return a single test (for that function's docstring): - - >>> finder = doctest.DocTestFinder() - -We'll simulate a __file__ attr that ends in pyc: - - >>> import test.test_doctest - >>> old = test.test_doctest.__file__ - >>> test.test_doctest.__file__ = 'test_doctest.pyc' - - >>> tests = finder.find(sample_func) - - >>> print tests # doctest: +ELLIPSIS - [] - -The exact name depends on how test_doctest was invoked, so allow for -leading path components. - - >>> tests[0].filename # doctest: +ELLIPSIS - '...test_doctest.py' - - >>> test.test_doctest.__file__ = old - - - >>> e = tests[0].examples[0] - >>> (e.source, e.want, e.lineno) - ('print sample_func(22)\n', '44\n', 3) - -By default, tests are created for objects with no docstring: - - >>> def no_docstring(v): - ... pass - >>> finder.find(no_docstring) - [] - -However, the optional argument `exclude_empty` to the DocTestFinder -constructor can be used to exclude tests for objects with empty -docstrings: - - >>> def no_docstring(v): - ... pass - >>> excl_empty_finder = doctest.DocTestFinder(exclude_empty=True) - >>> excl_empty_finder.find(no_docstring) - [] - -If the function has a docstring with no examples, then a test with no -examples is returned. (This lets `DocTestRunner` collect statistics -about which functions have no tests -- but is that useful? And should -an empty test also be created when there's no docstring?) - - >>> def no_examples(v): - ... ''' no doctest examples ''' - >>> finder.find(no_examples) # doctest: +ELLIPSIS - [] - -Finding Tests in Classes -~~~~~~~~~~~~~~~~~~~~~~~~ -For a class, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -methods, classmethods, staticmethods, properties, and nested classes. - - >>> finder = doctest.DocTestFinder() - >>> tests = finder.find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -New-style classes are also supported: - - >>> tests = finder.find(SampleNewStyleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 SampleNewStyleClass - 1 SampleNewStyleClass.__init__ - 1 SampleNewStyleClass.double - 1 SampleNewStyleClass.get - -Finding Tests in Modules -~~~~~~~~~~~~~~~~~~~~~~~~ -For a module, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -functions, classes, and the `__test__` dictionary, if it exists: - - >>> # A module - >>> import new - >>> m = new.module('some_module') - >>> def triple(val): - ... ''' - ... >>> print triple(11) - ... 33 - ... ''' - ... return val*3 - >>> m.__dict__.update({ - ... 'sample_func': sample_func, - ... 'SampleClass': SampleClass, - ... '__doc__': ''' - ... Module docstring. - ... >>> print 'module' - ... module - ... ''', - ... '__test__': { - ... 'd': '>>> print 6\n6\n>>> print 7\n7\n', - ... 'c': triple}}) - - >>> finder = doctest.DocTestFinder() - >>> # Use module=test.test_doctest, to prevent doctest from - >>> # ignoring the objects since they weren't defined in m. - >>> import test.test_doctest - >>> tests = finder.find(m, module=test.test_doctest) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 some_module - 3 some_module.SampleClass - 3 some_module.SampleClass.NestedClass - 1 some_module.SampleClass.NestedClass.__init__ - 1 some_module.SampleClass.__init__ - 2 some_module.SampleClass.a_classmethod - 1 some_module.SampleClass.a_property - 1 some_module.SampleClass.a_staticmethod - 1 some_module.SampleClass.double - 1 some_module.SampleClass.get - 1 some_module.__test__.c - 2 some_module.__test__.d - 1 some_module.sample_func - -Duplicate Removal -~~~~~~~~~~~~~~~~~ -If a single object is listed twice (under different names), then tests -will only be generated for it once: - - >>> from test import doctest_aliases - >>> tests = excl_empty_finder.find(doctest_aliases) - >>> print len(tests) - 2 - >>> print tests[0].name - test.doctest_aliases.TwoNames - - TwoNames.f and TwoNames.g are bound to the same object. - We can't guess which will be found in doctest's traversal of - TwoNames.__dict__ first, so we have to allow for either. - - >>> tests[1].name.split('.')[-1] in ['f', 'g'] - True - -Filter Functions -~~~~~~~~~~~~~~~~ -A filter function can be used to restrict which objects get examined, -but this is temporary, undocumented internal support for testmod's -deprecated isprivate gimmick. - - >>> def namefilter(prefix, base): - ... return base.startswith('a_') - >>> tests = doctest.DocTestFinder(_namefilter=namefilter).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 1 SampleClass.double - 1 SampleClass.get - -By default, that excluded objects with no doctests. exclude_empty=False -tells it to include (empty) tests for objects with no doctests. This feature -is really to support backward compatibility in what doctest.master.summarize() -displays. - - >>> tests = doctest.DocTestFinder(_namefilter=namefilter, - ... exclude_empty=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 0 SampleClass.NestedClass.get - 0 SampleClass.NestedClass.square - 1 SampleClass.__init__ - 1 SampleClass.double - 1 SampleClass.get - -If a given object is filtered out, then none of the objects that it -contains will be added either: - - >>> def namefilter(prefix, base): - ... return base == 'NestedClass' - >>> tests = doctest.DocTestFinder(_namefilter=namefilter).find(SampleClass) - >>> tests.sort() - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -The filter function apply to contained objects, and *not* to the -object explicitly passed to DocTestFinder: - - >>> def namefilter(prefix, base): - ... return base == 'SampleClass' - >>> tests = doctest.DocTestFinder(_namefilter=namefilter).find(SampleClass) - >>> len(tests) - 9 - -Turning off Recursion -~~~~~~~~~~~~~~~~~~~~~ -DocTestFinder can be told not to look for tests in contained objects -using the `recurse` flag: - - >>> tests = doctest.DocTestFinder(recurse=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - -Line numbers -~~~~~~~~~~~~ -DocTestFinder finds the line number of each example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... - ... some text - ... - ... >>> # examples are not created for comments & bare prompts. - ... >>> - ... ... - ... - ... >>> for x in range(10): - ... ... print x, - ... 0 1 2 3 4 5 6 7 8 9 - ... >>> x/2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> [e.lineno for e in test.examples] - [1, 9, 12] -""" - -def test_DocTestParser(): r""" -Unit tests for the `DocTestParser` class. - -DocTestParser is used to parse docstrings containing doctest examples. - -The `parse` method divides a docstring into examples and intervening -text: - - >>> s = ''' - ... >>> x, y = 2, 3 # no output expected - ... >>> if 1: - ... ... print x - ... ... print y - ... 2 - ... 3 - ... - ... Some text. - ... >>> x+y - ... 5 - ... ''' - >>> parser = doctest.DocTestParser() - >>> for piece in parser.parse(s): - ... if isinstance(piece, doctest.Example): - ... print 'Example:', (piece.source, piece.want, piece.lineno) - ... else: - ... print ' Text:', `piece` - Text: '\n' - Example: ('x, y = 2, 3 # no output expected\n', '', 1) - Text: '' - Example: ('if 1:\n print x\n print y\n', '2\n3\n', 2) - Text: '\nSome text.\n' - Example: ('x+y\n', '5\n', 9) - Text: '' - -The `get_examples` method returns just the examples: - - >>> for piece in parser.get_examples(s): - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) - -The `get_doctest` method creates a Test from the examples, along with the -given arguments: - - >>> test = parser.get_doctest(s, {}, 'name', 'filename', lineno=5) - >>> (test.name, test.filename, test.lineno) - ('name', 'filename', 5) - >>> for piece in test.examples: - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) -""" - -class test_DocTestRunner: - def basics(): r""" -Unit tests for the `DocTestRunner` class. - -DocTestRunner is used to run DocTest test cases, and to accumulate -statistics. Here's a simple DocTest case we can use: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x/2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - -The main DocTestRunner interface is the `run` method, which runs a -given DocTest case in a given namespace (globs). It returns a tuple -`(f,t)`, where `f` is the number of failed tests and `t` is the number -of tried tests. - - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 3) - -If any example produces incorrect output, then the test runner reports -the failure and proceeds to the next example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 14 - ... >>> x/2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=True).run(test) - ... # doctest: +ELLIPSIS - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 14 - ********************************************************************** - File ..., line 4, in f - Failed example: - print x - Expected: - 14 - Got: - 12 - Trying: - x/2 - Expecting: - 6 - ok - (1, 3) -""" - def verbose_flag(): r""" -The `verbose` flag makes the test runner generate more detailed -output: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x/2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - - >>> doctest.DocTestRunner(verbose=True).run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x/2 - Expecting: - 6 - ok - (0, 3) - -If the `verbose` flag is unspecified, then the output will be verbose -iff `-v` appears in sys.argv: - - >>> # Save the real sys.argv list. - >>> old_argv = sys.argv - - >>> # If -v does not appear in sys.argv, then output isn't verbose. - >>> sys.argv = ['test'] - >>> doctest.DocTestRunner().run(test) - (0, 3) - - >>> # If -v does appear in sys.argv, then output is verbose. - >>> sys.argv = ['test', '-v'] - >>> doctest.DocTestRunner().run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x/2 - Expecting: - 6 - ok - (0, 3) - - >>> # Restore sys.argv - >>> sys.argv = old_argv - -In the remaining examples, the test runner's verbosity will be -explicitly set, to ensure that the test behavior is consistent. - """ - def exceptions(): r""" -Tests of `DocTestRunner`'s exception handling. - -An expected exception is specified with a traceback message. The -lines between the first line and the type/value may be omitted or -replaced with any other string: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x/0 - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 2) - -An example may not generate output before it raises an exception; if -it does, then the traceback message will not be recognized as -signaling an expected exception, so the example will be reported as an -unexpected exception: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print 'pre-exception output', x/0 - ... pre-exception output - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 4, in f - Failed example: - print 'pre-exception output', x/0 - Exception raised: - ... - ZeroDivisionError: integer division or modulo by zero - (1, 2) - -Exception messages may contain newlines: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'multi\nline\nmessage' - ... Traceback (most recent call last): - ... ValueError: multi - ... line - ... message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - -If an exception is expected, but an exception with the wrong type or -message is raised, then it is reported as a failure: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' - Expected: - Traceback (most recent call last): - ValueError: wrong message - Got: - Traceback (most recent call last): - ... - ValueError: message - (1, 1) - -However, IGNORE_EXCEPTION_DETAIL can be used to allow a mismatch in the -detail: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - -But IGNORE_EXCEPTION_DETAIL does not allow a mismatch in the exception type: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... TypeError: wrong type - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - Expected: - Traceback (most recent call last): - TypeError: wrong type - Got: - Traceback (most recent call last): - ... - ValueError: message - (1, 1) - -If an exception is raised but not expected, then it is reported as an -unexpected exception: - - >>> def f(x): - ... r''' - ... >>> 1/0 - ... 0 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - 1/0 - Exception raised: - Traceback (most recent call last): - ... - ZeroDivisionError: integer division or modulo by zero - (1, 1) -""" - def optionflags(): r""" -Tests of `DocTestRunner`'s option flag handling. - -Several option flags can be used to customize the behavior of the test -runner. These are defined as module constants in doctest, and passed -to the DocTestRunner constructor (multiple constants should be or-ed -together). - -The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False -and 1/0: - - >>> def f(x): - ... '>>> True\n1\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1 - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - True - Expected: - 1 - Got: - True - (1, 1) - -The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines -and the '' marker: - - >>> def f(x): - ... '>>> print "a\\n\\nb"\na\n\nb\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_BLANKLINE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print "a\n\nb" - Expected: - a - - b - Got: - a - - b - (1, 1) - -The NORMALIZE_WHITESPACE flag causes all sequences of whitespace to be -treated as equal: - - >>> def f(x): - ... '>>> print 1, 2, 3\n 1 2\n 3' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print 1, 2, 3 - Expected: - 1 2 - 3 - Got: - 1 2 3 - (1, 1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.NORMALIZE_WHITESPACE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - (0, 1) - - An example from the docs: - >>> print range(20) #doctest: +NORMALIZE_WHITESPACE - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - -The ELLIPSIS flag causes ellipsis marker ("...") in the expected -output to match any substring in the actual output: - - >>> def f(x): - ... '>>> print range(15)\n[0, 1, 2, ..., 14]\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(15) - Expected: - [0, 1, 2, ..., 14] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - (1, 1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.ELLIPSIS - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - (0, 1) - - ... also matches nothing: - - >>> for i in range(100): - ... print i**2, #doctest: +ELLIPSIS - 0 1...4...9 16 ... 36 49 64 ... 9801 - - ... can be surprising; e.g., this test passes: - - >>> for i in range(21): #doctest: +ELLIPSIS - ... print i, - 0 1 2 ...1...2...0 - - Examples from the docs: - - >>> print range(20) # doctest:+ELLIPSIS - [0, 1, ..., 18, 19] - - >>> print range(20) # doctest: +ELLIPSIS - ... # doctest: +NORMALIZE_WHITESPACE - [0, 1, ..., 18, 19] - -The REPORT_UDIFF flag causes failures that involve multi-line expected -and actual outputs to be displayed using a unified diff: - - >>> def f(x): - ... r''' - ... >>> print '\n'.join('abcdefg') - ... a - ... B - ... c - ... d - ... f - ... g - ... h - ... ''' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print '\n'.join('abcdefg') - Expected: - a - B - c - d - f - g - h - Got: - a - b - c - d - e - f - g - (1, 1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_UDIFF - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print '\n'.join('abcdefg') - Differences (unified diff with -expected +actual): - @@ -1,7 +1,7 @@ - a - -B - +b - c - d - +e - f - g - -h - (1, 1) - -The REPORT_CDIFF flag causes failures that involve multi-line expected -and actual outputs to be displayed using a context diff: - - >>> # Reuse f() from the REPORT_UDIFF example, above. - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_CDIFF - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print '\n'.join('abcdefg') - Differences (context diff with expected followed by actual): - *************** - *** 1,7 **** - a - ! B - c - d - f - g - - h - --- 1,7 ---- - a - ! b - c - d - + e - f - g - (1, 1) - - -The REPORT_NDIFF flag causes failures to use the difflib.Differ algorithm -used by the popular ndiff.py utility. This does intraline difference -marking, as well as interline differences. - - >>> def f(x): - ... r''' - ... >>> print "a b c d e f g h i j k l m" - ... a b c d e f g h i j k 1 m - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_NDIFF - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print "a b c d e f g h i j k l m" - Differences (ndiff with -expected +actual): - - a b c d e f g h i j k 1 m - ? ^ - + a b c d e f g h i j k l m - ? + ++ ^ - (1, 1) - -The REPORT_ONLY_FIRST_FAILURE supresses result output after the first -failing example: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> print 2 # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - (3, 5) - -However, output from `report_start` is not supressed: - - >>> doctest.DocTestRunner(verbose=True, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - Trying: - print 1 # first success - Expecting: - 1 - ok - Trying: - print 2 # first failure - Expecting: - 200 - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - (3, 5) - -For the purposes of REPORT_ONLY_FIRST_FAILURE, unexpected exceptions -count as failures: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> raise ValueError(2) # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - raise ValueError(2) # first failure - Exception raised: - ... - ValueError: 2 - (3, 5) - -New option flags can also be registered, via register_optionflag(). Here -we reach into doctest's internals a bit. - - >>> unlikely = "UNLIKELY_OPTION_NAME" - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - False - >>> new_flag_value = doctest.register_optionflag(unlikely) - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - True - -Before 2.4.4/2.5, registering a name more than once erroneously created -more than one flag value. Here we verify that's fixed: - - >>> redundant_flag_value = doctest.register_optionflag(unlikely) - >>> redundant_flag_value == new_flag_value - True - -Clean up. - >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] - - """ - - def option_directives(): r""" -Tests of `DocTestRunner`'s option directive mechanism. - -Option directives can be used to turn option flags on or off for a -single example. To turn an option on for an example, follow that -example with a comment of the form ``# doctest: +OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) # should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (1, 2) - -To turn an option off for an example, follow that example with a -comment of the form ``# doctest: -OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) - ... [0, 1, ..., 9] - ... - ... >>> # should fail: no ellipsis - ... >>> print range(10) # doctest: -ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False, - ... optionflags=doctest.ELLIPSIS).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 6, in f - Failed example: - print range(10) # doctest: -ELLIPSIS - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (1, 2) - -Option directives affect only the example that they appear with; they -do not change the options for surrounding examples: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - ********************************************************************** - File ..., line 8, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (2, 3) - -Multiple options may be modified by a single option directive. They -may be separated by whitespace, commas, or both: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (1, 2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS,+NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (1, 2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - (1, 2) - -The option directive may be put on the line following the source, as -long as a continuation prompt is used: - - >>> def f(x): r''' - ... >>> print range(10) - ... ... # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - -For examples with multi-line source, the option directive may appear -at the end of any line: - - >>> def f(x): r''' - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, - ... 0 1 2 ... 9 - ... - ... >>> for x in range(10): - ... ... print x, # doctest: +ELLIPSIS - ... 0 1 2 ... 9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 2) - -If more than one line of an example with multi-line source has an -option directive, then they are combined: - - >>> def f(x): r''' - ... Should fail (option directive not on the last line): - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, # doctest: +NORMALIZE_WHITESPACE - ... 0 1 2...9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - (0, 1) - -It is an error to have a comment of the form ``# doctest:`` that is -*not* followed by words of the form ``+OPTION`` or ``-OPTION``, where -``OPTION`` is an option that has been registered with -`register_option`: - - >>> # Error: Option not registered - >>> s = '>>> print 12 #doctest: +BADOPTION' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: '+BADOPTION' - - >>> # Error: No + or - prefix - >>> s = '>>> print 12 #doctest: ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: 'ELLIPSIS' - -It is an error to use an option directive on a line that contains no -source: - - >>> s = '>>> # doctest: +ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' -""" - -def test_testsource(): r""" -Unit tests for `testsource()`. - -The testsource() function takes a module and a name, finds the (first) -test with that name in that module, and converts it to a script. The -example code is converted to regular Python code. The surrounding -words and expected output are converted to comments: - - >>> import test.test_doctest - >>> name = 'test.test_doctest.sample_func' - >>> print doctest.testsource(test.test_doctest, name) - # Blah blah - # - print sample_func(22) - # Expected: - ## 44 - # - # Yee ha! - - - >>> name = 'test.test_doctest.SampleNewStyleClass' - >>> print doctest.testsource(test.test_doctest, name) - print '1\n2\n3' - # Expected: - ## 1 - ## 2 - ## 3 - - - >>> name = 'test.test_doctest.SampleClass.a_classmethod' - >>> print doctest.testsource(test.test_doctest, name) - print SampleClass.a_classmethod(10) - # Expected: - ## 12 - print SampleClass(0).a_classmethod(10) - # Expected: - ## 12 - -""" - -def test_debug(): r""" - -Create a docstring that we want to debug: - - >>> s = ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... ''' - -Create some fake stdin input, to feed to the debugger: - - >>> import tempfile - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput(['next', 'print x', 'continue']) - -Run the debugger on the docstring, and then restore sys.stdin. - - >>> try: doctest.debug_src(s) - ... finally: sys.stdin = real_stdin - > (1)?() - (Pdb) next - 12 - --Return-- - > (1)?()->None - (Pdb) print x - 12 - (Pdb) continue - -""" - -def test_pdb_set_trace(): - """Using pdb.set_trace from a doctest. - - You can use pdb.set_trace from a doctest. To do so, you must - retrieve the set_trace function from the pdb module at the time - you use it. The doctest module changes sys.stdout so that it can - capture program output. It also temporarily replaces pdb.set_trace - with a version that restores stdout. This is necessary for you to - see debugger output. - - >>> doc = ''' - ... >>> x = 42 - ... >>> import pdb; pdb.set_trace() - ... ''' - >>> parser = doctest.DocTestParser() - >>> test = parser.get_doctest(doc, {}, "foo", "foo.py", 0) - >>> runner = doctest.DocTestRunner(verbose=False) - - To demonstrate this, we'll create a fake standard input that - captures our debugger input: - - >>> import tempfile - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'print x', # print data defined by the example - ... 'continue', # stop debugging - ... '']) - - >>> try: runner.run(test) - ... finally: sys.stdin = real_stdin - --Return-- - > (1)?()->None - -> import pdb; pdb.set_trace() - (Pdb) print x - 42 - (Pdb) continue - (0, 2) - - You can also put pdb.set_trace in a function called from a test: - - >>> def calls_set_trace(): - ... y=2 - ... import pdb; pdb.set_trace() - - >>> doc = ''' - ... >>> x=1 - ... >>> calls_set_trace() - ... ''' - >>> test = parser.get_doctest(doc, globals(), "foo", "foo.py", 0) - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'print y', # print data defined in the function - ... 'up', # out of function - ... 'print x', # print data defined by the example - ... 'continue', # stop debugging - ... '']) - - >>> try: - ... runner.run(test) - ... finally: - ... sys.stdin = real_stdin - --Return-- - > (3)calls_set_trace()->None - -> import pdb; pdb.set_trace() - (Pdb) print y - 2 - (Pdb) up - > (1)?() - -> calls_set_trace() - (Pdb) print x - 1 - (Pdb) continue - (0, 2) - - During interactive debugging, source code is shown, even for - doctest examples: - - >>> doc = ''' - ... >>> def f(x): - ... ... g(x*2) - ... >>> def g(x): - ... ... print x+3 - ... ... import pdb; pdb.set_trace() - ... >>> f(3) - ... ''' - >>> test = parser.get_doctest(doc, globals(), "foo", "foo.py", 0) - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'list', # list source from example 2 - ... 'next', # return from g() - ... 'list', # list source from example 1 - ... 'next', # return from f() - ... 'list', # list source from example 3 - ... 'continue', # stop debugging - ... '']) - >>> try: runner.run(test) - ... finally: sys.stdin = real_stdin - ... # doctest: +NORMALIZE_WHITESPACE - --Return-- - > (3)g()->None - -> import pdb; pdb.set_trace() - (Pdb) list - 1 def g(x): - 2 print x+3 - 3 -> import pdb; pdb.set_trace() - [EOF] - (Pdb) next - --Return-- - > (2)f()->None - -> g(x*2) - (Pdb) list - 1 def f(x): - 2 -> g(x*2) - [EOF] - (Pdb) next - --Return-- - > (1)?()->None - -> f(3) - (Pdb) list - 1 -> f(3) - [EOF] - (Pdb) continue - ********************************************************************** - File "foo.py", line 7, in foo - Failed example: - f(3) - Expected nothing - Got: - 9 - (1, 3) - """ - -def test_pdb_set_trace_nested(): - """This illustrates more-demanding use of set_trace with nested functions. - - >>> class C(object): - ... def calls_set_trace(self): - ... y = 1 - ... import pdb; pdb.set_trace() - ... self.f1() - ... y = 2 - ... def f1(self): - ... x = 1 - ... self.f2() - ... x = 2 - ... def f2(self): - ... z = 1 - ... z = 2 - - >>> calls_set_trace = C().calls_set_trace - - >>> doc = ''' - ... >>> a = 1 - ... >>> calls_set_trace() - ... ''' - >>> parser = doctest.DocTestParser() - >>> runner = doctest.DocTestRunner(verbose=False) - >>> test = parser.get_doctest(doc, globals(), "foo", "foo.py", 0) - >>> real_stdin = sys.stdin - >>> sys.stdin = _FakeInput([ - ... 'print y', # print data defined in the function - ... 'step', 'step', 'step', 'step', 'step', 'step', 'print z', - ... 'up', 'print x', - ... 'up', 'print y', - ... 'up', 'print foo', - ... 'continue', # stop debugging - ... '']) - - >>> try: - ... runner.run(test) - ... finally: - ... sys.stdin = real_stdin - > (5)calls_set_trace() - -> self.f1() - (Pdb) print y - 1 - (Pdb) step - --Call-- - > (7)f1() - -> def f1(self): - (Pdb) step - > (8)f1() - -> x = 1 - (Pdb) step - > (9)f1() - -> self.f2() - (Pdb) step - --Call-- - > (11)f2() - -> def f2(self): - (Pdb) step - > (12)f2() - -> z = 1 - (Pdb) step - > (13)f2() - -> z = 2 - (Pdb) print z - 1 - (Pdb) up - > (9)f1() - -> self.f2() - (Pdb) print x - 1 - (Pdb) up - > (5)calls_set_trace() - -> self.f1() - (Pdb) print y - 1 - (Pdb) up - > (1)?() - -> calls_set_trace() - (Pdb) print foo - *** NameError: name 'foo' is not defined - (Pdb) continue - (0, 2) -""" - -def test_DocTestSuite(): - """DocTestSuite creates a unittest test suite from a doctest. - - We create a Suite by providing a module. A module can be provided - by passing a module object: - - >>> import unittest - >>> import test.sample_doctest - >>> suite = doctest.DocTestSuite(test.sample_doctest) - >>> suite.run(unittest.TestResult()) - - - We can also supply the module by name: - - >>> suite = doctest.DocTestSuite('test.sample_doctest') - >>> suite.run(unittest.TestResult()) - - - We can use the current module: - - >>> suite = test.sample_doctest.test_suite() - >>> suite.run(unittest.TestResult()) - - - We can supply global variables. If we pass globs, they will be - used instead of the module globals. Here we'll pass an empty - globals, triggering an extra error: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', globs={}) - >>> suite.run(unittest.TestResult()) - - - Alternatively, we can provide extra globals. Here we'll make an - error go away by providing an extra global variable: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... extraglobs={'y': 1}) - >>> suite.run(unittest.TestResult()) - - - You can pass option flags. Here we'll cause an extra error - by disabling the blank-line feature: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> suite.run(unittest.TestResult()) - - - You can supply setUp and tearDown functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. Here - we'll use the setUp function to supply the missing variable y: - - >>> def setUp(test): - ... test.globs['y'] = 1 - - >>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals, which are a copy of the - sample_doctest module dictionary. The test globals are - automatically cleared for us after a test. - - Finally, you can provide an alternate test finder. Here we'll - use a custom test_finder to to run just the test named bar. - However, the test in the module docstring, and the two tests - in the module __test__ dict, aren't filtered, so we actually - run three tests besides bar's. The filtering mechanisms are - poorly conceived, and will go away someday. - - >>> finder = doctest.DocTestFinder( - ... _namefilter=lambda prefix, base: base!='bar') - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... test_finder=finder) - >>> suite.run(unittest.TestResult()) - - """ - -def test_DocFileSuite(): - """We can test tests found in text files using a DocFileSuite. - - We create a suite by providing the names of one or more text - files that include examples: - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt') - >>> suite.run(unittest.TestResult()) - - - The test files are looked for in the directory containing the - calling module. A package keyword argument can be provided to - specify a different relative location. - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... package='test') - >>> suite.run(unittest.TestResult()) - - - '/' should be used as a path separator. It will be converted - to a native separator at run time: - - >>> suite = doctest.DocFileSuite('../test/test_doctest.txt') - >>> suite.run(unittest.TestResult()) - - - If DocFileSuite is used from an interactive session, then files - are resolved relative to the directory of sys.argv[0]: - - >>> import new, os.path, test.test_doctest - >>> save_argv = sys.argv - >>> sys.argv = [test.test_doctest.__file__] - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... package=new.module('__main__')) - >>> sys.argv = save_argv - - 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__) - >>> test_pkg_path = os.path.split(test_doctest_path)[0] - - >>> # 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, module_relative=False) - >>> suite.run(unittest.TestResult()) - - - 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', - ... 'test_doctest2.txt', - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - In this case, we supplied a missing favorite color. You can - provide doctest options: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - And, you can provide setUp and tearDown functions: - - You can supply setUp and teatDoen functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. - Here, we'll use a setUp function to set the favorite color in - test_doctest.txt: - - >>> def setUp(test): - ... test.globs['favorite_color'] = 'blue' - - >>> suite = doctest.DocFileSuite('test_doctest.txt', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals. The test globals are - automatically cleared for us after a test. - - """ - -def test_trailing_space_in_test(): - """ - Trailing spaces in expected output are significant: - - >>> x, y = 'foo', '' - >>> print x, y - foo \n - """ - - -def test_unittest_reportflags(): - """Default unittest reporting flags can be set to control reporting - - Here, we'll set the REPORT_ONLY_FIRST_FAILURE option so we see - only the first failure of each test. First, we'll look at the - output without the flag. The file test_doctest.txt file has two - tests. They both fail if blank lines are disabled: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> import unittest - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - ... - - Note that we see both failures displayed. - - >>> old = doctest.set_unittest_reportflags( - ... doctest.REPORT_ONLY_FIRST_FAILURE) - - Now, when we run the test: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - - - - We get only the first failure. - - If we give any reporting options when we set up the tests, - however: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE | doctest.REPORT_NDIFF) - - Then the default eporting options are ignored: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - print 'a' - print - print 'b' - Differences (ndiff with -expected +actual): - a - - - + - b - - - - - Test runners can restore the formatting flags after they run: - - >>> ignored = doctest.set_unittest_reportflags(old) - - """ - -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 - - 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. - -# Ignore all warnings about the use of class Tester in this module. -# Note that the name of this module may differ depending on how it's -# imported, so the use of __name__ is important. -warnings.filterwarnings("ignore", "class Tester", DeprecationWarning, - __name__, 0) - -def old_test1(): r""" ->>> from doctest import Tester ->>> t = Tester(globs={'x': 42}, verbose=0) ->>> t.runstring(r''' -... >>> x = x * 2 -... >>> print x -... 42 -... ''', 'XYZ') -********************************************************************** -Line 3, in XYZ -Failed example: - print x -Expected: - 42 -Got: - 84 -(1, 2) ->>> t.runstring(">>> x = x * 2\n>>> print x\n84\n", 'example2') -(0, 2) ->>> t.summarize() -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -***Test Failed*** 1 failures. -(1, 4) ->>> t.summarize(verbose=1) -1 items passed all tests: - 2 tests in example2 -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -4 tests in 2 items. -3 passed and 1 failed. -***Test Failed*** 1 failures. -(1, 4) -""" - -def old_test2(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=1) - >>> test = r''' - ... # just an example - ... >>> x = 1 + 2 - ... >>> x - ... 3 - ... ''' - >>> t.runstring(test, "Example") - Running string Example - Trying: - x = 1 + 2 - Expecting nothing - ok - Trying: - x - Expecting: - 3 - ok - 0 of 2 examples failed in string Example - (0, 2) -""" - -def old_test3(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> def _f(): - ... '''Trivial docstring example. - ... >>> assert 2 == 2 - ... ''' - ... return 32 - ... - >>> t.rundoc(_f) # expect 0 failures in 1 example - (0, 1) -""" - -def old_test4(): """ - >>> import new - >>> m1 = new.module('_m1') - >>> m2 = new.module('_m2') - >>> test_data = \""" - ... def _f(): - ... '''>>> assert 1 == 1 - ... ''' - ... def g(): - ... '''>>> assert 2 != 1 - ... ''' - ... class H: - ... '''>>> assert 2 > 1 - ... ''' - ... def bar(self): - ... '''>>> assert 1 < 2 - ... ''' - ... \""" - >>> exec test_data in m1.__dict__ - >>> exec test_data in m2.__dict__ - >>> m1.__dict__.update({"f2": m2._f, "g2": m2.g, "h2": m2.H}) - - Tests that objects outside m1 are excluded: - - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test", m1) # f2 and g2 and h2 skipped - (0, 4) - - Once more, not excluding stuff outside m1: - - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test_pvt") # None are skipped. - (0, 8) - - The exclusion of objects from outside the designated module is - meant to be invoked automagically by testmod. - - >>> doctest.testmod(m1, verbose=False) - (0, 4) -""" - -###################################################################### -## Main -###################################################################### - -def test_main(): - # Check the doctest cases in doctest itself: - test_support.run_doctest(doctest, verbosity=True) - # Check the doctest cases defined here: - from test import test_doctest - test_support.run_doctest(test_doctest, verbosity=True) - -import trace, sys, re, StringIO -def test_coverage(coverdir): - tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], - trace=0, count=1) - tracer.run('reload(doctest); test_main()') - r = tracer.results() - print 'Writing coverage results...' - r.write_results(show_missing=True, summary=True, - coverdir=coverdir) - -if __name__ == '__main__': - if '-c' in sys.argv: - test_coverage('/tmp/doctest.cover') - else: - test_main() diff --git a/py/compat/testing/test_doctest.txt b/py/compat/testing/test_doctest.txt deleted file mode 100644 index f8e851e1a..000000000 --- a/py/compat/testing/test_doctest.txt +++ /dev/null @@ -1,17 +0,0 @@ -This is a sample doctest in a text file. - -In this example, we'll rely on a global variable being set for us -already: - - >>> favorite_color - 'blue' - -We can make this fail by disabling the blank-line feature. - - >>> if 1: - ... print 'a' - ... print - ... print 'b' - a - - b diff --git a/py/compat/testing/test_doctest2.py b/py/compat/testing/test_doctest2.py deleted file mode 100644 index c009ab7c2..000000000 --- a/py/compat/testing/test_doctest2.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -u"""A module to test whether doctest recognizes some 2.2 features, -like static and class methods. - ->>> print 'yup' # 1 -yup - -We include some (random) encoded (utf-8) text in the text surrounding -the example. It should be ignored: - -ЉЊЈЁЂ - -""" - -from test import test_support - -class C(object): - u"""Class C. - - >>> print C() # 2 - 42 - - - We include some (random) encoded (utf-8) text in the text surrounding - the example. It should be ignored: - - ЉЊЈЁЂ - - """ - - def __init__(self): - """C.__init__. - - >>> print C() # 3 - 42 - """ - - def __str__(self): - """ - >>> print C() # 4 - 42 - """ - return "42" - - class D(object): - """A nested D class. - - >>> print "In D!" # 5 - In D! - """ - - def nested(self): - """ - >>> print 3 # 6 - 3 - """ - - def getx(self): - """ - >>> c = C() # 7 - >>> c.x = 12 # 8 - >>> print c.x # 9 - -12 - """ - return -self._x - - def setx(self, value): - """ - >>> c = C() # 10 - >>> c.x = 12 # 11 - >>> print c.x # 12 - -12 - """ - self._x = value - - x = property(getx, setx, doc="""\ - >>> c = C() # 13 - >>> c.x = 12 # 14 - >>> print c.x # 15 - -12 - """) - - def statm(): - """ - A static method. - - >>> print C.statm() # 16 - 666 - >>> print C().statm() # 17 - 666 - """ - return 666 - - statm = staticmethod(statm) - - def clsm(cls, val): - """ - A class method. - - >>> print C.clsm(22) # 18 - 22 - >>> print C().clsm(23) # 19 - 23 - """ - return val - - clsm = classmethod(clsm) - -def test_main(): - from py.__.compat.testing import test_doctest2 - EXPECTED = 19 - f, t = test_support.run_doctest(test_doctest2) - if t != EXPECTED: - raise test_support.TestFailed("expected %d tests to run, not %d" % - (EXPECTED, t)) - -# Pollute the namespace with a bunch of imported functions and classes, -# to make sure they don't get tested. -from py.compat.doctest import * - -if __name__ == '__main__': - test_main() diff --git a/py/compat/testing/test_doctest2.txt b/py/compat/testing/test_doctest2.txt deleted file mode 100644 index 2e14856c2..000000000 --- a/py/compat/testing/test_doctest2.txt +++ /dev/null @@ -1,14 +0,0 @@ -This is a sample doctest in a text file. - -In this example, we'll rely on some silly setup: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - True - -This test also has some (random) encoded (utf-8) unicode text: - - ЉЊЈЁЂ - -This doesn't cause a problem in the tect surrounding the examples, but -we include it here (in this test text file) to make sure. :) diff --git a/py/compat/testing/test_optparse.py b/py/compat/testing/test_optparse.py deleted file mode 100644 index 9443630b9..000000000 --- a/py/compat/testing/test_optparse.py +++ /dev/null @@ -1,1503 +0,0 @@ -#!/usr/bin/env python - -# -# Test suite for Optik. Supplied by Johannes Gijsbers -# (taradino@softhome.net) -- translated from the original Optik -# test suite to this PyUnit-based version. -# -# $Id: test_optparse.py 46506 2006-05-28 18:15:43Z armin.rigo $ -# - -import sys -import os -import copy -import unittest - -from cStringIO import StringIO -from pprint import pprint -from test import test_support - -import py -optparse = py.compat.optparse -import sys -sys.modules['optparse'] = optparse - -from optparse import make_option, Option, IndentedHelpFormatter, \ - TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, \ - SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, \ - BadOptionError, OptionValueError, Values, _match_abbrev - -# Do the right thing with boolean values for all known Python versions. -try: - True, False -except NameError: - (True, False) = (1, 0) - - -class InterceptedError(Exception): - def __init__(self, - error_message=None, - exit_status=None, - exit_message=None): - self.error_message = error_message - self.exit_status = exit_status - self.exit_message = exit_message - - def __str__(self): - return self.error_message or self.exit_message or "intercepted error" - -class InterceptingOptionParser(OptionParser): - def exit(self, status=0, msg=None): - raise InterceptedError(exit_status=status, exit_message=msg) - - def error(self, msg): - raise InterceptedError(error_message=msg) - - -class BaseTest(unittest.TestCase): - def assertParseOK(self, args, expected_opts, expected_positional_args): - """Assert the options are what we expected when parsing arguments. - - Otherwise, fail with a nicely formatted message. - - Keyword arguments: - args -- A list of arguments to parse with OptionParser. - expected_opts -- The options expected. - expected_positional_args -- The positional arguments expected. - - Returns the options and positional args for further testing. - """ - - (options, positional_args) = self.parser.parse_args(args) - optdict = vars(options) - - self.assertEqual(optdict, expected_opts, - """ -Options are %(optdict)s. -Should be %(expected_opts)s. -Args were %(args)s.""" % locals()) - - self.assertEqual(positional_args, expected_positional_args, - """ -Positional arguments are %(positional_args)s. -Should be %(expected_positional_args)s. -Args were %(args)s.""" % locals ()) - - return (options, positional_args) - - def assertRaises(self, - func, - args, - kwargs, - expected_exception, - expected_message): - """ - Assert that the expected exception is raised when calling a - function, and that the right error message is included with - that exception. - - Arguments: - func -- the function to call - args -- positional arguments to `func` - kwargs -- keyword arguments to `func` - expected_exception -- exception that should be raised - expected_output -- output we expect to see - - Returns the exception raised for further testing. - """ - if args is None: - args = () - if kwargs is None: - kwargs = {} - - try: - func(*args, **kwargs) - except expected_exception, err: - actual_message = str(err) - self.assertEqual(actual_message, - expected_message, - """\ -expected exception message: -'''%(expected_message)s''' -actual exception message: -'''%(actual_message)s''' -""" % locals()) - - return err - else: - self.fail("""expected exception %(expected_exception)s not raised -called %(func)r -with args %(args)r -and kwargs %(kwargs)r -""" % locals ()) - - - # -- Assertions used in more than one class -------------------- - - def assertParseFail(self, cmdline_args, expected_output): - """ - Assert the parser fails with the expected message. Caller - must ensure that self.parser is an InterceptingOptionParser. - """ - try: - self.parser.parse_args(cmdline_args) - except InterceptedError, err: - self.assertEqual(err.error_message, expected_output) - else: - self.assertFalse("expected parse failure") - - def assertOutput(self, - cmdline_args, - expected_output, - expected_status=0, - expected_error=None): - """Assert the parser prints the expected output on stdout.""" - save_stdout = sys.stdout - try: - try: - sys.stdout = StringIO() - self.parser.parse_args(cmdline_args) - finally: - output = sys.stdout.getvalue() - sys.stdout = save_stdout - - except InterceptedError, err: - self.assertEqual(output, expected_output) - self.assertEqual(err.exit_status, expected_status) - self.assertEqual(err.exit_message, expected_error) - else: - self.assertFalse("expected parser.exit()") - - def assertTypeError(self, func, expected_message, *args): - """Assert that TypeError is raised when executing func.""" - self.assertRaises(func, args, None, TypeError, expected_message) - - def assertHelp(self, parser, expected_help): - actual_help = parser.format_help() - if actual_help != expected_help: - raise self.failureException( - 'help text failure; expected:\n"' + - expected_help + '"; got:\n"' + - actual_help + '"\n') - -# -- Test make_option() aka Option ------------------------------------- - -# It's not necessary to test correct options here. All the tests in the -# parser.parse_args() section deal with those, because they're needed -# there. - -class TestOptionChecks(BaseTest): - def setUp(self): - self.parser = OptionParser(usage=SUPPRESS_USAGE) - - def assertOptionError(self, expected_message, args=[], kwargs={}): - self.assertRaises(make_option, args, kwargs, - OptionError, expected_message) - - def test_opt_string_empty(self): - self.assertTypeError(make_option, - "at least one option string must be supplied") - - def test_opt_string_too_short(self): - self.assertOptionError( - "invalid option string 'b': must be at least two characters long", - ["b"]) - - def test_opt_string_short_invalid(self): - self.assertOptionError( - "invalid short option string '--': must be " - "of the form -x, (x any non-dash char)", - ["--"]) - - def test_opt_string_long_invalid(self): - self.assertOptionError( - "invalid long option string '---': " - "must start with --, followed by non-dash", - ["---"]) - - def test_attr_invalid(self): - self.assertOptionError( - "option -b: invalid keyword arguments: bar, foo", - ["-b"], {'foo': None, 'bar': None}) - - def test_action_invalid(self): - self.assertOptionError( - "option -b: invalid action: 'foo'", - ["-b"], {'action': 'foo'}) - - def test_type_invalid(self): - self.assertOptionError( - "option -b: invalid option type: 'foo'", - ["-b"], {'type': 'foo'}) - self.assertOptionError( - "option -b: invalid option type: 'tuple'", - ["-b"], {'type': tuple}) - - def test_no_type_for_action(self): - self.assertOptionError( - "option -b: must not supply a type for action 'count'", - ["-b"], {'action': 'count', 'type': 'int'}) - - def test_no_choices_list(self): - self.assertOptionError( - "option -b/--bad: must supply a list of " - "choices for type 'choice'", - ["-b", "--bad"], {'type': "choice"}) - - def test_bad_choices_list(self): - typename = type('').__name__ - self.assertOptionError( - "option -b/--bad: choices must be a list of " - "strings ('%s' supplied)" % typename, - ["-b", "--bad"], - {'type': "choice", 'choices':"bad choices"}) - - def test_no_choices_for_type(self): - self.assertOptionError( - "option -b: must not supply choices for type 'int'", - ["-b"], {'type': 'int', 'choices':"bad"}) - - def test_no_const_for_action(self): - self.assertOptionError( - "option -b: 'const' must not be supplied for action 'store'", - ["-b"], {'action': 'store', 'const': 1}) - - def test_no_nargs_for_action(self): - self.assertOptionError( - "option -b: 'nargs' must not be supplied for action 'count'", - ["-b"], {'action': 'count', 'nargs': 2}) - - def test_callback_not_callable(self): - self.assertOptionError( - "option -b: callback not callable: 'foo'", - ["-b"], {'action': 'callback', - 'callback': 'foo'}) - - def dummy(self): - pass - - def test_callback_args_no_tuple(self): - self.assertOptionError( - "option -b: callback_args, if supplied, " - "must be a tuple: not 'foo'", - ["-b"], {'action': 'callback', - 'callback': self.dummy, - 'callback_args': 'foo'}) - - def test_callback_kwargs_no_dict(self): - self.assertOptionError( - "option -b: callback_kwargs, if supplied, " - "must be a dict: not 'foo'", - ["-b"], {'action': 'callback', - 'callback': self.dummy, - 'callback_kwargs': 'foo'}) - - def test_no_callback_for_action(self): - self.assertOptionError( - "option -b: callback supplied ('foo') for non-callback option", - ["-b"], {'action': 'store', - 'callback': 'foo'}) - - def test_no_callback_args_for_action(self): - self.assertOptionError( - "option -b: callback_args supplied for non-callback option", - ["-b"], {'action': 'store', - 'callback_args': 'foo'}) - - def test_no_callback_kwargs_for_action(self): - self.assertOptionError( - "option -b: callback_kwargs supplied for non-callback option", - ["-b"], {'action': 'store', - 'callback_kwargs': 'foo'}) - -class TestOptionParser(BaseTest): - def setUp(self): - self.parser = OptionParser() - self.parser.add_option("-v", "--verbose", "-n", "--noisy", - action="store_true", dest="verbose") - self.parser.add_option("-q", "--quiet", "--silent", - action="store_false", dest="verbose") - - def test_add_option_no_Option(self): - self.assertTypeError(self.parser.add_option, - "not an Option instance: None", None) - - def test_add_option_invalid_arguments(self): - self.assertTypeError(self.parser.add_option, - "invalid arguments", None, None) - - def test_get_option(self): - opt1 = self.parser.get_option("-v") - self.assert_(isinstance(opt1, Option)) - self.assertEqual(opt1._short_opts, ["-v", "-n"]) - self.assertEqual(opt1._long_opts, ["--verbose", "--noisy"]) - self.assertEqual(opt1.action, "store_true") - self.assertEqual(opt1.dest, "verbose") - - def test_get_option_equals(self): - opt1 = self.parser.get_option("-v") - opt2 = self.parser.get_option("--verbose") - opt3 = self.parser.get_option("-n") - opt4 = self.parser.get_option("--noisy") - self.assert_(opt1 is opt2 is opt3 is opt4) - - def test_has_option(self): - self.assert_(self.parser.has_option("-v")) - self.assert_(self.parser.has_option("--verbose")) - - def assert_removed(self): - self.assert_(self.parser.get_option("-v") is None) - self.assert_(self.parser.get_option("--verbose") is None) - self.assert_(self.parser.get_option("-n") is None) - self.assert_(self.parser.get_option("--noisy") is None) - - self.failIf(self.parser.has_option("-v")) - self.failIf(self.parser.has_option("--verbose")) - self.failIf(self.parser.has_option("-n")) - self.failIf(self.parser.has_option("--noisy")) - - self.assert_(self.parser.has_option("-q")) - self.assert_(self.parser.has_option("--silent")) - - def test_remove_short_opt(self): - self.parser.remove_option("-n") - self.assert_removed() - - def test_remove_long_opt(self): - self.parser.remove_option("--verbose") - self.assert_removed() - - def test_remove_nonexistent(self): - self.assertRaises(self.parser.remove_option, ('foo',), None, - ValueError, "no such option 'foo'") - -class TestOptionValues(BaseTest): - def setUp(self): - pass - - def test_basics(self): - values = Values() - self.assertEqual(vars(values), {}) - self.assertEqual(values, {}) - self.assertNotEqual(values, {"foo": "bar"}) - self.assertNotEqual(values, "") - - dict = {"foo": "bar", "baz": 42} - values = Values(defaults=dict) - self.assertEqual(vars(values), dict) - self.assertEqual(values, dict) - self.assertNotEqual(values, {"foo": "bar"}) - self.assertNotEqual(values, {}) - self.assertNotEqual(values, "") - self.assertNotEqual(values, []) - - -class TestTypeAliases(BaseTest): - def setUp(self): - self.parser = OptionParser() - - def test_type_aliases(self): - self.parser.add_option("-x", type=int) - self.parser.add_option("-s", type=str) - self.parser.add_option("-t", type="str") - self.assertEquals(self.parser.get_option("-x").type, "int") - self.assertEquals(self.parser.get_option("-s").type, "string") - self.assertEquals(self.parser.get_option("-t").type, "string") - - -# Custom type for testing processing of default values. -_time_units = { 's' : 1, 'm' : 60, 'h' : 60*60, 'd' : 60*60*24 } - -def _check_duration(option, opt, value): - try: - if value[-1].isdigit(): - return int(value) - else: - return int(value[:-1]) * _time_units[value[-1]] - except ValueError, IndexError: - raise OptionValueError( - 'option %s: invalid duration: %r' % (opt, value)) - -class DurationOption(Option): - TYPES = Option.TYPES + ('duration',) - TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) - TYPE_CHECKER['duration'] = _check_duration - -class TestDefaultValues(BaseTest): - def setUp(self): - self.parser = OptionParser() - self.parser.add_option("-v", "--verbose", default=True) - self.parser.add_option("-q", "--quiet", dest='verbose') - self.parser.add_option("-n", type="int", default=37) - self.parser.add_option("-m", type="int") - self.parser.add_option("-s", default="foo") - self.parser.add_option("-t") - self.parser.add_option("-u", default=None) - self.expected = { 'verbose': True, - 'n': 37, - 'm': None, - 's': "foo", - 't': None, - 'u': None } - - def test_basic_defaults(self): - self.assertEqual(self.parser.get_default_values(), self.expected) - - def test_mixed_defaults_post(self): - self.parser.set_defaults(n=42, m=-100) - self.expected.update({'n': 42, 'm': -100}) - self.assertEqual(self.parser.get_default_values(), self.expected) - - def test_mixed_defaults_pre(self): - self.parser.set_defaults(x="barf", y="blah") - self.parser.add_option("-x", default="frob") - self.parser.add_option("-y") - - self.expected.update({'x': "frob", 'y': "blah"}) - self.assertEqual(self.parser.get_default_values(), self.expected) - - self.parser.remove_option("-y") - self.parser.add_option("-y", default=None) - self.expected.update({'y': None}) - self.assertEqual(self.parser.get_default_values(), self.expected) - - def test_process_default(self): - self.parser.option_class = DurationOption - self.parser.add_option("-d", type="duration", default=300) - self.parser.add_option("-e", type="duration", default="6m") - self.parser.set_defaults(n="42") - self.expected.update({'d': 300, 'e': 360, 'n': 42}) - self.assertEqual(self.parser.get_default_values(), self.expected) - - self.parser.set_process_default_values(False) - self.expected.update({'d': 300, 'e': "6m", 'n': "42"}) - self.assertEqual(self.parser.get_default_values(), self.expected) - - -class TestProgName(BaseTest): - """ - Test that %prog expands to the right thing in usage, version, - and help strings. - """ - - def assertUsage(self, parser, expected_usage): - self.assertEqual(parser.get_usage(), expected_usage) - - def assertVersion(self, parser, expected_version): - self.assertEqual(parser.get_version(), expected_version) - - - def test_default_progname(self): - # Make sure that program name taken from sys.argv[0] by default. - save_argv = sys.argv[:] - try: - sys.argv[0] = os.path.join("foo", "bar", "baz.py") - parser = OptionParser("usage: %prog ...", version="%prog 1.2") - expected_usage = "usage: baz.py ...\n" - self.assertUsage(parser, expected_usage) - self.assertVersion(parser, "baz.py 1.2") - self.assertHelp(parser, - expected_usage + "\n" + - "options:\n" - " --version show program's version number and exit\n" - " -h, --help show this help message and exit\n") - finally: - sys.argv[:] = save_argv - - def test_custom_progname(self): - parser = OptionParser(prog="thingy", - version="%prog 0.1", - usage="%prog arg arg") - parser.remove_option("-h") - parser.remove_option("--version") - expected_usage = "usage: thingy arg arg\n" - self.assertUsage(parser, expected_usage) - self.assertVersion(parser, "thingy 0.1") - self.assertHelp(parser, expected_usage + "\n") - - -class TestExpandDefaults(BaseTest): - def setUp(self): - self.parser = OptionParser(prog="test") - self.help_prefix = """\ -usage: test [options] - -options: - -h, --help show this help message and exit -""" - self.file_help = "read from FILE [default: %default]" - self.expected_help_file = self.help_prefix + \ - " -f FILE, --file=FILE read from FILE [default: foo.txt]\n" - self.expected_help_none = self.help_prefix + \ - " -f FILE, --file=FILE read from FILE [default: none]\n" - - def test_option_default(self): - self.parser.add_option("-f", "--file", - default="foo.txt", - help=self.file_help) - self.assertHelp(self.parser, self.expected_help_file) - - def test_parser_default_1(self): - self.parser.add_option("-f", "--file", - help=self.file_help) - self.parser.set_default('file', "foo.txt") - self.assertHelp(self.parser, self.expected_help_file) - - def test_parser_default_2(self): - self.parser.add_option("-f", "--file", - help=self.file_help) - self.parser.set_defaults(file="foo.txt") - self.assertHelp(self.parser, self.expected_help_file) - - def test_no_default(self): - self.parser.add_option("-f", "--file", - help=self.file_help) - self.assertHelp(self.parser, self.expected_help_none) - - def test_default_none_1(self): - self.parser.add_option("-f", "--file", - default=None, - help=self.file_help) - self.assertHelp(self.parser, self.expected_help_none) - - def test_default_none_2(self): - self.parser.add_option("-f", "--file", - help=self.file_help) - self.parser.set_defaults(file=None) - self.assertHelp(self.parser, self.expected_help_none) - - def test_float_default(self): - self.parser.add_option( - "-p", "--prob", - help="blow up with probability PROB [default: %default]") - self.parser.set_defaults(prob=0.43) - expected_help = self.help_prefix + \ - " -p PROB, --prob=PROB blow up with probability PROB [default: 0.43]\n" - self.assertHelp(self.parser, expected_help) - - def test_alt_expand(self): - self.parser.add_option("-f", "--file", - default="foo.txt", - help="read from FILE [default: *DEFAULT*]") - self.parser.formatter.default_tag = "*DEFAULT*" - self.assertHelp(self.parser, self.expected_help_file) - - def test_no_expand(self): - self.parser.add_option("-f", "--file", - default="foo.txt", - help="read from %default file") - self.parser.formatter.default_tag = None - expected_help = self.help_prefix + \ - " -f FILE, --file=FILE read from %default file\n" - self.assertHelp(self.parser, expected_help) - - -# -- Test parser.parse_args() ------------------------------------------ - -class TestStandard(BaseTest): - def setUp(self): - options = [make_option("-a", type="string"), - make_option("-b", "--boo", type="int", dest='boo'), - make_option("--foo", action="append")] - - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, - option_list=options) - - def test_required_value(self): - self.assertParseFail(["-a"], "-a option requires an argument") - - def test_invalid_integer(self): - self.assertParseFail(["-b", "5x"], - "option -b: invalid integer value: '5x'") - - def test_no_such_option(self): - self.assertParseFail(["--boo13"], "no such option: --boo13") - - def test_long_invalid_integer(self): - self.assertParseFail(["--boo=x5"], - "option --boo: invalid integer value: 'x5'") - - def test_empty(self): - self.assertParseOK([], {'a': None, 'boo': None, 'foo': None}, []) - - def test_shortopt_empty_longopt_append(self): - self.assertParseOK(["-a", "", "--foo=blah", "--foo="], - {'a': "", 'boo': None, 'foo': ["blah", ""]}, - []) - - def test_long_option_append(self): - self.assertParseOK(["--foo", "bar", "--foo", "", "--foo=x"], - {'a': None, - 'boo': None, - 'foo': ["bar", "", "x"]}, - []) - - def test_option_argument_joined(self): - self.assertParseOK(["-abc"], - {'a': "bc", 'boo': None, 'foo': None}, - []) - - def test_option_argument_split(self): - self.assertParseOK(["-a", "34"], - {'a': "34", 'boo': None, 'foo': None}, - []) - - def test_option_argument_joined_integer(self): - self.assertParseOK(["-b34"], - {'a': None, 'boo': 34, 'foo': None}, - []) - - def test_option_argument_split_negative_integer(self): - self.assertParseOK(["-b", "-5"], - {'a': None, 'boo': -5, 'foo': None}, - []) - - def test_long_option_argument_joined(self): - self.assertParseOK(["--boo=13"], - {'a': None, 'boo': 13, 'foo': None}, - []) - - def test_long_option_argument_split(self): - self.assertParseOK(["--boo", "111"], - {'a': None, 'boo': 111, 'foo': None}, - []) - - def test_long_option_short_option(self): - self.assertParseOK(["--foo=bar", "-axyz"], - {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, - []) - - def test_abbrev_long_option(self): - self.assertParseOK(["--f=bar", "-axyz"], - {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, - []) - - def test_defaults(self): - (options, args) = self.parser.parse_args([]) - defaults = self.parser.get_default_values() - self.assertEqual(vars(defaults), vars(options)) - - def test_ambiguous_option(self): - self.parser.add_option("--foz", action="store", - type="string", dest="foo") - self.assertParseFail(["--f=bar"], - "ambiguous option: --f (--foo, --foz?)") - - - def test_short_and_long_option_split(self): - self.assertParseOK(["-a", "xyz", "--foo", "bar"], - {'a': 'xyz', 'boo': None, 'foo': ["bar"]}, - []), - - def test_short_option_split_long_option_append(self): - self.assertParseOK(["--foo=bar", "-b", "123", "--foo", "baz"], - {'a': None, 'boo': 123, 'foo': ["bar", "baz"]}, - []) - - def test_short_option_split_one_positional_arg(self): - self.assertParseOK(["-a", "foo", "bar"], - {'a': "foo", 'boo': None, 'foo': None}, - ["bar"]), - - def test_short_option_consumes_separator(self): - self.assertParseOK(["-a", "--", "foo", "bar"], - {'a': "--", 'boo': None, 'foo': None}, - ["foo", "bar"]), - - def test_short_option_joined_and_separator(self): - self.assertParseOK(["-ab", "--", "--foo", "bar"], - {'a': "b", 'boo': None, 'foo': None}, - ["--foo", "bar"]), - - def test_invalid_option_becomes_positional_arg(self): - self.assertParseOK(["-ab", "-", "--foo", "bar"], - {'a': "b", 'boo': None, 'foo': ["bar"]}, - ["-"]) - - def test_no_append_versus_append(self): - self.assertParseOK(["-b3", "-b", "5", "--foo=bar", "--foo", "baz"], - {'a': None, 'boo': 5, 'foo': ["bar", "baz"]}, - []) - - def test_option_consumes_optionlike_string(self): - self.assertParseOK(["-a", "-b3"], - {'a': "-b3", 'boo': None, 'foo': None}, - []) - -class TestBool(BaseTest): - def setUp(self): - options = [make_option("-v", - "--verbose", - action="store_true", - dest="verbose", - default=''), - make_option("-q", - "--quiet", - action="store_false", - dest="verbose")] - self.parser = OptionParser(option_list = options) - - def test_bool_default(self): - self.assertParseOK([], - {'verbose': ''}, - []) - - def test_bool_false(self): - (options, args) = self.assertParseOK(["-q"], - {'verbose': 0}, - []) - if hasattr(__builtins__, 'False'): - self.failUnless(options.verbose is False) - - def test_bool_true(self): - (options, args) = self.assertParseOK(["-v"], - {'verbose': 1}, - []) - if hasattr(__builtins__, 'True'): - self.failUnless(options.verbose is True) - - def test_bool_flicker_on_and_off(self): - self.assertParseOK(["-qvq", "-q", "-v"], - {'verbose': 1}, - []) - -class TestChoice(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.parser.add_option("-c", action="store", type="choice", - dest="choice", choices=["one", "two", "three"]) - - def test_valid_choice(self): - self.assertParseOK(["-c", "one", "xyz"], - {'choice': 'one'}, - ["xyz"]) - - def test_invalid_choice(self): - self.assertParseFail(["-c", "four", "abc"], - "option -c: invalid choice: 'four' " - "(choose from 'one', 'two', 'three')") - - def test_add_choice_option(self): - self.parser.add_option("-d", "--default", - choices=["four", "five", "six"]) - opt = self.parser.get_option("-d") - self.assertEqual(opt.type, "choice") - self.assertEqual(opt.action, "store") - -class TestCount(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.v_opt = make_option("-v", action="count", dest="verbose") - self.parser.add_option(self.v_opt) - self.parser.add_option("--verbose", type="int", dest="verbose") - self.parser.add_option("-q", "--quiet", - action="store_const", dest="verbose", const=0) - - def test_empty(self): - self.assertParseOK([], {'verbose': None}, []) - - def test_count_one(self): - self.assertParseOK(["-v"], {'verbose': 1}, []) - - def test_count_three(self): - self.assertParseOK(["-vvv"], {'verbose': 3}, []) - - def test_count_three_apart(self): - self.assertParseOK(["-v", "-v", "-v"], {'verbose': 3}, []) - - def test_count_override_amount(self): - self.assertParseOK(["-vvv", "--verbose=2"], {'verbose': 2}, []) - - def test_count_override_quiet(self): - self.assertParseOK(["-vvv", "--verbose=2", "-q"], {'verbose': 0}, []) - - def test_count_overriding(self): - self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"], - {'verbose': 1}, []) - - def test_count_interspersed_args(self): - self.assertParseOK(["--quiet", "3", "-v"], - {'verbose': 1}, - ["3"]) - - def test_count_no_interspersed_args(self): - self.parser.disable_interspersed_args() - self.assertParseOK(["--quiet", "3", "-v"], - {'verbose': 0}, - ["3", "-v"]) - - def test_count_no_such_option(self): - self.assertParseFail(["-q3", "-v"], "no such option: -3") - - def test_count_option_no_value(self): - self.assertParseFail(["--quiet=3", "-v"], - "--quiet option does not take a value") - - def test_count_with_default(self): - self.parser.set_default('verbose', 0) - self.assertParseOK([], {'verbose':0}, []) - - def test_count_overriding_default(self): - self.parser.set_default('verbose', 0) - self.assertParseOK(["-vvv", "--verbose=2", "-q", "-v"], - {'verbose': 1}, []) - -class TestMultipleArgs(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.parser.add_option("-p", "--point", - action="store", nargs=3, type="float", dest="point") - - def test_nargs_with_positional_args(self): - self.assertParseOK(["foo", "-p", "1", "2.5", "-4.3", "xyz"], - {'point': (1.0, 2.5, -4.3)}, - ["foo", "xyz"]) - - def test_nargs_long_opt(self): - self.assertParseOK(["--point", "-1", "2.5", "-0", "xyz"], - {'point': (-1.0, 2.5, -0.0)}, - ["xyz"]) - - def test_nargs_invalid_float_value(self): - self.assertParseFail(["-p", "1.0", "2x", "3.5"], - "option -p: " - "invalid floating-point value: '2x'") - - def test_nargs_required_values(self): - self.assertParseFail(["--point", "1.0", "3.5"], - "--point option requires 3 arguments") - -class TestMultipleArgsAppend(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.parser.add_option("-p", "--point", action="store", nargs=3, - type="float", dest="point") - self.parser.add_option("-f", "--foo", action="append", nargs=2, - type="int", dest="foo") - - def test_nargs_append(self): - self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"], - {'point': None, 'foo': [(4, -3), (1, 666)]}, - ["blah"]) - - def test_nargs_append_required_values(self): - self.assertParseFail(["-f4,3"], - "-f option requires 2 arguments") - - def test_nargs_append_simple(self): - self.assertParseOK(["--foo=3", "4"], - {'point': None, 'foo':[(3, 4)]}, - []) - -class TestVersion(BaseTest): - def test_version(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, - version="%prog 0.1") - save_argv = sys.argv[:] - try: - sys.argv[0] = os.path.join(os.curdir, "foo", "bar") - self.assertOutput(["--version"], "bar 0.1\n") - finally: - sys.argv[:] = save_argv - - def test_no_version(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.assertParseFail(["--version"], - "no such option: --version") - -# -- Test conflicting default values and parser.parse_args() ----------- - -class TestConflictingDefaults(BaseTest): - """Conflicting default values: the last one should win.""" - def setUp(self): - self.parser = OptionParser(option_list=[ - make_option("-v", action="store_true", dest="verbose", default=1)]) - - def test_conflict_default(self): - self.parser.add_option("-q", action="store_false", dest="verbose", - default=0) - self.assertParseOK([], {'verbose': 0}, []) - - def test_conflict_default_none(self): - self.parser.add_option("-q", action="store_false", dest="verbose", - default=None) - self.assertParseOK([], {'verbose': None}, []) - -class TestOptionGroup(BaseTest): - def setUp(self): - self.parser = OptionParser(usage=SUPPRESS_USAGE) - - def test_option_group_create_instance(self): - group = OptionGroup(self.parser, "Spam") - self.parser.add_option_group(group) - group.add_option("--spam", action="store_true", - help="spam spam spam spam") - self.assertParseOK(["--spam"], {'spam': 1}, []) - - def test_add_group_no_group(self): - self.assertTypeError(self.parser.add_option_group, - "not an OptionGroup instance: None", None) - - def test_add_group_invalid_arguments(self): - self.assertTypeError(self.parser.add_option_group, - "invalid arguments", None, None) - - def test_add_group_wrong_parser(self): - group = OptionGroup(self.parser, "Spam") - group.parser = OptionParser() - self.assertRaises(self.parser.add_option_group, (group,), None, - ValueError, "invalid OptionGroup (wrong parser)") - - def test_group_manipulate(self): - group = self.parser.add_option_group("Group 2", - description="Some more options") - group.set_title("Bacon") - group.add_option("--bacon", type="int") - self.assert_(self.parser.get_option_group("--bacon"), group) - -# -- Test extending and parser.parse_args() ---------------------------- - -class TestExtendAddTypes(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, - option_class=self.MyOption) - self.parser.add_option("-a", None, type="string", dest="a") - self.parser.add_option("-f", "--file", type="file", dest="file") - - class MyOption (Option): - def check_file (option, opt, value): - if not os.path.exists(value): - raise OptionValueError("%s: file does not exist" % value) - elif not os.path.isfile(value): - raise OptionValueError("%s: not a regular file" % value) - return value - - TYPES = Option.TYPES + ("file",) - TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER) - TYPE_CHECKER["file"] = check_file - - def test_extend_file(self): - open(test_support.TESTFN, "w").close() - self.assertParseOK(["--file", test_support.TESTFN, "-afoo"], - {'file': test_support.TESTFN, 'a': 'foo'}, - []) - - os.unlink(test_support.TESTFN) - - def test_extend_file_nonexistent(self): - self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], - "%s: file does not exist" % - test_support.TESTFN) - - def test_file_irregular(self): - os.mkdir(test_support.TESTFN) - self.assertParseFail(["--file", test_support.TESTFN, "-afoo"], - "%s: not a regular file" % - test_support.TESTFN) - os.rmdir(test_support.TESTFN) - -class TestExtendAddActions(BaseTest): - def setUp(self): - options = [self.MyOption("-a", "--apple", action="extend", - type="string", dest="apple")] - self.parser = OptionParser(option_list=options) - - class MyOption (Option): - ACTIONS = Option.ACTIONS + ("extend",) - STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) - TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) - - def take_action (self, action, dest, opt, value, values, parser): - if action == "extend": - lvalue = value.split(",") - values.ensure_value(dest, []).extend(lvalue) - else: - Option.take_action(self, action, dest, opt, parser, value, - values) - - def test_extend_add_action(self): - self.assertParseOK(["-afoo,bar", "--apple=blah"], - {'apple': ["foo", "bar", "blah"]}, - []) - - def test_extend_add_action_normal(self): - self.assertParseOK(["-a", "foo", "-abar", "--apple=x,y"], - {'apple': ["foo", "bar", "x", "y"]}, - []) - -# -- Test callbacks and parser.parse_args() ---------------------------- - -class TestCallback(BaseTest): - def setUp(self): - options = [make_option("-x", - None, - action="callback", - callback=self.process_opt), - make_option("-f", - "--file", - action="callback", - callback=self.process_opt, - type="string", - dest="filename")] - self.parser = OptionParser(option_list=options) - - def process_opt(self, option, opt, value, parser_): - if opt == "-x": - self.assertEqual(option._short_opts, ["-x"]) - self.assertEqual(option._long_opts, []) - self.assert_(parser_ is self.parser) - self.assert_(value is None) - self.assertEqual(vars(parser_.values), {'filename': None}) - - parser_.values.x = 42 - elif opt == "--file": - self.assertEqual(option._short_opts, ["-f"]) - self.assertEqual(option._long_opts, ["--file"]) - self.assert_(parser_ is self.parser) - self.assertEqual(value, "foo") - self.assertEqual(vars(parser_.values), {'filename': None, 'x': 42}) - - setattr(parser_.values, option.dest, value) - else: - self.fail("Unknown option %r in process_opt." % opt) - - def test_callback(self): - self.assertParseOK(["-x", "--file=foo"], - {'filename': "foo", 'x': 42}, - []) - - def test_callback_help(self): - # This test was prompted by SF bug #960515 -- the point is - # not to inspect the help text, just to make sure that - # format_help() doesn't crash. - parser = OptionParser(usage=SUPPRESS_USAGE) - parser.remove_option("-h") - parser.add_option("-t", "--test", action="callback", - callback=lambda: None, type="string", - help="foo") - - expected_help = ("options:\n" - " -t TEST, --test=TEST foo\n") - self.assertHelp(parser, expected_help) - - -class TestCallbackExtraArgs(BaseTest): - def setUp(self): - options = [make_option("-p", "--point", action="callback", - callback=self.process_tuple, - callback_args=(3, int), type="string", - dest="points", default=[])] - self.parser = OptionParser(option_list=options) - - def process_tuple (self, option, opt, value, parser_, len, type): - self.assertEqual(len, 3) - self.assert_(type is int) - - if opt == "-p": - self.assertEqual(value, "1,2,3") - elif opt == "--point": - self.assertEqual(value, "4,5,6") - - value = tuple(map(type, value.split(","))) - getattr(parser_.values, option.dest).append(value) - - def test_callback_extra_args(self): - self.assertParseOK(["-p1,2,3", "--point", "4,5,6"], - {'points': [(1,2,3), (4,5,6)]}, - []) - -class TestCallbackMeddleArgs(BaseTest): - def setUp(self): - options = [make_option(str(x), action="callback", - callback=self.process_n, dest='things') - for x in range(-1, -6, -1)] - self.parser = OptionParser(option_list=options) - - # Callback that meddles in rargs, largs - def process_n (self, option, opt, value, parser_): - # option is -3, -5, etc. - nargs = int(opt[1:]) - rargs = parser_.rargs - if len(rargs) < nargs: - self.fail("Expected %d arguments for %s option." % (nargs, opt)) - dest = parser_.values.ensure_value(option.dest, []) - dest.append(tuple(rargs[0:nargs])) - parser_.largs.append(nargs) - del rargs[0:nargs] - - def test_callback_meddle_args(self): - self.assertParseOK(["-1", "foo", "-3", "bar", "baz", "qux"], - {'things': [("foo",), ("bar", "baz", "qux")]}, - [1, 3]) - - def test_callback_meddle_args_separator(self): - self.assertParseOK(["-2", "foo", "--"], - {'things': [('foo', '--')]}, - [2]) - -class TestCallbackManyArgs(BaseTest): - def setUp(self): - options = [make_option("-a", "--apple", action="callback", nargs=2, - callback=self.process_many, type="string"), - make_option("-b", "--bob", action="callback", nargs=3, - callback=self.process_many, type="int")] - self.parser = OptionParser(option_list=options) - - def process_many (self, option, opt, value, parser_): - if opt == "-a": - self.assertEqual(value, ("foo", "bar")) - elif opt == "--apple": - self.assertEqual(value, ("ding", "dong")) - elif opt == "-b": - self.assertEqual(value, (1, 2, 3)) - elif opt == "--bob": - self.assertEqual(value, (-666, 42, 0)) - - def test_many_args(self): - self.assertParseOK(["-a", "foo", "bar", "--apple", "ding", "dong", - "-b", "1", "2", "3", "--bob", "-666", "42", - "0"], - {"apple": None, "bob": None}, - []) - -class TestCallbackCheckAbbrev(BaseTest): - def setUp(self): - self.parser = OptionParser() - self.parser.add_option("--foo-bar", action="callback", - callback=self.check_abbrev) - - def check_abbrev (self, option, opt, value, parser): - self.assertEqual(opt, "--foo-bar") - - def test_abbrev_callback_expansion(self): - self.assertParseOK(["--foo"], {}, []) - -class TestCallbackVarArgs(BaseTest): - def setUp(self): - options = [make_option("-a", type="int", nargs=2, dest="a"), - make_option("-b", action="store_true", dest="b"), - make_option("-c", "--callback", action="callback", - callback=self.variable_args, dest="c")] - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, - option_list=options) - - def variable_args (self, option, opt, value, parser): - self.assert_(value is None) - done = 0 - value = [] - rargs = parser.rargs - while rargs: - arg = rargs[0] - if ((arg[:2] == "--" and len(arg) > 2) or - (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")): - break - else: - value.append(arg) - del rargs[0] - setattr(parser.values, option.dest, value) - - def test_variable_args(self): - self.assertParseOK(["-a3", "-5", "--callback", "foo", "bar"], - {'a': (3, -5), 'b': None, 'c': ["foo", "bar"]}, - []) - - def test_consume_separator_stop_at_option(self): - self.assertParseOK(["-c", "37", "--", "xxx", "-b", "hello"], - {'a': None, - 'b': True, - 'c': ["37", "--", "xxx"]}, - ["hello"]) - - def test_positional_arg_and_variable_args(self): - self.assertParseOK(["hello", "-c", "foo", "-", "bar"], - {'a': None, - 'b': None, - 'c':["foo", "-", "bar"]}, - ["hello"]) - - def test_stop_at_option(self): - self.assertParseOK(["-c", "foo", "-b"], - {'a': None, 'b': True, 'c': ["foo"]}, - []) - - def test_stop_at_invalid_option(self): - self.assertParseFail(["-c", "3", "-5", "-a"], "no such option: -5") - - -# -- Test conflict handling and parser.parse_args() -------------------- - -class ConflictBase(BaseTest): - def setUp(self): - options = [make_option("-v", "--verbose", action="count", - dest="verbose", help="increment verbosity")] - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE, - option_list=options) - - def show_version (self, option, opt, value, parser): - parser.values.show_version = 1 - -class TestConflict(ConflictBase): - """Use the default conflict resolution for Optik 1.2: error.""" - def assert_conflict_error(self, func): - err = self.assertRaises( - func, ("-v", "--version"), {'action' : "callback", - 'callback' : self.show_version, - 'help' : "show version"}, - OptionConflictError, - "option -v/--version: conflicting option string(s): -v") - - self.assertEqual(err.msg, "conflicting option string(s): -v") - self.assertEqual(err.option_id, "-v/--version") - - def test_conflict_error(self): - self.assert_conflict_error(self.parser.add_option) - - def test_conflict_error_group(self): - group = OptionGroup(self.parser, "Group 1") - self.assert_conflict_error(group.add_option) - - def test_no_such_conflict_handler(self): - self.assertRaises( - self.parser.set_conflict_handler, ('foo',), None, - ValueError, "invalid conflict_resolution value 'foo'") - - -class TestConflictResolve(ConflictBase): - def setUp(self): - ConflictBase.setUp(self) - self.parser.set_conflict_handler("resolve") - self.parser.add_option("-v", "--version", action="callback", - callback=self.show_version, help="show version") - - def test_conflict_resolve(self): - v_opt = self.parser.get_option("-v") - verbose_opt = self.parser.get_option("--verbose") - version_opt = self.parser.get_option("--version") - - self.assert_(v_opt is version_opt) - self.assert_(v_opt is not verbose_opt) - self.assertEqual(v_opt._long_opts, ["--version"]) - self.assertEqual(version_opt._short_opts, ["-v"]) - self.assertEqual(version_opt._long_opts, ["--version"]) - self.assertEqual(verbose_opt._short_opts, []) - self.assertEqual(verbose_opt._long_opts, ["--verbose"]) - - def test_conflict_resolve_help(self): - self.assertOutput(["-h"], """\ -options: - --verbose increment verbosity - -h, --help show this help message and exit - -v, --version show version -""") - - def test_conflict_resolve_short_opt(self): - self.assertParseOK(["-v"], - {'verbose': None, 'show_version': 1}, - []) - - def test_conflict_resolve_long_opt(self): - self.assertParseOK(["--verbose"], - {'verbose': 1}, - []) - - def test_conflict_resolve_long_opts(self): - self.assertParseOK(["--verbose", "--version"], - {'verbose': 1, 'show_version': 1}, - []) - -class TestConflictOverride(BaseTest): - def setUp(self): - self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE) - self.parser.set_conflict_handler("resolve") - self.parser.add_option("-n", "--dry-run", - action="store_true", dest="dry_run", - help="don't do anything") - self.parser.add_option("--dry-run", "-n", - action="store_const", const=42, dest="dry_run", - help="dry run mode") - - def test_conflict_override_opts(self): - opt = self.parser.get_option("--dry-run") - self.assertEqual(opt._short_opts, ["-n"]) - self.assertEqual(opt._long_opts, ["--dry-run"]) - - def test_conflict_override_help(self): - self.assertOutput(["-h"], """\ -options: - -h, --help show this help message and exit - -n, --dry-run dry run mode -""") - - def test_conflict_override_args(self): - self.assertParseOK(["-n"], - {'dry_run': 42}, - []) - -# -- Other testing. ---------------------------------------------------- - -_expected_help_basic = """\ -usage: bar.py [options] - -options: - -a APPLE throw APPLEs at basket - -b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the - evil spirits that cause trouble and mayhem) - --foo=FOO store FOO in the foo list for later fooing - -h, --help show this help message and exit -""" - -_expected_help_long_opts_first = """\ -usage: bar.py [options] - -options: - -a APPLE throw APPLEs at basket - --boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the - evil spirits that cause trouble and mayhem) - --foo=FOO store FOO in the foo list for later fooing - --help, -h show this help message and exit -""" - -_expected_help_title_formatter = """\ -Usage -===== - bar.py [options] - -options -======= --a APPLE throw APPLEs at basket ---boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the - evil spirits that cause trouble and mayhem) ---foo=FOO store FOO in the foo list for later fooing ---help, -h show this help message and exit -""" - -_expected_help_short_lines = """\ -usage: bar.py [options] - -options: - -a APPLE throw APPLEs at basket - -b NUM, --boo=NUM shout "boo!" NUM times (in order to - frighten away all the evil spirits - that cause trouble and mayhem) - --foo=FOO store FOO in the foo list for later - fooing - -h, --help show this help message and exit -""" - -class TestHelp(BaseTest): - def setUp(self): - self.parser = self.make_parser(80) - - def make_parser(self, columns): - options = [ - make_option("-a", type="string", dest='a', - metavar="APPLE", help="throw APPLEs at basket"), - make_option("-b", "--boo", type="int", dest='boo', - metavar="NUM", - help= - "shout \"boo!\" NUM times (in order to frighten away " - "all the evil spirits that cause trouble and mayhem)"), - make_option("--foo", action="append", type="string", dest='foo', - help="store FOO in the foo list for later fooing"), - ] - os.environ['COLUMNS'] = str(columns) - return InterceptingOptionParser(option_list=options) - - def assertHelpEquals(self, expected_output): - save_argv = sys.argv[:] - try: - # Make optparse believe bar.py is being executed. - sys.argv[0] = os.path.join("foo", "bar.py") - self.assertOutput(["-h"], expected_output) - finally: - sys.argv[:] = save_argv - - def test_help(self): - self.assertHelpEquals(_expected_help_basic) - - def test_help_old_usage(self): - self.parser.set_usage("usage: %prog [options]") - self.assertHelpEquals(_expected_help_basic) - - def test_help_long_opts_first(self): - self.parser.formatter.short_first = 0 - self.assertHelpEquals(_expected_help_long_opts_first) - - def test_help_title_formatter(self): - self.parser.formatter = TitledHelpFormatter() - self.assertHelpEquals(_expected_help_title_formatter) - - def test_wrap_columns(self): - # Ensure that wrapping respects $COLUMNS environment variable. - # Need to reconstruct the parser, since that's the only time - # we look at $COLUMNS. - self.parser = self.make_parser(60) - self.assertHelpEquals(_expected_help_short_lines) - - def test_help_description_groups(self): - self.parser.set_description( - "This is the program description for %prog. %prog has " - "an option group as well as single options.") - - group = OptionGroup( - self.parser, "Dangerous Options", - "Caution: use of these options is at your own risk. " - "It is believed that some of them bite.") - group.add_option("-g", action="store_true", help="Group option.") - self.parser.add_option_group(group) - - self.assertHelpEquals("""\ -usage: bar.py [options] - -This is the program description for bar.py. bar.py has an option group as -well as single options. - -options: - -a APPLE throw APPLEs at basket - -b NUM, --boo=NUM shout "boo!" NUM times (in order to frighten away all the - evil spirits that cause trouble and mayhem) - --foo=FOO store FOO in the foo list for later fooing - -h, --help show this help message and exit - - Dangerous Options: - Caution: use of these options is at your own risk. It is believed - that some of them bite. - - -g Group option. -""") - - - - -class TestMatchAbbrev(BaseTest): - def test_match_abbrev(self): - self.assertEqual(_match_abbrev("--f", - {"--foz": None, - "--foo": None, - "--fie": None, - "--f": None}), - "--f") - - def test_match_abbrev_error(self): - s = "--f" - wordmap = {"--foz": None, "--foo": None, "--fie": None} - self.assertRaises( - _match_abbrev, (s, wordmap), None, - BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)") - - -def _testclasses(): - mod = sys.modules[__name__] - return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] - -def suite(): - suite = unittest.TestSuite() - for testclass in _testclasses(): - suite.addTest(unittest.makeSuite(testclass)) - return suite - -def test_main(): - test_support.run_suite(suite()) - -if __name__ == '__main__': - unittest.main() diff --git a/py/compat/testing/test_subprocess.py b/py/compat/testing/test_subprocess.py deleted file mode 100644 index 444589439..000000000 --- a/py/compat/testing/test_subprocess.py +++ /dev/null @@ -1,563 +0,0 @@ -import unittest -from test import test_support -from py.compat import subprocess -import sys -import signal -import os -import tempfile -import time -import re - -mswindows = (sys.platform == "win32") - -# -# Depends on the following external programs: Python -# - -if mswindows: - SETBINARY = ('import msvcrt; msvcrt.setmode(sys.stdout.fileno(), ' - 'os.O_BINARY);') -else: - SETBINARY = '' - -# In a debug build, stuff like "[6580 refs]" is printed to stderr at -# shutdown time. That frustrates tests trying to check stderr produced -# from a spawned Python process. -def remove_stderr_debug_decorations(stderr): - return re.sub(r"\[\d+ refs\]\r?\n?$", "", stderr) - -class ProcessTestCase(unittest.TestCase): - def mkstemp(self): - """wrapper for mkstemp, calling mktemp if mkstemp is not available""" - if hasattr(tempfile, "mkstemp"): - return tempfile.mkstemp() - else: - fname = tempfile.mktemp() - return os.open(fname, os.O_RDWR|os.O_CREAT), fname - - # - # Generic tests - # - def test_call_seq(self): - # call() function with sequence argument - rc = subprocess.call([sys.executable, "-c", - "import sys; sys.exit(47)"]) - self.assertEqual(rc, 47) - - def test_call_kwargs(self): - # call() function with keyword args - newenv = os.environ.copy() - newenv["FRUIT"] = "banana" - rc = subprocess.call([sys.executable, "-c", - 'import sys, os;' \ - 'sys.exit(os.getenv("FRUIT")=="banana")'], - env=newenv) - self.assertEqual(rc, 1) - - def test_stdin_none(self): - # .stdin is None when not redirected - p = subprocess.Popen([sys.executable, "-c", 'print "banana"'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p.wait() - self.assertEqual(p.stdin, None) - - def test_stdout_none(self): - # .stdout is None when not redirected - p = subprocess.Popen([sys.executable, "-c", - 'print " this bit of output is from a ' - 'test of stdout in a different ' - 'process ..."'], - stdin=subprocess.PIPE, stderr=subprocess.PIPE) - p.wait() - self.assertEqual(p.stdout, None) - - def test_stderr_none(self): - # .stderr is None when not redirected - p = subprocess.Popen([sys.executable, "-c", 'print "banana"'], - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - p.wait() - self.assertEqual(p.stderr, None) - - def test_executable(self): - p = subprocess.Popen(["somethingyoudonthave", - "-c", "import sys; sys.exit(47)"], - executable=sys.executable) - p.wait() - self.assertEqual(p.returncode, 47) - - def test_stdin_pipe(self): - # stdin redirection - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.exit(sys.stdin.read() == "pear")'], - stdin=subprocess.PIPE) - p.stdin.write("pear") - p.stdin.close() - p.wait() - self.assertEqual(p.returncode, 1) - - def test_stdin_filedes(self): - # stdin is set to open file descriptor - tf = tempfile.TemporaryFile() - d = tf.fileno() - os.write(d, "pear") - os.lseek(d, 0, 0) - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.exit(sys.stdin.read() == "pear")'], - stdin=d) - p.wait() - self.assertEqual(p.returncode, 1) - - def test_stdin_fileobj(self): - # stdin is set to open file object - tf = tempfile.TemporaryFile() - tf.write("pear") - tf.seek(0) - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.exit(sys.stdin.read() == "pear")'], - stdin=tf) - p.wait() - self.assertEqual(p.returncode, 1) - - def test_stdout_pipe(self): - # stdout redirection - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stdout.write("orange")'], - stdout=subprocess.PIPE) - self.assertEqual(p.stdout.read(), "orange") - - def test_stdout_filedes(self): - # stdout is set to open file descriptor - tf = tempfile.TemporaryFile() - d = tf.fileno() - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stdout.write("orange")'], - stdout=d) - p.wait() - os.lseek(d, 0, 0) - self.assertEqual(os.read(d, 1024), "orange") - - def test_stdout_fileobj(self): - # stdout is set to open file object - tf = tempfile.TemporaryFile() - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stdout.write("orange")'], - stdout=tf) - p.wait() - tf.seek(0) - self.assertEqual(tf.read(), "orange") - - def test_stderr_pipe(self): - # stderr redirection - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stderr.write("strawberry")'], - stderr=subprocess.PIPE) - self.assertEqual(remove_stderr_debug_decorations(p.stderr.read()), - "strawberry") - - def test_stderr_filedes(self): - # stderr is set to open file descriptor - tf = tempfile.TemporaryFile() - d = tf.fileno() - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stderr.write("strawberry")'], - stderr=d) - p.wait() - os.lseek(d, 0, 0) - self.assertEqual(remove_stderr_debug_decorations(os.read(d, 1024)), - "strawberry") - - def test_stderr_fileobj(self): - # stderr is set to open file object - tf = tempfile.TemporaryFile() - p = subprocess.Popen([sys.executable, "-c", - 'import sys; sys.stderr.write("strawberry")'], - stderr=tf) - p.wait() - tf.seek(0) - self.assertEqual(remove_stderr_debug_decorations(tf.read()), - "strawberry") - - def test_stdout_stderr_pipe(self): - # capture stdout and stderr to the same pipe - p = subprocess.Popen([sys.executable, "-c", - 'import sys;' \ - 'sys.stdout.write("apple");' \ - 'sys.stdout.flush();' \ - 'sys.stderr.write("orange")'], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output = p.stdout.read() - stripped = remove_stderr_debug_decorations(output) - self.assertEqual(stripped, "appleorange") - - def test_stdout_stderr_file(self): - # capture stdout and stderr to the same open file - tf = tempfile.TemporaryFile() - p = subprocess.Popen([sys.executable, "-c", - 'import sys;' \ - 'sys.stdout.write("apple");' \ - 'sys.stdout.flush();' \ - 'sys.stderr.write("orange")'], - stdout=tf, - stderr=tf) - p.wait() - tf.seek(0) - output = tf.read() - stripped = remove_stderr_debug_decorations(output) - self.assertEqual(stripped, "appleorange") - - def test_cwd(self): - tmpdir = os.getenv("TEMP", "/tmp") - # We cannot use os.path.realpath to canonicalize the path, - # since it doesn't expand Tru64 {memb} strings. See bug 1063571. - cwd = os.getcwd() - os.chdir(tmpdir) - tmpdir = os.getcwd() - os.chdir(cwd) - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' \ - 'sys.stdout.write(os.getcwd())'], - stdout=subprocess.PIPE, - cwd=tmpdir) - normcase = os.path.normcase - self.assertEqual(normcase(p.stdout.read()), normcase(tmpdir)) - - def test_env(self): - newenv = os.environ.copy() - newenv["FRUIT"] = "orange" - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' \ - 'sys.stdout.write(os.getenv("FRUIT"))'], - stdout=subprocess.PIPE, - env=newenv) - self.assertEqual(p.stdout.read(), "orange") - - def test_communicate(self): - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' \ - 'sys.stderr.write("pineapple");' \ - 'sys.stdout.write(sys.stdin.read())'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (stdout, stderr) = p.communicate("banana") - self.assertEqual(stdout, "banana") - self.assertEqual(remove_stderr_debug_decorations(stderr), - "pineapple") - - def test_communicate_returns(self): - # communicate() should return None if no redirection is active - p = subprocess.Popen([sys.executable, "-c", - "import sys; sys.exit(47)"]) - (stdout, stderr) = p.communicate() - self.assertEqual(stdout, None) - self.assertEqual(stderr, None) - - def test_communicate_pipe_buf(self): - # communicate() with writes larger than pipe_buf - # This test will probably deadlock rather than fail, if - # communicate() does not work properly. - x, y = os.pipe() - if mswindows: - pipe_buf = 512 - else: - pipe_buf = os.fpathconf(x, "PC_PIPE_BUF") - os.close(x) - os.close(y) - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' - 'sys.stdout.write(sys.stdin.read(47));' \ - 'sys.stderr.write("xyz"*%d);' \ - 'sys.stdout.write(sys.stdin.read())' % pipe_buf], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - string_to_write = "abc"*pipe_buf - (stdout, stderr) = p.communicate(string_to_write) - self.assertEqual(stdout, string_to_write) - - def test_writes_before_communicate(self): - # stdin.write before communicate() - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' \ - 'sys.stdout.write(sys.stdin.read())'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p.stdin.write("banana") - (stdout, stderr) = p.communicate("split") - self.assertEqual(stdout, "bananasplit") - self.assertEqual(remove_stderr_debug_decorations(stderr), "") - - def test_universal_newlines(self): - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' + SETBINARY + - 'sys.stdout.write("line1\\n");' - 'sys.stdout.flush();' - 'sys.stdout.write("line2\\r");' - 'sys.stdout.flush();' - 'sys.stdout.write("line3\\r\\n");' - 'sys.stdout.flush();' - 'sys.stdout.write("line4\\r");' - 'sys.stdout.flush();' - 'sys.stdout.write("\\nline5");' - 'sys.stdout.flush();' - 'sys.stdout.write("\\nline6");'], - stdout=subprocess.PIPE, - universal_newlines=1) - stdout = p.stdout.read() - if hasattr(open, 'newlines'): - # Interpreter with universal newline support - self.assertEqual(stdout, - "line1\nline2\nline3\nline4\nline5\nline6") - else: - # Interpreter without universal newline support - self.assertEqual(stdout, - "line1\nline2\rline3\r\nline4\r\nline5\nline6") - - def test_universal_newlines_communicate(self): - # universal newlines through communicate() - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' + SETBINARY + - 'sys.stdout.write("line1\\n");' - 'sys.stdout.flush();' - 'sys.stdout.write("line2\\r");' - 'sys.stdout.flush();' - 'sys.stdout.write("line3\\r\\n");' - 'sys.stdout.flush();' - 'sys.stdout.write("line4\\r");' - 'sys.stdout.flush();' - 'sys.stdout.write("\\nline5");' - 'sys.stdout.flush();' - 'sys.stdout.write("\\nline6");'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=1) - (stdout, stderr) = p.communicate() - if hasattr(open, 'newlines'): - # Interpreter with universal newline support - self.assertEqual(stdout, - "line1\nline2\nline3\nline4\nline5\nline6") - else: - # Interpreter without universal newline support - self.assertEqual(stdout, "line1\nline2\rline3\r\nline4\r\nline5\nline6") - - def test_no_leaking(self): - # Make sure we leak no resources - if test_support.is_resource_enabled("subprocess") and not mswindows: - max_handles = 1026 # too much for most UNIX systems - else: - max_handles = 65 - for i in range(max_handles): - p = subprocess.Popen([sys.executable, "-c", - "import sys;sys.stdout.write(sys.stdin.read())"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - data = p.communicate("lime")[0] - self.assertEqual(data, "lime") - - - def test_list2cmdline(self): - self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']), - '"a b c" d e') - self.assertEqual(subprocess.list2cmdline(['ab"c', '\\', 'd']), - 'ab\\"c \\ d') - self.assertEqual(subprocess.list2cmdline(['a\\\\\\b', 'de fg', 'h']), - 'a\\\\\\b "de fg" h') - self.assertEqual(subprocess.list2cmdline(['a\\"b', 'c', 'd']), - 'a\\\\\\"b c d') - self.assertEqual(subprocess.list2cmdline(['a\\\\b c', 'd', 'e']), - '"a\\\\b c" d e') - self.assertEqual(subprocess.list2cmdline(['a\\\\b\\ c', 'd', 'e']), - '"a\\\\b\\ c" d e') - - - def test_poll(self): - p = subprocess.Popen([sys.executable, - "-c", "import time; time.sleep(1)"]) - count = 0 - while p.poll() is None: - time.sleep(0.1) - count += 1 - # We expect that the poll loop probably went around about 10 times, - # but, based on system scheduling we can't control, it's possible - # poll() never returned None. It "should be" very rare that it - # didn't go around at least twice. - self.assert_(count >= 2) - # Subsequent invocations should just return the returncode - self.assertEqual(p.poll(), 0) - - - def test_wait(self): - p = subprocess.Popen([sys.executable, - "-c", "import time; time.sleep(2)"]) - self.assertEqual(p.wait(), 0) - # Subsequent invocations should just return the returncode - self.assertEqual(p.wait(), 0) - - - def test_invalid_bufsize(self): - # an invalid type of the bufsize argument should raise - # TypeError. - try: - subprocess.Popen([sys.executable, "-c", "pass"], "orange") - except TypeError: - pass - else: - self.fail("Expected TypeError") - - # - # POSIX tests - # - if not mswindows: - def test_exceptions(self): - # catched & re-raised exceptions - try: - p = subprocess.Popen([sys.executable, "-c", ""], - cwd="/this/path/does/not/exist") - except OSError, e: - # The attribute child_traceback should contain "os.chdir" - # somewhere. - self.assertNotEqual(e.child_traceback.find("os.chdir"), -1) - else: - self.fail("Expected OSError") - - def test_run_abort(self): - # returncode handles signal termination - p = subprocess.Popen([sys.executable, - "-c", "import os; os.abort()"]) - p.wait() - self.assertEqual(-p.returncode, signal.SIGABRT) - - def test_preexec(self): - # preexec function - p = subprocess.Popen([sys.executable, "-c", - 'import sys,os;' \ - 'sys.stdout.write(os.getenv("FRUIT"))'], - stdout=subprocess.PIPE, - preexec_fn=lambda: os.putenv("FRUIT", "apple")) - self.assertEqual(p.stdout.read(), "apple") - - def test_args_string(self): - # args is a string - f, fname = self.mkstemp() - os.write(f, "#!/bin/sh\n") - os.write(f, "exec %s -c 'import sys; sys.exit(47)'\n" % - sys.executable) - os.close(f) - os.chmod(fname, 0700) - p = subprocess.Popen(fname) - p.wait() - os.remove(fname) - self.assertEqual(p.returncode, 47) - - def test_invalid_args(self): - # invalid arguments should raise ValueError - self.assertRaises(ValueError, subprocess.call, - [sys.executable, - "-c", "import sys; sys.exit(47)"], - startupinfo=47) - self.assertRaises(ValueError, subprocess.call, - [sys.executable, - "-c", "import sys; sys.exit(47)"], - creationflags=47) - - def test_shell_sequence(self): - # Run command through the shell (sequence) - newenv = os.environ.copy() - newenv["FRUIT"] = "apple" - p = subprocess.Popen(["echo $FRUIT"], shell=1, - stdout=subprocess.PIPE, - env=newenv) - self.assertEqual(p.stdout.read().strip(), "apple") - - def test_shell_string(self): - # Run command through the shell (string) - newenv = os.environ.copy() - newenv["FRUIT"] = "apple" - p = subprocess.Popen("echo $FRUIT", shell=1, - stdout=subprocess.PIPE, - env=newenv) - self.assertEqual(p.stdout.read().strip(), "apple") - - def test_call_string(self): - # call() function with string argument on UNIX - f, fname = self.mkstemp() - os.write(f, "#!/bin/sh\n") - os.write(f, "exec %s -c 'import sys; sys.exit(47)'\n" % - sys.executable) - os.close(f) - os.chmod(fname, 0700) - rc = subprocess.call(fname) - os.remove(fname) - self.assertEqual(rc, 47) - - - # - # Windows tests - # - if mswindows: - def test_startupinfo(self): - # startupinfo argument - # We uses hardcoded constants, because we do not want to - # depend on win32all. - STARTF_USESHOWWINDOW = 1 - SW_MAXIMIZE = 3 - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags = STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_MAXIMIZE - # Since Python is a console process, it won't be affected - # by wShowWindow, but the argument should be silently - # ignored - subprocess.call([sys.executable, "-c", "import sys; sys.exit(0)"], - startupinfo=startupinfo) - - def test_creationflags(self): - # creationflags argument - CREATE_NEW_CONSOLE = 16 - sys.stderr.write(" a DOS box should flash briefly ...\n") - subprocess.call(sys.executable + - ' -c "import time; time.sleep(0.25)"', - creationflags=CREATE_NEW_CONSOLE) - - def test_invalid_args(self): - # invalid arguments should raise ValueError - self.assertRaises(ValueError, subprocess.call, - [sys.executable, - "-c", "import sys; sys.exit(47)"], - preexec_fn=lambda: 1) - self.assertRaises(ValueError, subprocess.call, - [sys.executable, - "-c", "import sys; sys.exit(47)"], - close_fds=True) - - def test_shell_sequence(self): - # Run command through the shell (sequence) - newenv = os.environ.copy() - newenv["FRUIT"] = "physalis" - p = subprocess.Popen(["set"], shell=1, - stdout=subprocess.PIPE, - env=newenv) - self.assertNotEqual(p.stdout.read().find("physalis"), -1) - - def test_shell_string(self): - # Run command through the shell (string) - newenv = os.environ.copy() - newenv["FRUIT"] = "physalis" - p = subprocess.Popen("set", shell=1, - stdout=subprocess.PIPE, - env=newenv) - self.assertNotEqual(p.stdout.read().find("physalis"), -1) - - def test_call_string(self): - # call() function with string argument on Windows - rc = subprocess.call(sys.executable + - ' -c "import sys; sys.exit(47)"') - self.assertEqual(rc, 47) - - -def test_main(): - test_support.run_unittest(ProcessTestCase) - -if __name__ == "__main__": - test_main() diff --git a/py/compat/testing/test_textwrap.py b/py/compat/testing/test_textwrap.py deleted file mode 100644 index 4838f1ca3..000000000 --- a/py/compat/testing/test_textwrap.py +++ /dev/null @@ -1,532 +0,0 @@ -# -# Test suite for the textwrap module. -# -# Original tests written by Greg Ward . -# Converted to PyUnit by Peter Hansen . -# Currently maintained by Greg Ward. -# -# $Id: test_textwrap.py 38573 2005-03-05 02:38:33Z gward $ -# - -import unittest -from test import test_support - -import py -textwrap = py.compat.textwrap -TextWrapper = textwrap.TextWrapper -wrap = textwrap.wrap -fill = textwrap.fill -dedent = textwrap.dedent - -class BaseTestCase(unittest.TestCase): - '''Parent class with utility methods for textwrap tests.''' - - def show(self, textin): - if isinstance(textin, list): - result = [] - for i in range(len(textin)): - result.append(" %d: %r" % (i, textin[i])) - result = '\n'.join(result) - elif isinstance(textin, basestring): - result = " %s\n" % repr(textin) - return result - - - def check(self, result, expect): - self.assertEquals(result, expect, - 'expected:\n%s\nbut got:\n%s' % ( - self.show(expect), self.show(result))) - - def check_wrap(self, text, width, expect, **kwargs): - result = wrap(text, width, **kwargs) - self.check(result, expect) - - def check_split(self, text, expect): - result = self.wrapper._split(text) - self.assertEquals(result, expect, - "\nexpected %r\n" - "but got %r" % (expect, result)) - - -class WrapTestCase(BaseTestCase): - - def setUp(self): - self.wrapper = TextWrapper(width=45) - - def test_simple(self): - # Simple case: just words, spaces, and a bit of punctuation - - text = "Hello there, how are you this fine day? I'm glad to hear it!" - - self.check_wrap(text, 12, - ["Hello there,", - "how are you", - "this fine", - "day? I'm", - "glad to hear", - "it!"]) - self.check_wrap(text, 42, - ["Hello there, how are you this fine day?", - "I'm glad to hear it!"]) - self.check_wrap(text, 80, [text]) - - - def test_whitespace(self): - # Whitespace munging and end-of-sentence detection - - text = """\ -This is a paragraph that already has -line breaks. But some of its lines are much longer than the others, -so it needs to be wrapped. -Some lines are \ttabbed too. -What a mess! -""" - - expect = ["This is a paragraph that already has line", - "breaks. But some of its lines are much", - "longer than the others, so it needs to be", - "wrapped. Some lines are tabbed too. What a", - "mess!"] - - wrapper = TextWrapper(45, fix_sentence_endings=True) - result = wrapper.wrap(text) - self.check(result, expect) - - result = wrapper.fill(text) - self.check(result, '\n'.join(expect)) - - def test_fix_sentence_endings(self): - wrapper = TextWrapper(60, fix_sentence_endings=True) - - # SF #847346: ensure that fix_sentence_endings=True does the - # right thing even on input short enough that it doesn't need to - # be wrapped. - text = "A short line. Note the single space." - expect = ["A short line. Note the single space."] - self.check(wrapper.wrap(text), expect) - - # Test some of the hairy end cases that _fix_sentence_endings() - # is supposed to handle (the easy stuff is tested in - # test_whitespace() above). - text = "Well, Doctor? What do you think?" - expect = ["Well, Doctor? What do you think?"] - self.check(wrapper.wrap(text), expect) - - text = "Well, Doctor?\nWhat do you think?" - self.check(wrapper.wrap(text), expect) - - text = 'I say, chaps! Anyone for "tennis?"\nHmmph!' - expect = ['I say, chaps! Anyone for "tennis?" Hmmph!'] - self.check(wrapper.wrap(text), expect) - - wrapper.width = 20 - expect = ['I say, chaps!', 'Anyone for "tennis?"', 'Hmmph!'] - self.check(wrapper.wrap(text), expect) - - text = 'And she said, "Go to hell!"\nCan you believe that?' - expect = ['And she said, "Go to', - 'hell!" Can you', - 'believe that?'] - self.check(wrapper.wrap(text), expect) - - wrapper.width = 60 - expect = ['And she said, "Go to hell!" Can you believe that?'] - self.check(wrapper.wrap(text), expect) - - def test_wrap_short(self): - # Wrapping to make short lines longer - - text = "This is a\nshort paragraph." - - self.check_wrap(text, 20, ["This is a short", - "paragraph."]) - self.check_wrap(text, 40, ["This is a short paragraph."]) - - - def test_wrap_short_1line(self): - # Test endcases - - text = "This is a short line." - - self.check_wrap(text, 30, ["This is a short line."]) - self.check_wrap(text, 30, ["(1) This is a short line."], - initial_indent="(1) ") - - - def test_hyphenated(self): - # Test breaking hyphenated words - - text = ("this-is-a-useful-feature-for-" - "reformatting-posts-from-tim-peters'ly") - - self.check_wrap(text, 40, - ["this-is-a-useful-feature-for-", - "reformatting-posts-from-tim-peters'ly"]) - self.check_wrap(text, 41, - ["this-is-a-useful-feature-for-", - "reformatting-posts-from-tim-peters'ly"]) - self.check_wrap(text, 42, - ["this-is-a-useful-feature-for-reformatting-", - "posts-from-tim-peters'ly"]) - - def test_hyphenated_numbers(self): - # Test that hyphenated numbers (eg. dates) are not broken like words. - text = ("Python 1.0.0 was released on 1994-01-26. Python 1.0.1 was\n" - "released on 1994-02-15.") - - self.check_wrap(text, 30, ['Python 1.0.0 was released on', - '1994-01-26. Python 1.0.1 was', - 'released on 1994-02-15.']) - self.check_wrap(text, 40, ['Python 1.0.0 was released on 1994-01-26.', - 'Python 1.0.1 was released on 1994-02-15.']) - - text = "I do all my shopping at 7-11." - self.check_wrap(text, 25, ["I do all my shopping at", - "7-11."]) - self.check_wrap(text, 27, ["I do all my shopping at", - "7-11."]) - self.check_wrap(text, 29, ["I do all my shopping at 7-11."]) - - def test_em_dash(self): - # Test text with em-dashes - text = "Em-dashes should be written -- thus." - self.check_wrap(text, 25, - ["Em-dashes should be", - "written -- thus."]) - - # Probe the boundaries of the properly written em-dash, - # ie. " -- ". - self.check_wrap(text, 29, - ["Em-dashes should be written", - "-- thus."]) - expect = ["Em-dashes should be written --", - "thus."] - self.check_wrap(text, 30, expect) - self.check_wrap(text, 35, expect) - self.check_wrap(text, 36, - ["Em-dashes should be written -- thus."]) - - # The improperly written em-dash is handled too, because - # it's adjacent to non-whitespace on both sides. - text = "You can also do--this or even---this." - expect = ["You can also do", - "--this or even", - "---this."] - self.check_wrap(text, 15, expect) - self.check_wrap(text, 16, expect) - expect = ["You can also do--", - "this or even---", - "this."] - self.check_wrap(text, 17, expect) - self.check_wrap(text, 19, expect) - expect = ["You can also do--this or even", - "---this."] - self.check_wrap(text, 29, expect) - self.check_wrap(text, 31, expect) - expect = ["You can also do--this or even---", - "this."] - self.check_wrap(text, 32, expect) - self.check_wrap(text, 35, expect) - - # All of the above behaviour could be deduced by probing the - # _split() method. - text = "Here's an -- em-dash and--here's another---and another!" - expect = ["Here's", " ", "an", " ", "--", " ", "em-", "dash", " ", - "and", "--", "here's", " ", "another", "---", - "and", " ", "another!"] - self.check_split(text, expect) - - text = "and then--bam!--he was gone" - expect = ["and", " ", "then", "--", "bam!", "--", - "he", " ", "was", " ", "gone"] - self.check_split(text, expect) - - - def test_unix_options (self): - # Test that Unix-style command-line options are wrapped correctly. - # Both Optik (OptionParser) and Docutils rely on this behaviour! - - text = "You should use the -n option, or --dry-run in its long form." - self.check_wrap(text, 20, - ["You should use the", - "-n option, or --dry-", - "run in its long", - "form."]) - self.check_wrap(text, 21, - ["You should use the -n", - "option, or --dry-run", - "in its long form."]) - expect = ["You should use the -n option, or", - "--dry-run in its long form."] - self.check_wrap(text, 32, expect) - self.check_wrap(text, 34, expect) - self.check_wrap(text, 35, expect) - self.check_wrap(text, 38, expect) - expect = ["You should use the -n option, or --dry-", - "run in its long form."] - self.check_wrap(text, 39, expect) - self.check_wrap(text, 41, expect) - expect = ["You should use the -n option, or --dry-run", - "in its long form."] - self.check_wrap(text, 42, expect) - - # Again, all of the above can be deduced from _split(). - text = "the -n option, or --dry-run or --dryrun" - expect = ["the", " ", "-n", " ", "option,", " ", "or", " ", - "--dry-", "run", " ", "or", " ", "--dryrun"] - self.check_split(text, expect) - - def test_funky_hyphens (self): - # Screwy edge cases cooked up by David Goodger. All reported - # in SF bug #596434. - self.check_split("what the--hey!", ["what", " ", "the", "--", "hey!"]) - self.check_split("what the--", ["what", " ", "the--"]) - self.check_split("what the--.", ["what", " ", "the--."]) - self.check_split("--text--.", ["--text--."]) - - # When I first read bug #596434, this is what I thought David - # was talking about. I was wrong; these have always worked - # fine. The real problem is tested in test_funky_parens() - # below... - self.check_split("--option", ["--option"]) - self.check_split("--option-opt", ["--option-", "opt"]) - self.check_split("foo --option-opt bar", - ["foo", " ", "--option-", "opt", " ", "bar"]) - - def test_punct_hyphens(self): - # Oh bother, SF #965425 found another problem with hyphens -- - # hyphenated words in single quotes weren't handled correctly. - # In fact, the bug is that *any* punctuation around a hyphenated - # word was handled incorrectly, except for a leading "--", which - # was special-cased for Optik and Docutils. So test a variety - # of styles of punctuation around a hyphenated word. - # (Actually this is based on an Optik bug report, #813077). - self.check_split("the 'wibble-wobble' widget", - ['the', ' ', "'wibble-", "wobble'", ' ', 'widget']) - self.check_split('the "wibble-wobble" widget', - ['the', ' ', '"wibble-', 'wobble"', ' ', 'widget']) - self.check_split("the (wibble-wobble) widget", - ['the', ' ', "(wibble-", "wobble)", ' ', 'widget']) - self.check_split("the ['wibble-wobble'] widget", - ['the', ' ', "['wibble-", "wobble']", ' ', 'widget']) - - def test_funky_parens (self): - # Second part of SF bug #596434: long option strings inside - # parentheses. - self.check_split("foo (--option) bar", - ["foo", " ", "(--option)", " ", "bar"]) - - # Related stuff -- make sure parens work in simpler contexts. - self.check_split("foo (bar) baz", - ["foo", " ", "(bar)", " ", "baz"]) - self.check_split("blah (ding dong), wubba", - ["blah", " ", "(ding", " ", "dong),", - " ", "wubba"]) - - def test_initial_whitespace(self): - # SF bug #622849 reported inconsistent handling of leading - # whitespace; let's test that a bit, shall we? - text = " This is a sentence with leading whitespace." - self.check_wrap(text, 50, - [" This is a sentence with leading whitespace."]) - self.check_wrap(text, 30, - [" This is a sentence with", "leading whitespace."]) - - def test_unicode(self): - # *Very* simple test of wrapping Unicode strings. I'm sure - # there's more to it than this, but let's at least make - # sure textwrap doesn't crash on Unicode input! - text = u"Hello there, how are you today?" - self.check_wrap(text, 50, [u"Hello there, how are you today?"]) - self.check_wrap(text, 20, [u"Hello there, how are", "you today?"]) - olines = self.wrapper.wrap(text) - assert isinstance(olines, list) and isinstance(olines[0], unicode) - otext = self.wrapper.fill(text) - assert isinstance(otext, unicode) - - def test_split(self): - # Ensure that the standard _split() method works as advertised - # in the comments - - text = "Hello there -- you goof-ball, use the -b option!" - - result = self.wrapper._split(text) - self.check(result, - ["Hello", " ", "there", " ", "--", " ", "you", " ", "goof-", - "ball,", " ", "use", " ", "the", " ", "-b", " ", "option!"]) - - def test_bad_width(self): - # Ensure that width <= 0 is caught. - text = "Whatever, it doesn't matter." - self.assertRaises(ValueError, wrap, text, 0) - self.assertRaises(ValueError, wrap, text, -1) - - -class LongWordTestCase (BaseTestCase): - def setUp(self): - self.wrapper = TextWrapper() - self.text = '''\ -Did you say "supercalifragilisticexpialidocious?" -How *do* you spell that odd word, anyways? -''' - - def test_break_long(self): - # Wrap text with long words and lots of punctuation - - self.check_wrap(self.text, 30, - ['Did you say "supercalifragilis', - 'ticexpialidocious?" How *do*', - 'you spell that odd word,', - 'anyways?']) - self.check_wrap(self.text, 50, - ['Did you say "supercalifragilisticexpialidocious?"', - 'How *do* you spell that odd word, anyways?']) - - # SF bug 797650. Prevent an infinite loop by making sure that at - # least one character gets split off on every pass. - self.check_wrap('-'*10+'hello', 10, - ['----------', - ' h', - ' e', - ' l', - ' l', - ' o'], - subsequent_indent = ' '*15) - - def test_nobreak_long(self): - # Test with break_long_words disabled - self.wrapper.break_long_words = 0 - self.wrapper.width = 30 - expect = ['Did you say', - '"supercalifragilisticexpialidocious?"', - 'How *do* you spell that odd', - 'word, anyways?' - ] - result = self.wrapper.wrap(self.text) - self.check(result, expect) - - # Same thing with kwargs passed to standalone wrap() function. - result = wrap(self.text, width=30, break_long_words=0) - self.check(result, expect) - - -class IndentTestCases(BaseTestCase): - - # called before each test method - def setUp(self): - self.text = '''\ -This paragraph will be filled, first without any indentation, -and then with some (including a hanging indent).''' - - - def test_fill(self): - # Test the fill() method - - expect = '''\ -This paragraph will be filled, first -without any indentation, and then with -some (including a hanging indent).''' - - result = fill(self.text, 40) - self.check(result, expect) - - - def test_initial_indent(self): - # Test initial_indent parameter - - expect = [" This paragraph will be filled,", - "first without any indentation, and then", - "with some (including a hanging indent)."] - result = wrap(self.text, 40, initial_indent=" ") - self.check(result, expect) - - expect = "\n".join(expect) - result = fill(self.text, 40, initial_indent=" ") - self.check(result, expect) - - - def test_subsequent_indent(self): - # Test subsequent_indent parameter - - expect = '''\ - * This paragraph will be filled, first - without any indentation, and then - with some (including a hanging - indent).''' - - result = fill(self.text, 40, - initial_indent=" * ", subsequent_indent=" ") - self.check(result, expect) - - -# Despite the similar names, DedentTestCase is *not* the inverse -# of IndentTestCase! -class DedentTestCase(unittest.TestCase): - - def test_dedent_nomargin(self): - # No lines indented. - text = "Hello there.\nHow are you?\nOh good, I'm glad." - self.assertEquals(dedent(text), text) - - # Similar, with a blank line. - text = "Hello there.\n\nBoo!" - self.assertEquals(dedent(text), text) - - # Some lines indented, but overall margin is still zero. - text = "Hello there.\n This is indented." - self.assertEquals(dedent(text), text) - - # Again, add a blank line. - text = "Hello there.\n\n Boo!\n" - self.assertEquals(dedent(text), text) - - def test_dedent_even(self): - # All lines indented by two spaces. - text = " Hello there.\n How are ya?\n Oh good." - expect = "Hello there.\nHow are ya?\nOh good." - self.assertEquals(dedent(text), expect) - - # Same, with blank lines. - text = " Hello there.\n\n How are ya?\n Oh good.\n" - expect = "Hello there.\n\nHow are ya?\nOh good.\n" - self.assertEquals(dedent(text), expect) - - # Now indent one of the blank lines. - text = " Hello there.\n \n How are ya?\n Oh good.\n" - expect = "Hello there.\n\nHow are ya?\nOh good.\n" - self.assertEquals(dedent(text), expect) - - def test_dedent_uneven(self): - # Lines indented unevenly. - text = '''\ - def foo(): - while 1: - return foo - ''' - expect = '''\ -def foo(): - while 1: - return foo -''' - self.assertEquals(dedent(text), expect) - - # Uneven indentation with a blank line. - text = " Foo\n Bar\n\n Baz\n" - expect = "Foo\n Bar\n\n Baz\n" - self.assertEquals(dedent(text), expect) - - # Uneven indentation with a whitespace-only line. - text = " Foo\n Bar\n \n Baz\n" - expect = "Foo\n Bar\n\n Baz\n" - self.assertEquals(dedent(text), expect) - - - -def test_main(): - test_support.run_unittest(WrapTestCase, - LongWordTestCase, - IndentTestCases, - DedentTestCase) - -if __name__ == '__main__': - test_main() diff --git a/py/compat/textwrap.py b/py/compat/textwrap.py deleted file mode 100644 index 568e1fbcb..000000000 --- a/py/compat/textwrap.py +++ /dev/null @@ -1,357 +0,0 @@ -"""Text wrapping and filling. -""" - -# Copyright (C) 1999-2001 Gregory P. Ward. -# Copyright (C) 2002, 2003 Python Software Foundation. -# Written by Greg Ward - -__revision__ = "$Id: textwrap.py 36379 2007-01-09 16:55:49Z cfbolz $" - -import string, re - -# Do the right thing with boolean values for all known Python versions -# (so this module can be copied to projects that don't depend on Python -# 2.3, e.g. Optik and Docutils). -try: - True, False -except NameError: - (True, False) = (1, 0) - -__all__ = ['TextWrapper', 'wrap', 'fill'] - -# Hardcode the recognized whitespace characters to the US-ASCII -# whitespace characters. The main reason for doing this is that in -# ISO-8859-1, 0xa0 is non-breaking whitespace, so in certain locales -# that character winds up in string.whitespace. Respecting -# string.whitespace in those cases would 1) make textwrap treat 0xa0 the -# same as any other whitespace char, which is clearly wrong (it's a -# *non-breaking* space), 2) possibly cause problems with Unicode, -# since 0xa0 is not in range(128). -_whitespace = '\t\n\x0b\x0c\r ' - -class TextWrapper: - """ - Object for wrapping/filling text. The public interface consists of - the wrap() and fill() methods; the other methods are just there for - subclasses to override in order to tweak the default behaviour. - If you want to completely replace the main wrapping algorithm, - you'll probably have to override _wrap_chunks(). - - Several instance attributes control various aspects of wrapping: - width (default: 70) - the maximum width of wrapped lines (unless break_long_words - is false) - initial_indent (default: "") - string that will be prepended to the first line of wrapped - output. Counts towards the line's width. - subsequent_indent (default: "") - string that will be prepended to all lines save the first - of wrapped output; also counts towards each line's width. - expand_tabs (default: true) - Expand tabs in input text to spaces before further processing. - Each tab will become 1 .. 8 spaces, depending on its position in - its line. If false, each tab is treated as a single character. - replace_whitespace (default: true) - Replace all whitespace characters in the input text by spaces - after tab expansion. Note that if expand_tabs is false and - replace_whitespace is true, every tab will be converted to a - single space! - fix_sentence_endings (default: false) - Ensure that sentence-ending punctuation is always followed - by two spaces. Off by default because the algorithm is - (unavoidably) imperfect. - break_long_words (default: true) - Break words longer than 'width'. If false, those words will not - be broken, and some lines might be longer than 'width'. - """ - - whitespace_trans = string.maketrans(_whitespace, ' ' * len(_whitespace)) - - unicode_whitespace_trans = {} - uspace = ord(u' ') - for x in map(ord, _whitespace): - unicode_whitespace_trans[x] = uspace - - # This funky little regex is just the trick for splitting - # text up into word-wrappable chunks. E.g. - # "Hello there -- you goof-ball, use the -b option!" - # splits into - # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option! - # (after stripping out empty strings). - wordsep_re = re.compile( - r'(\s+|' # any whitespace - r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words - r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash - - # XXX this is not locale- or charset-aware -- string.lowercase - # is US-ASCII only (and therefore English-only) - sentence_end_re = re.compile(r'[%s]' # lowercase letter - r'[\.\!\?]' # sentence-ending punct. - r'[\"\']?' # optional end-of-quote - % string.lowercase) - - - def __init__(self, - width=70, - initial_indent="", - subsequent_indent="", - expand_tabs=True, - replace_whitespace=True, - fix_sentence_endings=False, - break_long_words=True): - self.width = width - self.initial_indent = initial_indent - self.subsequent_indent = subsequent_indent - self.expand_tabs = expand_tabs - self.replace_whitespace = replace_whitespace - self.fix_sentence_endings = fix_sentence_endings - self.break_long_words = break_long_words - - - # -- Private methods ----------------------------------------------- - # (possibly useful for subclasses to override) - - def _munge_whitespace(self, text): - """_munge_whitespace(text : string) -> string - - Munge whitespace in text: expand tabs and convert all other - whitespace characters to spaces. Eg. " foo\tbar\n\nbaz" - becomes " foo bar baz". - """ - if self.expand_tabs: - text = text.expandtabs() - if self.replace_whitespace: - if isinstance(text, str): - text = text.translate(self.whitespace_trans) - elif isinstance(text, unicode): - text = text.translate(self.unicode_whitespace_trans) - return text - - - def _split(self, text): - """_split(text : string) -> [string] - - Split the text to wrap into indivisible chunks. Chunks are - not quite the same as words; see wrap_chunks() for full - details. As an example, the text - Look, goof-ball -- use the -b option! - breaks into the following chunks: - 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ', - 'use', ' ', 'the', ' ', '-b', ' ', 'option!' - """ - chunks = self.wordsep_re.split(text) - chunks = filter(None, chunks) - return chunks - - def _fix_sentence_endings(self, chunks): - """_fix_sentence_endings(chunks : [string]) - - Correct for sentence endings buried in 'chunks'. Eg. when the - original text contains "... foo.\nBar ...", munge_whitespace() - and split() will convert that to [..., "foo.", " ", "Bar", ...] - which has one too few spaces; this method simply changes the one - space to two. - """ - i = 0 - pat = self.sentence_end_re - while i < len(chunks)-1: - if chunks[i+1] == " " and pat.search(chunks[i]): - chunks[i+1] = " " - i += 2 - else: - i += 1 - - def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): - """_handle_long_word(chunks : [string], - cur_line : [string], - cur_len : int, width : int) - - Handle a chunk of text (most likely a word, not whitespace) that - is too long to fit in any line. - """ - space_left = max(width - cur_len, 1) - - # If we're allowed to break long words, then do so: put as much - # of the next chunk onto the current line as will fit. - if self.break_long_words: - cur_line.append(reversed_chunks[-1][:space_left]) - reversed_chunks[-1] = reversed_chunks[-1][space_left:] - - # Otherwise, we have to preserve the long word intact. Only add - # it to the current line if there's nothing already there -- - # that minimizes how much we violate the width constraint. - elif not cur_line: - cur_line.append(reversed_chunks.pop()) - - # If we're not allowed to break long words, and there's already - # text on the current line, do nothing. Next time through the - # main loop of _wrap_chunks(), we'll wind up here again, but - # cur_len will be zero, so the next line will be entirely - # devoted to the long word that we can't handle right now. - - def _wrap_chunks(self, chunks): - """_wrap_chunks(chunks : [string]) -> [string] - - Wrap a sequence of text chunks and return a list of lines of - length 'self.width' or less. (If 'break_long_words' is false, - some lines may be longer than this.) Chunks correspond roughly - to words and the whitespace between them: each chunk is - indivisible (modulo 'break_long_words'), but a line break can - come between any two chunks. Chunks should not have internal - whitespace; ie. a chunk is either all whitespace or a "word". - Whitespace chunks will be removed from the beginning and end of - lines, but apart from that whitespace is preserved. - """ - lines = [] - if self.width <= 0: - raise ValueError("invalid width %r (must be > 0)" % self.width) - - # Arrange in reverse order so items can be efficiently popped - # from a stack of chucks. - chunks.reverse() - - while chunks: - - # Start the list of chunks that will make up the current line. - # cur_len is just the length of all the chunks in cur_line. - cur_line = [] - cur_len = 0 - - # Figure out which static string will prefix this line. - if lines: - indent = self.subsequent_indent - else: - indent = self.initial_indent - - # Maximum width for this line. - width = self.width - len(indent) - - # First chunk on line is whitespace -- drop it, unless this - # is the very beginning of the text (ie. no lines started yet). - if chunks[-1].strip() == '' and lines: - del chunks[-1] - - while chunks: - l = len(chunks[-1]) - - # Can at least squeeze this chunk onto the current line. - if cur_len + l <= width: - cur_line.append(chunks.pop()) - cur_len += l - - # Nope, this line is full. - else: - break - - # The current line is full, and the next chunk is too big to - # fit on *any* line (not just this one). - if chunks and len(chunks[-1]) > width: - self._handle_long_word(chunks, cur_line, cur_len, width) - - # If the last chunk on this line is all whitespace, drop it. - if cur_line and cur_line[-1].strip() == '': - del cur_line[-1] - - # Convert current line back to a string and store it in list - # of all lines (return value). - if cur_line: - lines.append(indent + ''.join(cur_line)) - - return lines - - - # -- Public interface ---------------------------------------------- - - def wrap(self, text): - """wrap(text : string) -> [string] - - Reformat the single paragraph in 'text' so it fits in lines of - no more than 'self.width' columns, and return a list of wrapped - lines. Tabs in 'text' are expanded with string.expandtabs(), - and all other whitespace characters (including newline) are - converted to space. - """ - text = self._munge_whitespace(text) - chunks = self._split(text) - if self.fix_sentence_endings: - self._fix_sentence_endings(chunks) - return self._wrap_chunks(chunks) - - def fill(self, text): - """fill(text : string) -> string - - Reformat the single paragraph in 'text' to fit in lines of no - more than 'self.width' columns, and return a new string - containing the entire wrapped paragraph. - """ - return "\n".join(self.wrap(text)) - - -# -- Convenience interface --------------------------------------------- - -def wrap(text, width=70, **kwargs): - """Wrap a single paragraph of text, returning a list of wrapped lines. - - Reformat the single paragraph in 'text' so it fits in lines of no - more than 'width' columns, and return a list of wrapped lines. By - default, tabs in 'text' are expanded with string.expandtabs(), and - all other whitespace characters (including newline) are converted to - space. See TextWrapper class for available keyword args to customize - wrapping behaviour. - """ - w = TextWrapper(width=width, **kwargs) - return w.wrap(text) - -def fill(text, width=70, **kwargs): - """Fill a single paragraph of text, returning a new string. - - Reformat the single paragraph in 'text' to fit in lines of no more - than 'width' columns, and return a new string containing the entire - wrapped paragraph. As with wrap(), tabs are expanded and other - whitespace characters converted to space. See TextWrapper class for - available keyword args to customize wrapping behaviour. - """ - w = TextWrapper(width=width, **kwargs) - return w.fill(text) - - -# -- Loosely related functionality ------------------------------------- - -def dedent(text): - """dedent(text : string) -> string - - Remove any whitespace than can be uniformly removed from the left - of every line in `text`. - - This can be used e.g. to make triple-quoted strings line up with - the left edge of screen/whatever, while still presenting it in the - source code in indented form. - - For example: - - def test(): - # end first line with \ to avoid the empty line! - s = '''\ - hello - world - ''' - print repr(s) # prints ' hello\n world\n ' - print repr(dedent(s)) # prints 'hello\n world\n' - """ - lines = text.expandtabs().split('\n') - margin = None - for line in lines: - content = line.lstrip() - if not content: - continue - indent = len(line) - len(content) - if margin is None: - margin = indent - else: - margin = min(margin, indent) - - if margin is not None and margin > 0: - for i in range(len(lines)): - lines[i] = lines[i][margin:] - - return '\n'.join(lines) diff --git a/py/execnet/register.py b/py/execnet/register.py index 29b517541..1ac85852d 100644 --- a/py/execnet/register.py +++ b/py/execnet/register.py @@ -1,12 +1,8 @@ import os, inspect, socket import sys -from py.magic import autopath ; mypath = autopath() - -try: - from subprocess import Popen, PIPE -except ImportError: - from py.__.compat.subprocess import Popen, PIPE +import py +from subprocess import Popen, PIPE import py if sys.platform == "win32": @@ -128,8 +124,9 @@ class SocketGateway(InstallableGateway): host, port = ('', 0) # XXX works on all platforms? else: host, port = hostport + mydir = py.path.local(__file__).dirpath() socketserverbootstrap = py.code.Source( - mypath.dirpath('script', 'socketserver.py').read('rU'), """ + mydir.join('script', 'socketserver.py').read('rU'), """ import socket sock = bind_and_listen((%r, %r)) port = sock.getsockname() diff --git a/py/log/testing/test_warning.py b/py/log/testing/test_warning.py index 638052875..6a0b586b9 100644 --- a/py/log/testing/test_warning.py +++ b/py/log/testing/test_warning.py @@ -27,6 +27,32 @@ def test_stacklevel(): warning = str(err) assert warning.find(":%s" % lno) != -1 +def test_stacklevel_initpkg_with_resolve(testdir): + mod = testdir.makepyfile(initpkg=""" + import py + def __getattr__(): + f() + def f(): + py.log._apiwarn("x", "some", stacklevel="initpkg") + """).pyimport() + capture = py.io.StdCapture() + mod.__getattr__() + out, err = capture.reset() + lno = test_stacklevel_initpkg_with_resolve.func_code.co_firstlineno + 9 + warning = str(err) + assert warning.find(":%s" % lno) != -1 + +def test_stacklevel_initpkg_no_resolve(): + def f(): + py.log._apiwarn("x", "some", stacklevel="initpkg") + capture = py.io.StdCapture() + f() + out, err = capture.reset() + lno = test_stacklevel_initpkg_no_resolve.func_code.co_firstlineno + 2 + warning = str(err) + assert warning.find(":%s" % lno) != -1 + + def test_function(): capture = py.io.StdCapture() py.log._apiwarn("x.y.z", "something", function=test_function) diff --git a/py/log/warning.py b/py/log/warning.py index 74a7a94a0..5cb0cad53 100644 --- a/py/log/warning.py +++ b/py/log/warning.py @@ -13,6 +13,18 @@ class Warning(py.std.exceptions.DeprecationWarning): def _apiwarn(startversion, msg, stacklevel=1, function=None): # below is mostly COPIED from python2.4/warnings.py's def warn() # Get context information + if stacklevel == "initpkg": + frame = sys._getframe(1) + level = 2 + while frame: + co = frame.f_code + if co.co_name == "__getattr__" and co.co_filename.find("initpkg") !=-1: + stacklevel = level + break + level += 1 + frame = frame.f_back + else: + stacklevel = 1 msg = "%s (since version %s)" %(msg, startversion) warn(msg, stacklevel=stacklevel+1, function=function) diff --git a/py/path/local.py b/py/path/local.py index 45b445c28..5d5a84a61 100644 --- a/py/path/local.py +++ b/py/path/local.py @@ -165,6 +165,8 @@ class LocalPath(FSBase): try: import hashlib as mod except ImportError: + if hashtype == "sha1": + hashtype = "sha" mod = __import__(hashtype) hash = getattr(mod, hashtype)() except (AttributeError, ImportError): @@ -534,7 +536,7 @@ class LocalPath(FSBase): to be executed. Note that this process is directly invoked and not through a system shell. """ - from py.compat.subprocess import Popen, PIPE + from subprocess import Popen, PIPE argv = map(str, argv) proc = Popen([str(self)] + list(argv), stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate() diff --git a/py/test/config.py b/py/test/config.py index 3d25fa45f..a3a2cd3f5 100644 --- a/py/test/config.py +++ b/py/test/config.py @@ -23,7 +23,7 @@ class Config(object): """ test configuration object, provides access to config valueso, the pluginmanager and plugin api. """ - Option = py.compat.optparse.Option # deprecated + Option = py.std.optparse.Option Error = Error basetemp = None _sessionclass = None diff --git a/py/test/parseopt.py b/py/test/parseopt.py index 8a660849b..d3a084894 100644 --- a/py/test/parseopt.py +++ b/py/test/parseopt.py @@ -14,7 +14,7 @@ for options. basic usage: """ import py -from py.compat import optparse +import optparse class Parser: """ Parser for command line arguments. """ @@ -76,11 +76,11 @@ class OptionGroup: def addoption(self, *optnames, **attrs): """ add an option to this group. """ - option = py.compat.optparse.Option(*optnames, **attrs) + option = optparse.Option(*optnames, **attrs) self._addoption_instance(option, shortupper=False) def _addoption(self, *optnames, **attrs): - option = py.compat.optparse.Option(*optnames, **attrs) + option = optparse.Option(*optnames, **attrs) self._addoption_instance(option, shortupper=True) def _addoption_instance(self, option, shortupper=False): diff --git a/py/test/plugin/pytest_doctest.py b/py/test/plugin/pytest_doctest.py index bcfe1baee..cad5616f0 100644 --- a/py/test/plugin/pytest_doctest.py +++ b/py/test/plugin/pytest_doctest.py @@ -15,6 +15,7 @@ as well. import py from py.__.code.code import TerminalRepr, ReprFileLocation +import doctest def pytest_addoption(parser): group = parser.addgroup("doctest options") @@ -46,7 +47,7 @@ class DoctestItem(py.test.collect.Item): self.fspath = path def repr_failure(self, excinfo): - if excinfo.errisinstance(py.compat.doctest.DocTestFailure): + if excinfo.errisinstance(doctest.DocTestFailure): doctestfailure = excinfo.value example = doctestfailure.example test = doctestfailure.test @@ -54,8 +55,8 @@ class DoctestItem(py.test.collect.Item): lineno = test.lineno + example.lineno + 1 message = excinfo.type.__name__ reprlocation = ReprFileLocation(filename, lineno, message) - checker = py.compat.doctest.OutputChecker() - REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF + checker = doctest.OutputChecker() + REPORT_UDIFF = doctest.REPORT_UDIFF filelines = py.path.local(filename).readlines(cr=0) i = max(test.lineno, max(0, lineno - 10)) # XXX? lines = [] @@ -65,7 +66,7 @@ class DoctestItem(py.test.collect.Item): lines += checker.output_difference(example, doctestfailure.got, REPORT_UDIFF).split("\n") return ReprFailDoctest(reprlocation, lines) - elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): + elif excinfo.errisinstance(doctest.UnexpectedException): excinfo = py.code.ExceptionInfo(excinfo.value.exc_info) return super(DoctestItem, self).repr_failure(excinfo) else: @@ -74,14 +75,14 @@ class DoctestItem(py.test.collect.Item): class DoctestTextfile(DoctestItem): def runtest(self): if not self._deprecated_testexecution(): - failed, tot = py.compat.doctest.testfile( + failed, tot = doctest.testfile( str(self.fspath), module_relative=False, raise_on_error=True, verbose=0) class DoctestModule(DoctestItem): def runtest(self): module = self.fspath.pyimport() - failed, tot = py.compat.doctest.testmod( + failed, tot = doctest.testmod( module, raise_on_error=True, verbose=0) diff --git a/py/test/plugin/pytest_restdoc.py b/py/test/plugin/pytest_restdoc.py index a0d05c57e..c6daf508f 100644 --- a/py/test/plugin/pytest_restdoc.py +++ b/py/test/plugin/pytest_restdoc.py @@ -231,7 +231,7 @@ class DoctestText(py.test.collect.Item): l.append(line) docstring = "\n".join(l) mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) + failed, tot = py.std.doctest.testmod(mod, verbose=1) if failed: py.test.fail("doctest %s: %s failed out of %s" %( self.fspath, failed, tot)) diff --git a/py/test/testing/test_parseopt.py b/py/test/testing/test_parseopt.py index bd1dc2023..50ee7b90c 100644 --- a/py/test/testing/test_parseopt.py +++ b/py/test/testing/test_parseopt.py @@ -1,5 +1,5 @@ import py -from py.__.test import parseopt +from py.__.test import parseopt class TestParser: def test_init(self, capsys): @@ -30,7 +30,7 @@ class TestParser: group = parseopt.OptionGroup("hello") group.addoption("--option1", action="store_true") assert len(group.options) == 1 - assert isinstance(group.options[0], py.compat.optparse.Option) + assert isinstance(group.options[0], parseopt.optparse.Option) def test_group_shortopt_lowercase(self): parser = parseopt.Parser() diff --git a/py/test/testing/test_recording.py b/py/test/testing/test_recording.py index b6f1fe5e2..8c143d933 100644 --- a/py/test/testing/test_recording.py +++ b/py/test/testing/test_recording.py @@ -1,8 +1,8 @@ import py,sys -py.test.skip("implementation missing: recording") class TestRecordingAccept: def test_recording_and_back(self, testdir): + py.test.skip("implementation missing: recording") p = testdir.makepyfile(""" import py def test_fail():