[py-svn] r57773 - in py/branch/py-compat-2.5.2: . contrib contrib/py_unittest py py/apigen/tracer py/code/testing py/compat py/compat/testing py/green py/test py/test/dsession/testing py/test/report py/test/report/testing py/test/testing
hpk at codespeak.net
hpk at codespeak.net
Tue Sep 2 21:40:23 CEST 2008
Author: hpk
Date: Tue Sep 2 21:40:21 2008
New Revision: 57773
Added:
py/branch/py-compat-2.5.2/
- copied from r57745, py/trunk/
py/branch/py-compat-2.5.2/MANIFEST
- copied unchanged from r57755, py/trunk/MANIFEST
py/branch/py-compat-2.5.2/README.txt
- copied unchanged from r57755, py/trunk/README.txt
py/branch/py-compat-2.5.2/contrib/
- copied from r57756, py/trunk/contrib/
py/branch/py-compat-2.5.2/contrib/py_unittest/
- copied from r57771, py/trunk/contrib/py_unittest/
py/branch/py-compat-2.5.2/py/
- copied from r57755, py/trunk/py/
py/branch/py-compat-2.5.2/py/code/testing/test_excinfo.py
- copied unchanged from r57757, py/trunk/py/code/testing/test_excinfo.py
py/branch/py-compat-2.5.2/py/compat/conftest.pyc (contents, props changed)
py/branch/py-compat-2.5.2/py/compat/linecache.py (contents, props changed)
py/branch/py-compat-2.5.2/py/compat/pdb.py (contents, props changed)
py/branch/py-compat-2.5.2/py/test/dsession/testing/test_functional_dsession.py
- copied unchanged from r57758, py/trunk/py/test/dsession/testing/test_functional_dsession.py
py/branch/py-compat-2.5.2/py/test/dsession/testing/test_hostmanage.py
- copied unchanged from r57758, py/trunk/py/test/dsession/testing/test_hostmanage.py
py/branch/py-compat-2.5.2/py/test/dsession/testing/test_masterslave.py
- copied unchanged from r57758, py/trunk/py/test/dsession/testing/test_masterslave.py
py/branch/py-compat-2.5.2/py/test/pycollect.py
- copied unchanged from r57762, py/trunk/py/test/pycollect.py
py/branch/py-compat-2.5.2/py/test/report/base.py
- copied unchanged from r57762, py/trunk/py/test/report/base.py
py/branch/py-compat-2.5.2/py/test/report/terminal.py
- copied unchanged from r57762, py/trunk/py/test/report/terminal.py
py/branch/py-compat-2.5.2/py/test/report/testing/test_basereporter.py
- copied unchanged from r57762, py/trunk/py/test/report/testing/test_basereporter.py
py/branch/py-compat-2.5.2/py/test/testing/suptest.py
- copied unchanged from r57772, py/trunk/py/test/testing/suptest.py
py/branch/py-compat-2.5.2/py/test/testing/test_collect.py
- copied unchanged from r57762, py/trunk/py/test/testing/test_collect.py
py/branch/py-compat-2.5.2/py/test/testing/test_config.py
- copied unchanged from r57762, py/trunk/py/test/testing/test_config.py
py/branch/py-compat-2.5.2/py/test/testing/test_event.py
- copied unchanged from r57762, py/trunk/py/test/testing/test_event.py
py/branch/py-compat-2.5.2/py/test/testing/test_session.py
- copied unchanged from r57762, py/trunk/py/test/testing/test_session.py
py/branch/py-compat-2.5.2/setup.py
- copied unchanged from r57755, py/trunk/setup.py
Removed:
py/branch/py-compat-2.5.2/py/compat/conftest.py
py/branch/py-compat-2.5.2/py/green/
py/branch/py-compat-2.5.2/py/test/dsession/testing/basetest.py
py/branch/py-compat-2.5.2/py/test/testing/setupdata.py
py/branch/py-compat-2.5.2/py/test/testing/test_repevent.py
Modified:
py/branch/py-compat-2.5.2/py/__init__.py
py/branch/py-compat-2.5.2/py/apigen/tracer/description.py
py/branch/py-compat-2.5.2/py/compat/doctest.py
py/branch/py-compat-2.5.2/py/compat/optparse.py
py/branch/py-compat-2.5.2/py/compat/subprocess.py
py/branch/py-compat-2.5.2/py/compat/testing/test_doctest.py
py/branch/py-compat-2.5.2/py/compat/testing/test_doctest2.py
py/branch/py-compat-2.5.2/py/compat/testing/test_optparse.py
py/branch/py-compat-2.5.2/py/compat/testing/test_textwrap.py
py/branch/py-compat-2.5.2/py/compat/textwrap.py
Log:
branch to use 2.5.2 compat modules,
needs more work and fails on py/doc/apigen.txt text
which probably is an apigen bug.
Modified: py/branch/py-compat-2.5.2/py/__init__.py
==============================================================================
--- py/trunk/py/__init__.py (original)
+++ py/branch/py-compat-2.5.2/py/__init__.py Tue Sep 2 21:40:21 2008
@@ -176,11 +176,12 @@
'log.Syslog' : ('./log/consumer.py', 'Syslog'),
'log.get' : ('./log/logger.py', 'get'),
- # compatibility modules (taken from 2.4.4)
+ # compatibility modules (taken from 2.5.2)
'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.pdb' : ('./compat/pdb.py', '*'),
+ #'compat.subprocess' : ('./compat/subprocess.py', '*'),
})
Modified: py/branch/py-compat-2.5.2/py/apigen/tracer/description.py
==============================================================================
--- py/trunk/py/apigen/tracer/description.py (original)
+++ py/branch/py-compat-2.5.2/py/apigen/tracer/description.py Tue Sep 2 21:40:21 2008
@@ -78,6 +78,8 @@
if upward_frame:
if hasattr(upward_frame, 'raw'):
upward_frame = upward_frame.raw
+ assert upward_frame in stack, (upward_frame, stack)
+ assert frame in stack, (frame, stack)
lst = [py.code.Frame(i) for i in stack[stack.index(frame):\
stack.index(upward_frame)+1]]
if len(lst) > 1:
Deleted: /py/trunk/py/compat/conftest.py
==============================================================================
--- /py/trunk/py/compat/conftest.py Tue Sep 2 21:40:21 2008
+++ (empty file)
@@ -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")
Added: py/branch/py-compat-2.5.2/py/compat/conftest.pyc
==============================================================================
Binary file. No diff available.
Modified: py/branch/py-compat-2.5.2/py/compat/doctest.py
==============================================================================
--- py/trunk/py/compat/doctest.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/doctest.py Tue Sep 2 21:40:21 2008
@@ -54,6 +54,7 @@
'DONT_ACCEPT_BLANKLINE',
'NORMALIZE_WHITESPACE',
'ELLIPSIS',
+ 'SKIP',
'IGNORE_EXCEPTION_DETAIL',
'COMPARISON_FLAGS',
'REPORT_UDIFF',
@@ -62,7 +63,6 @@
'REPORT_ONLY_FIRST_FAILURE',
'REPORTING_FLAGS',
# 1. Utility Functions
- 'is_private',
# 2. Example & DocTest
'Example',
'DocTest',
@@ -95,16 +95,11 @@
import __future__
-import sys, traceback, inspect, linecache, os, re, types
+import sys, traceback, inspect, linecache, os, re
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 <source, want> pair, plus an intra-docstring line number.
# - DocTest: a collection of examples, parsed from a docstring, plus
@@ -135,12 +130,14 @@
DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE')
NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE')
ELLIPSIS = register_optionflag('ELLIPSIS')
+SKIP = register_optionflag('SKIP')
IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL')
COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 |
DONT_ACCEPT_BLANKLINE |
NORMALIZE_WHITESPACE |
ELLIPSIS |
+ SKIP |
IGNORE_EXCEPTION_DETAIL)
REPORT_UDIFF = register_optionflag('REPORT_UDIFF')
@@ -175,35 +172,6 @@
## 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
@@ -235,6 +203,18 @@
else:
raise TypeError("Expected a module, string, or None")
+def _load_testfile(filename, package, module_relative):
+ if module_relative:
+ package = _normalize_module(package, 3)
+ filename = _module_relative_path(package, filename)
+ if hasattr(package, '__loader__'):
+ if hasattr(package.__loader__, 'get_data'):
+ file_contents = package.__loader__.get_data(filename)
+ # get_data() opens files as 'rb', so one must do the equivalent
+ # conversion as universal newlines would do.
+ return file_contents.replace(os.linesep, '\n'), filename
+ return open(filename).read(), filename
+
def _indent(s, indent=4):
"""
Add the given number of space characters to the beginning every
@@ -340,7 +320,20 @@
"""
def __init__(self, out):
self.__out = out
- pdb.Pdb.__init__(self)
+ self.__debugger_used = False
+ pdb.Pdb.__init__(self, stdout=out)
+
+ def set_trace(self, frame=None):
+ self.__debugger_used = True
+ if frame is None:
+ frame = sys._getframe().f_back
+ pdb.Pdb.set_trace(self, frame)
+
+ def set_continue(self):
+ # Calling set_continue unconditionally would break unit test
+ # coverage reporting, as Bdb.set_continue calls sys.settrace(None).
+ if self.__debugger_used:
+ pdb.Pdb.set_continue(self)
def trace_dispatch(self, *args):
# Redirect stdout to the given stream.
@@ -747,7 +740,7 @@
"""
def __init__(self, verbose=False, parser=DocTestParser(),
- recurse=True, _namefilter=None, exclude_empty=True):
+ recurse=True, exclude_empty=True):
"""
Create a new doctest finder.
@@ -767,12 +760,8 @@
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):
+ 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'
@@ -855,13 +844,6 @@
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
@@ -903,9 +885,6 @@
# 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
@@ -934,9 +913,6 @@
# 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)
@@ -1229,6 +1205,10 @@
else:
self.optionflags &= ~optionflag
+ # If 'SKIP' is set, then skip this example.
+ if self.optionflags & SKIP:
+ continue
+
# Record that we started this example.
tries += 1
if not quiet:
@@ -1324,13 +1304,13 @@
__LINECACHE_FILENAME_RE = re.compile(r'<doctest '
r'(?P<name>[\w\.]+)'
r'\[(?P<examplenum>\d+)\]>$')
- def __patched_linecache_getlines(self, filename, additional_arg=None):
+ def __patched_linecache_getlines(self, filename, module_globals=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)
+ return self.save_linecache_getlines(filename, module_globals)
def run(self, test, compileflags=None, out=None, clear_globs=True):
"""
@@ -1597,7 +1577,7 @@
- test: the DocTest object being run
- - excample: the Example object that failed
+ - example: the Example object that failed
- got: the actual output
"""
@@ -1616,7 +1596,7 @@
- test: the DocTest object being run
- - excample: the Example object that failed
+ - example: the Example object that failed
- exc_info: the exception info
"""
@@ -1740,17 +1720,16 @@
# class, updated by testmod.
master = None
-def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None,
+def testmod(m=None, name=None, globs=None, verbose=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,
+ """m=None, name=None, globs=None, verbose=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.
+ with m.__doc__.
Also test examples reachable from dict m.__test__ if it exists and is
not None. m.__test__ maps names to functions, classes and strings;
@@ -1788,6 +1767,7 @@
DONT_ACCEPT_BLANKLINE
NORMALIZE_WHITESPACE
ELLIPSIS
+ SKIP
IGNORE_EXCEPTION_DETAIL
REPORT_UDIFF
REPORT_CDIFF
@@ -1798,13 +1778,6 @@
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
@@ -1815,11 +1788,6 @@
"""
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
@@ -1836,7 +1804,7 @@
name = m.__name__
# Find, parse, and run all tests in the given module.
- finder = DocTestFinder(_namefilter=isprivate, exclude_empty=exclude_empty)
+ finder = DocTestFinder(exclude_empty=exclude_empty)
if raise_on_error:
runner = DebugRunner(verbose=verbose, optionflags=optionflags)
@@ -1858,7 +1826,8 @@
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()):
+ extraglobs=None, raise_on_error=False, parser=DocTestParser(),
+ encoding=None):
"""
Test examples in the given file. Return (#failures, #tests).
@@ -1910,6 +1879,7 @@
DONT_ACCEPT_BLANKLINE
NORMALIZE_WHITESPACE
ELLIPSIS
+ SKIP
IGNORE_EXCEPTION_DETAIL
REPORT_UDIFF
REPORT_CDIFF
@@ -1923,6 +1893,9 @@
Optional keyword arg "parser" specifies a DocTestParser (or
subclass) that should be used to extract tests from the files.
+ Optional keyword arg "encoding" specifies an encoding that should
+ be used to convert the file to unicode.
+
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
@@ -1938,9 +1911,7 @@
"relative paths.")
# Relativize the path
- if module_relative:
- package = _normalize_module(package)
- filename = _module_relative_path(package, filename)
+ text, filename = _load_testfile(filename, package, module_relative)
# If no name was given, then use the file's name.
if name is None:
@@ -1959,9 +1930,11 @@
else:
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
+ if encoding is not None:
+ text = text.decode(encoding)
+
# Read the file, convert it to a test, and run it.
- s = open(filename).read()
- test = parser.get_doctest(s, globs, name, filename, 0)
+ test = parser.get_doctest(text, globs, name, filename, 0)
runner.run(test)
if report:
@@ -2004,8 +1977,7 @@
# actually used in any way.
class Tester:
- def __init__(self, mod=None, globs=None, verbose=None,
- isprivate=None, optionflags=0):
+ def __init__(self, mod=None, globs=None, verbose=None, optionflags=0):
warnings.warn("class Tester is deprecated; "
"use class doctest.DocTestRunner instead",
@@ -2020,9 +1992,8 @@
self.globs = globs
self.verbose = verbose
- self.isprivate = isprivate
self.optionflags = optionflags
- self.testfinder = DocTestFinder(_namefilter=isprivate)
+ self.testfinder = DocTestFinder()
self.testrunner = DocTestRunner(verbose=verbose,
optionflags=optionflags)
@@ -2330,22 +2301,29 @@
)
def DocFileTest(path, module_relative=True, package=None,
- globs=None, parser=DocTestParser(), **options):
+ globs=None, parser=DocTestParser(),
+ encoding=None, **options):
if globs is None:
globs = {}
+ else:
+ globs = globs.copy()
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)
+ doc, path = _load_testfile(path, package, module_relative)
+
+ if "__file__" not in globs:
+ globs["__file__"] = path
# Find the file and read it.
name = os.path.basename(path)
- doc = open(path).read()
+
+ # If an encoding is specified, use it to convert the file to unicode
+ if encoding is not None:
+ doc = doc.decode(encoding)
# Convert it to a test, and wrap it in a DocFileCase.
test = parser.get_doctest(doc, globs, name, path, 0)
@@ -2403,6 +2381,9 @@
parser
A DocTestParser (or subclass) that should be used to extract
tests from the files.
+
+ encoding
+ An encoding that will be used to convert the files to unicode.
"""
suite = unittest.TestSuite()
Added: py/branch/py-compat-2.5.2/py/compat/linecache.py
==============================================================================
--- (empty file)
+++ py/branch/py-compat-2.5.2/py/compat/linecache.py Tue Sep 2 21:40:21 2008
@@ -0,0 +1,136 @@
+"""Cache lines from files.
+
+This is intended to read lines from modules imported -- hence if a filename
+is not found, it will look down the module search path for a file by
+that name.
+"""
+
+import sys
+import os
+
+__all__ = ["getline", "clearcache", "checkcache"]
+
+def getline(filename, lineno, module_globals=None):
+ lines = getlines(filename, module_globals)
+ if 1 <= lineno <= len(lines):
+ return lines[lineno-1]
+ else:
+ return ''
+
+
+# The cache
+
+cache = {} # The cache
+
+
+def clearcache():
+ """Clear the cache entirely."""
+
+ global cache
+ cache = {}
+
+
+def getlines(filename, module_globals=None):
+ """Get the lines for a file from the cache.
+ Update the cache if it doesn't contain an entry for this file already."""
+
+ if filename in cache:
+ return cache[filename][2]
+ else:
+ return updatecache(filename, module_globals)
+
+
+def checkcache(filename=None):
+ """Discard cache entries that are out of date.
+ (This is not checked upon each call!)"""
+
+ if filename is None:
+ filenames = cache.keys()
+ else:
+ if filename in cache:
+ filenames = [filename]
+ else:
+ return
+
+ for filename in filenames:
+ size, mtime, lines, fullname = cache[filename]
+ if mtime is None:
+ continue # no-op for files loaded via a __loader__
+ try:
+ stat = os.stat(fullname)
+ except os.error:
+ del cache[filename]
+ continue
+ if size != stat.st_size or mtime != stat.st_mtime:
+ del cache[filename]
+
+
+def updatecache(filename, module_globals=None):
+ """Update a cache entry and return its list of lines.
+ If something's wrong, print a message, discard the cache entry,
+ and return an empty list."""
+
+ if filename in cache:
+ del cache[filename]
+ if not filename or filename[0] + filename[-1] == '<>':
+ return []
+
+ fullname = filename
+ try:
+ stat = os.stat(fullname)
+ except os.error, msg:
+ basename = os.path.split(filename)[1]
+
+ # Try for a __loader__, if available
+ if module_globals and '__loader__' in module_globals:
+ name = module_globals.get('__name__')
+ loader = module_globals['__loader__']
+ get_source = getattr(loader, 'get_source', None)
+
+ if name and get_source:
+ if basename.startswith(name.split('.')[-1]+'.'):
+ try:
+ data = get_source(name)
+ except (ImportError, IOError):
+ pass
+ else:
+ if data is None:
+ # No luck, the PEP302 loader cannot find the source
+ # for this module.
+ return []
+ cache[filename] = (
+ len(data), None,
+ [line+'\n' for line in data.splitlines()], fullname
+ )
+ return cache[filename][2]
+
+ # Try looking through the module search path.
+
+ for dirname in sys.path:
+ # When using imputil, sys.path may contain things other than
+ # strings; ignore them when it happens.
+ try:
+ fullname = os.path.join(dirname, basename)
+ except (TypeError, AttributeError):
+ # Not sufficiently string-like to do anything useful with.
+ pass
+ else:
+ try:
+ stat = os.stat(fullname)
+ break
+ except os.error:
+ pass
+ else:
+ # No luck
+## print '*** Cannot stat', filename, ':', msg
+ return []
+ try:
+ fp = open(fullname, 'rU')
+ lines = fp.readlines()
+ fp.close()
+ except IOError, msg:
+## print '*** Cannot open', fullname, ':', msg
+ return []
+ size, mtime = stat.st_size, stat.st_mtime
+ cache[filename] = size, mtime, lines, fullname
+ return lines
Modified: py/branch/py-compat-2.5.2/py/compat/optparse.py
==============================================================================
--- py/trunk/py/compat/optparse.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/optparse.py Tue Sep 2 21:40:21 2008
@@ -16,7 +16,7 @@
# Python developers: please do not make changes to this file, since
# it is automatically generated from the Optik source code.
-__version__ = "1.5a2"
+__version__ = "1.5.3"
__all__ = ['Option',
'SUPPRESS_HELP',
@@ -35,8 +35,8 @@
'BadOptionError']
__copyright__ = """
-Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved.
-Copyright (c) 2002-2004 Python Software Foundation. All rights reserved.
+Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved.
+Copyright (c) 2002-2006 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
@@ -69,17 +69,24 @@
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
+# Id: option_parser.py 527 2006-07-23 15:21:30Z greg
+# Id: option.py 522 2006-06-11 16:22:03Z gward
+# Id: help.py 527 2006-07-23 15:21:30Z greg
+# Id: errors.py 509 2006-04-20 00:58:24Z gward
+
+try:
+ from gettext import gettext
+except ImportError:
+ def gettext(message):
+ return message
+_ = gettext
+
class OptParseError (Exception):
def __init__(self, msg):
@@ -118,8 +125,25 @@
class BadOptionError (OptParseError):
"""
- Raised if an invalid or ambiguous option is seen on the command-line.
+ Raised if an invalid option is seen on the command line.
+ """
+ def __init__(self, opt_str):
+ self.opt_str = opt_str
+
+ def __str__(self):
+ return _("no such option: %s") % self.opt_str
+
+class AmbiguousOptionError (BadOptionError):
+ """
+ Raised if an ambiguous option is seen on the command line.
"""
+ def __init__(self, opt_str, possibilities):
+ BadOptionError.__init__(self, opt_str)
+ self.possibilities = possibilities
+
+ def __str__(self):
+ return (_("ambiguous option: %s (%s?)")
+ % (self.opt_str, ", ".join(self.possibilities)))
class HelpFormatter:
@@ -221,15 +245,30 @@
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
+ def _format_text(self, text):
+ """
+ Format a paragraph of free-form text for inclusion in the
+ help output at the current indentation level.
+ """
+ text_width = self.width - self.current_indent
indent = " "*self.current_indent
- return textwrap.fill(description,
- desc_width,
+ return textwrap.fill(text,
+ text_width,
initial_indent=indent,
- subsequent_indent=indent) + "\n"
+ subsequent_indent=indent)
+
+ def format_description(self, description):
+ if description:
+ return self._format_text(description) + "\n"
+ else:
+ return ""
+
+ def format_epilog(self, epilog):
+ if epilog:
+ return "\n" + self._format_text(epilog) + "\n"
+ else:
+ return ""
+
def expand_default(self, option):
if self.parser is None or not self.default_tag:
@@ -326,7 +365,7 @@
self, indent_increment, max_help_position, width, short_first)
def format_usage(self, usage):
- return _("usage: %s\n") % usage
+ return _("Usage: %s\n") % usage
def format_heading(self, heading):
return "%*s%s:\n" % (self.current_indent, "", heading)
@@ -351,8 +390,27 @@
return "%s\n%s\n" % (heading, "=-"[self.level] * len(heading))
-_builtin_cvt = { "int" : (int, _("integer")),
- "long" : (long, _("long integer")),
+def _parse_num(val, type):
+ if val[:2].lower() == "0x": # hexadecimal
+ radix = 16
+ elif val[:2].lower() == "0b": # binary
+ radix = 2
+ val = val[2:] or "0" # have to remove "0b" prefix
+ elif val[:1] == "0": # octal
+ radix = 8
+ else: # decimal
+ radix = 10
+
+ return type(val, radix)
+
+def _parse_int(val):
+ return _parse_num(val, int)
+
+def _parse_long(val):
+ return _parse_num(val, long)
+
+_builtin_cvt = { "int" : (_parse_int, _("integer")),
+ "long" : (_parse_long, _("long integer")),
"float" : (float, _("floating-point")),
"complex" : (complex, _("complex")) }
@@ -420,6 +478,7 @@
"store_true",
"store_false",
"append",
+ "append_const",
"count",
"callback",
"help",
@@ -433,6 +492,7 @@
"store_true",
"store_false",
"append",
+ "append_const",
"count")
# The set of actions for which it makes sense to supply a value
@@ -446,6 +506,10 @@
ALWAYS_TYPED_ACTIONS = ("store",
"append")
+ # The set of actions which take a 'const' attribute.
+ CONST_ACTIONS = ("store_const",
+ "append_const")
+
# The set of known types for option parsers. Again, listed here for
# constructor argument validation.
TYPES = ("string", "int", "long", "float", "complex", "choice")
@@ -572,9 +636,17 @@
# 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:
+ # Allow type objects or builtin type conversion functions
+ # (int, str, etc.) as an alternative to their names. (The
+ # complicated check of __builtin__ is only necessary for
+ # Python 2.1 and earlier, and is short-circuited by the
+ # first check on modern Pythons.)
+ import __builtin__
+ if ( type(self.type) is types.TypeType or
+ (hasattr(self.type, "__name__") and
+ getattr(__builtin__, self.type.__name__, None) is self.type) ):
self.type = self.type.__name__
+
if self.type == "str":
self.type = "string"
@@ -613,7 +685,7 @@
self.dest = self._short_opts[0][1]
def _check_const(self):
- if self.action != "store_const" and self.const is not None:
+ if self.action not in self.CONST_ACTIONS and self.const is not None:
raise OptionError(
"'const' must not be supplied for action %r" % self.action,
self)
@@ -720,6 +792,8 @@
setattr(values, dest, False)
elif action == "append":
values.ensure_value(dest, []).append(value)
+ elif action == "append_const":
+ values.ensure_value(dest, []).append(self.const)
elif action == "count":
setattr(values, dest, values.ensure_value(dest, 0) + 1)
elif action == "callback":
@@ -748,10 +822,15 @@
True, False
except NameError:
(True, False) = (1, 0)
+
try:
basestring
except NameError:
- basestring = (str, unicode)
+ def isbasestring(x):
+ return isinstance(x, (types.StringType, types.UnicodeType))
+else:
+ def isbasestring(x):
+ return isinstance(x, basestring)
class Values:
@@ -766,16 +845,13 @@
__repr__ = _repr
- def __eq__(self, other):
+ def __cmp__(self, other):
if isinstance(other, Values):
- return self.__dict__ == other.__dict__
- elif isinstance(other, dict):
- return self.__dict__ == other
+ return cmp(self.__dict__, other.__dict__)
+ elif isinstance(other, types.DictType):
+ return cmp(self.__dict__, other)
else:
- return False
-
- def __ne__(self, other):
- return not (self == other)
+ return -1
def _update_careful(self, dict):
"""
@@ -893,6 +969,13 @@
return self.description
+ def destroy(self):
+ """see OptionParser.destroy()."""
+ del self._short_opt
+ del self._long_opt
+ del self.defaults
+
+
# -- Option-adding methods -----------------------------------------
def _check_conflict(self, option):
@@ -1018,6 +1101,11 @@
def set_title(self, title):
self.title = title
+ def destroy(self):
+ """see OptionParser.destroy()."""
+ OptionContainer.destroy(self)
+ del self.option_list
+
# -- Help-formatting methods ---------------------------------------
def format_help(self, formatter):
@@ -1044,6 +1132,8 @@
prog : string
the name of the current program (to override
os.path.basename(sys.argv[0])).
+ epilog : string
+ paragraph of help text to print after option help
option_groups : [OptionGroup]
list of option groups in this parser (option groups are
@@ -1102,7 +1192,8 @@
description=None,
formatter=None,
add_help_option=True,
- prog=None):
+ prog=None,
+ epilog=None):
OptionContainer.__init__(
self, option_class, conflict_handler, description)
self.set_usage(usage)
@@ -1114,6 +1205,7 @@
formatter = IndentedHelpFormatter()
self.formatter = formatter
self.formatter.set_parser(self)
+ self.epilog = epilog
# Populate the option list; initial sources are the
# standard_option_list class attribute, the 'option_list'
@@ -1124,6 +1216,22 @@
self._init_parsing_state()
+
+ def destroy(self):
+ """
+ Declare that you are done with this OptionParser. This cleans up
+ reference cycles so the OptionParser (and all objects referenced by
+ it) can be garbage-collected promptly. After calling destroy(), the
+ OptionParser is unusable.
+ """
+ OptionContainer.destroy(self)
+ for group in self.option_groups:
+ group.destroy()
+ del self.option_list
+ del self.option_groups
+ del self.formatter
+
+
# -- Private methods -----------------------------------------------
# (used by our or OptionContainer's constructor)
@@ -1167,7 +1275,7 @@
elif usage is SUPPRESS_USAGE:
self.usage = None
# For backwards compatibility with Optik 1.3 and earlier.
- elif usage.startswith("usage:" + " "):
+ elif usage.lower().startswith("usage: "):
self.usage = usage[7:]
else:
self.usage = usage
@@ -1201,7 +1309,7 @@
defaults = self.defaults.copy()
for option in self._get_all_options():
default = defaults.get(option.dest)
- if isinstance(default, basestring):
+ if isbasestring(default):
opt_str = option.get_opt_string()
defaults[option.dest] = option.check_value(opt_str, default)
@@ -1276,7 +1384,7 @@
try:
stop = self._process_args(largs, rargs, values)
except (BadOptionError, OptionValueError), err:
- self.error(err.msg)
+ self.error(str(err))
args = largs + rargs
return self.check_values(values, args)
@@ -1401,7 +1509,7 @@
i += 1 # we have consumed a character
if not option:
- self.error(_("no such option: %s") % opt)
+ raise BadOptionError(opt)
if option.takes_value():
# Any characters left in arg? Pretend they're the
# next arg, and stop consuming characters of arg.
@@ -1501,7 +1609,7 @@
formatter = self.formatter
formatter.store_option_strings(self)
result = []
- result.append(formatter.format_heading(_("options")))
+ result.append(formatter.format_heading(_("Options")))
formatter.indent()
if self.option_list:
result.append(OptionContainer.format_option_help(self, formatter))
@@ -1513,6 +1621,9 @@
# Drop the last "\n", or the header if no options or option groups:
return "".join(result[:-1])
+ def format_epilog(self, formatter):
+ return formatter.format_epilog(self.epilog)
+
def format_help(self, formatter=None):
if formatter is None:
formatter = self.formatter
@@ -1522,8 +1633,16 @@
if self.description:
result.append(self.format_description(formatter) + "\n")
result.append(self.format_option_help(formatter))
+ result.append(self.format_epilog(formatter))
return "".join(result)
+ # used by test suite
+ def _get_encoding(self, file):
+ encoding = getattr(file, "encoding", None)
+ if not encoding:
+ encoding = sys.getdefaultencoding()
+ return encoding
+
def print_help(self, file=None):
"""print_help(file : file = stdout)
@@ -1532,7 +1651,8 @@
"""
if file is None:
file = sys.stdout
- file.write(self.format_help())
+ encoding = self._get_encoding(file)
+ file.write(self.format_help().encode(encoding, "replace"))
# class OptionParser
@@ -1555,12 +1675,11 @@
if len(possibilities) == 1:
return possibilities[0]
elif not possibilities:
- raise BadOptionError(_("no such option: %s") % s)
+ raise BadOptionError(s)
else:
# More than one possible completion: ambiguous prefix.
possibilities.sort()
- raise BadOptionError(_("ambiguous option: %s (%s?)")
- % (s, ", ".join(possibilities)))
+ raise AmbiguousOptionError(s, possibilities)
# Some day, there might be many Option classes. As of Optik 1.3, the
Added: py/branch/py-compat-2.5.2/py/compat/pdb.py
==============================================================================
--- (empty file)
+++ py/branch/py-compat-2.5.2/py/compat/pdb.py Tue Sep 2 21:40:21 2008
@@ -0,0 +1,1234 @@
+#! /usr/bin/env python
+
+"""A Python debugger."""
+
+# (See pdb.doc for documentation.)
+
+import sys
+import linecache
+import cmd
+import bdb
+from repr import Repr
+import os
+import re
+import pprint
+import traceback
+# Create a custom safe Repr instance and increase its maxstring.
+# The default of 30 truncates error messages too easily.
+_repr = Repr()
+_repr.maxstring = 200
+_saferepr = _repr.repr
+
+__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",
+ "post_mortem", "help"]
+
+def find_function(funcname, filename):
+ cre = re.compile(r'def\s+%s\s*[(]' % funcname)
+ try:
+ fp = open(filename)
+ except IOError:
+ return None
+ # consumer of this info expects the first line to be 1
+ lineno = 1
+ answer = None
+ while 1:
+ line = fp.readline()
+ if line == '':
+ break
+ if cre.match(line):
+ answer = funcname, filename, lineno
+ break
+ lineno = lineno + 1
+ fp.close()
+ return answer
+
+
+# Interaction prompt line will separate file and call info from code
+# text using value of line_prefix string. A newline and arrow may
+# be to your liking. You can set it once pdb is imported using the
+# command "pdb.line_prefix = '\n% '".
+# line_prefix = ': ' # Use this to get the old situation back
+line_prefix = '\n-> ' # Probably a better default
+
+class Pdb(bdb.Bdb, cmd.Cmd):
+
+ def __init__(self, completekey='tab', stdin=None, stdout=None):
+ bdb.Bdb.__init__(self)
+ cmd.Cmd.__init__(self, completekey, stdin, stdout)
+ if stdout:
+ self.use_rawinput = 0
+ self.prompt = '(Pdb) '
+ self.aliases = {}
+ self.mainpyfile = ''
+ self._wait_for_mainpyfile = 0
+ # Try to load readline if it exists
+ try:
+ import readline
+ except ImportError:
+ pass
+
+ # Read $HOME/.pdbrc and ./.pdbrc
+ self.rcLines = []
+ if 'HOME' in os.environ:
+ envHome = os.environ['HOME']
+ try:
+ rcFile = open(os.path.join(envHome, ".pdbrc"))
+ except IOError:
+ pass
+ else:
+ for line in rcFile.readlines():
+ self.rcLines.append(line)
+ rcFile.close()
+ try:
+ rcFile = open(".pdbrc")
+ except IOError:
+ pass
+ else:
+ for line in rcFile.readlines():
+ self.rcLines.append(line)
+ rcFile.close()
+
+ self.commands = {} # associates a command list to breakpoint numbers
+ self.commands_doprompt = {} # for each bp num, tells if the prompt must be disp. after execing the cmd list
+ self.commands_silent = {} # for each bp num, tells if the stack trace must be disp. after execing the cmd list
+ self.commands_defining = False # True while in the process of defining a command list
+ self.commands_bnum = None # The breakpoint number for which we are defining a list
+
+ def reset(self):
+ bdb.Bdb.reset(self)
+ self.forget()
+
+ def forget(self):
+ self.lineno = None
+ self.stack = []
+ self.curindex = 0
+ self.curframe = None
+
+ def setup(self, f, t):
+ self.forget()
+ self.stack, self.curindex = self.get_stack(f, t)
+ self.curframe = self.stack[self.curindex][0]
+ self.execRcLines()
+
+ # Can be executed earlier than 'setup' if desired
+ def execRcLines(self):
+ if self.rcLines:
+ # Make local copy because of recursion
+ rcLines = self.rcLines
+ # executed only once
+ self.rcLines = []
+ for line in rcLines:
+ line = line[:-1]
+ if len(line) > 0 and line[0] != '#':
+ self.onecmd(line)
+
+ # Override Bdb methods
+
+ def user_call(self, frame, argument_list):
+ """This method is called when there is the remote possibility
+ that we ever need to stop in this function."""
+ if self._wait_for_mainpyfile:
+ return
+ if self.stop_here(frame):
+ print >>self.stdout, '--Call--'
+ self.interaction(frame, None)
+
+ def user_line(self, frame):
+ """This function is called when we stop or break at this line."""
+ if self._wait_for_mainpyfile:
+ if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
+ or frame.f_lineno<= 0):
+ return
+ self._wait_for_mainpyfile = 0
+ if self.bp_commands(frame):
+ self.interaction(frame, None)
+
+ def bp_commands(self,frame):
+ """ Call every command that was set for the current active breakpoint (if there is one)
+ Returns True if the normal interaction function must be called, False otherwise """
+ #self.currentbp is set in bdb.py in bdb.break_here if a breakpoint was hit
+ if getattr(self,"currentbp",False) and self.currentbp in self.commands:
+ currentbp = self.currentbp
+ self.currentbp = 0
+ lastcmd_back = self.lastcmd
+ self.setup(frame, None)
+ for line in self.commands[currentbp]:
+ self.onecmd(line)
+ self.lastcmd = lastcmd_back
+ if not self.commands_silent[currentbp]:
+ self.print_stack_entry(self.stack[self.curindex])
+ if self.commands_doprompt[currentbp]:
+ self.cmdloop()
+ self.forget()
+ return
+ return 1
+
+ def user_return(self, frame, return_value):
+ """This function is called when a return trap is set here."""
+ frame.f_locals['__return__'] = return_value
+ print >>self.stdout, '--Return--'
+ self.interaction(frame, None)
+
+ def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
+ """This function is called if an exception occurs,
+ but only if we are to stop at or just below this level."""
+ frame.f_locals['__exception__'] = exc_type, exc_value
+ if type(exc_type) == type(''):
+ exc_type_name = exc_type
+ else: exc_type_name = exc_type.__name__
+ print >>self.stdout, exc_type_name + ':', _saferepr(exc_value)
+ self.interaction(frame, exc_traceback)
+
+ # General interaction function
+
+ def interaction(self, frame, traceback):
+ self.setup(frame, traceback)
+ self.print_stack_entry(self.stack[self.curindex])
+ self.cmdloop()
+ self.forget()
+
+ def default(self, line):
+ if line[:1] == '!': line = line[1:]
+ locals = self.curframe.f_locals
+ globals = self.curframe.f_globals
+ try:
+ code = compile(line + '\n', '<stdin>', 'single')
+ exec code in globals, locals
+ except:
+ t, v = sys.exc_info()[:2]
+ if type(t) == type(''):
+ exc_type_name = t
+ else: exc_type_name = t.__name__
+ print >>self.stdout, '***', exc_type_name + ':', v
+
+ def precmd(self, line):
+ """Handle alias expansion and ';;' separator."""
+ if not line.strip():
+ return line
+ args = line.split()
+ while args[0] in self.aliases:
+ line = self.aliases[args[0]]
+ ii = 1
+ for tmpArg in args[1:]:
+ line = line.replace("%" + str(ii),
+ tmpArg)
+ ii = ii + 1
+ line = line.replace("%*", ' '.join(args[1:]))
+ args = line.split()
+ # split into ';;' separated commands
+ # unless it's an alias command
+ if args[0] != 'alias':
+ marker = line.find(';;')
+ if marker >= 0:
+ # queue up everything after marker
+ next = line[marker+2:].lstrip()
+ self.cmdqueue.append(next)
+ line = line[:marker].rstrip()
+ return line
+
+ def onecmd(self, line):
+ """Interpret the argument as though it had been typed in response
+ to the prompt.
+
+ Checks whether this line is typed at the normal prompt or in
+ a breakpoint command list definition.
+ """
+ if not self.commands_defining:
+ return cmd.Cmd.onecmd(self, line)
+ else:
+ return self.handle_command_def(line)
+
+ def handle_command_def(self,line):
+ """ Handles one command line during command list definition. """
+ cmd, arg, line = self.parseline(line)
+ if cmd == 'silent':
+ self.commands_silent[self.commands_bnum] = True
+ return # continue to handle other cmd def in the cmd list
+ elif cmd == 'end':
+ self.cmdqueue = []
+ return 1 # end of cmd list
+ cmdlist = self.commands[self.commands_bnum]
+ if (arg):
+ cmdlist.append(cmd+' '+arg)
+ else:
+ cmdlist.append(cmd)
+ # Determine if we must stop
+ try:
+ func = getattr(self, 'do_' + cmd)
+ except AttributeError:
+ func = self.default
+ if func.func_name in self.commands_resuming : # one of the resuming commands.
+ self.commands_doprompt[self.commands_bnum] = False
+ self.cmdqueue = []
+ return 1
+ return
+
+ # Command definitions, called by cmdloop()
+ # The argument is the remaining string on the command line
+ # Return true to exit from the command loop
+
+ do_h = cmd.Cmd.do_help
+
+ def do_commands(self, arg):
+ """Defines a list of commands associated to a breakpoint
+ Those commands will be executed whenever the breakpoint causes the program to stop execution."""
+ if not arg:
+ bnum = len(bdb.Breakpoint.bpbynumber)-1
+ else:
+ try:
+ bnum = int(arg)
+ except:
+ print >>self.stdout, "Usage : commands [bnum]\n ...\n end"
+ return
+ self.commands_bnum = bnum
+ self.commands[bnum] = []
+ self.commands_doprompt[bnum] = True
+ self.commands_silent[bnum] = False
+ prompt_back = self.prompt
+ self.prompt = '(com) '
+ self.commands_defining = True
+ self.cmdloop()
+ self.commands_defining = False
+ self.prompt = prompt_back
+
+ def do_break(self, arg, temporary = 0):
+ # break [ ([filename:]lineno | function) [, "condition"] ]
+ if not arg:
+ if self.breaks: # There's at least one
+ print >>self.stdout, "Num Type Disp Enb Where"
+ for bp in bdb.Breakpoint.bpbynumber:
+ if bp:
+ bp.bpprint(self.stdout)
+ return
+ # parse arguments; comma has lowest precedence
+ # and cannot occur in filename
+ filename = None
+ lineno = None
+ cond = None
+ comma = arg.find(',')
+ if comma > 0:
+ # parse stuff after comma: "condition"
+ cond = arg[comma+1:].lstrip()
+ arg = arg[:comma].rstrip()
+ # parse stuff before comma: [filename:]lineno | function
+ colon = arg.rfind(':')
+ funcname = None
+ if colon >= 0:
+ filename = arg[:colon].rstrip()
+ f = self.lookupmodule(filename)
+ if not f:
+ print >>self.stdout, '*** ', repr(filename),
+ print >>self.stdout, 'not found from sys.path'
+ return
+ else:
+ filename = f
+ arg = arg[colon+1:].lstrip()
+ try:
+ lineno = int(arg)
+ except ValueError, msg:
+ print >>self.stdout, '*** Bad lineno:', arg
+ return
+ else:
+ # no colon; can be lineno or function
+ try:
+ lineno = int(arg)
+ except ValueError:
+ try:
+ func = eval(arg,
+ self.curframe.f_globals,
+ self.curframe.f_locals)
+ except:
+ func = arg
+ try:
+ if hasattr(func, 'im_func'):
+ func = func.im_func
+ code = func.func_code
+ #use co_name to identify the bkpt (function names
+ #could be aliased, but co_name is invariant)
+ funcname = code.co_name
+ lineno = code.co_firstlineno
+ filename = code.co_filename
+ except:
+ # last thing to try
+ (ok, filename, ln) = self.lineinfo(arg)
+ if not ok:
+ print >>self.stdout, '*** The specified object',
+ print >>self.stdout, repr(arg),
+ print >>self.stdout, 'is not a function'
+ print >>self.stdout, 'or was not found along sys.path.'
+ return
+ funcname = ok # ok contains a function name
+ lineno = int(ln)
+ if not filename:
+ filename = self.defaultFile()
+ # Check for reasonable breakpoint
+ line = self.checkline(filename, lineno)
+ if line:
+ # now set the break point
+ err = self.set_break(filename, line, temporary, cond, funcname)
+ if err: print >>self.stdout, '***', err
+ else:
+ bp = self.get_breaks(filename, line)[-1]
+ print >>self.stdout, "Breakpoint %d at %s:%d" % (bp.number,
+ bp.file,
+ bp.line)
+
+ # To be overridden in derived debuggers
+ def defaultFile(self):
+ """Produce a reasonable default."""
+ filename = self.curframe.f_code.co_filename
+ if filename == '<string>' and self.mainpyfile:
+ filename = self.mainpyfile
+ return filename
+
+ do_b = do_break
+
+ def do_tbreak(self, arg):
+ self.do_break(arg, 1)
+
+ def lineinfo(self, identifier):
+ failed = (None, None, None)
+ # Input is identifier, may be in single quotes
+ idstring = identifier.split("'")
+ if len(idstring) == 1:
+ # not in single quotes
+ id = idstring[0].strip()
+ elif len(idstring) == 3:
+ # quoted
+ id = idstring[1].strip()
+ else:
+ return failed
+ if id == '': return failed
+ parts = id.split('.')
+ # Protection for derived debuggers
+ if parts[0] == 'self':
+ del parts[0]
+ if len(parts) == 0:
+ return failed
+ # Best first guess at file to look at
+ fname = self.defaultFile()
+ if len(parts) == 1:
+ item = parts[0]
+ else:
+ # More than one part.
+ # First is module, second is method/class
+ f = self.lookupmodule(parts[0])
+ if f:
+ fname = f
+ item = parts[1]
+ answer = find_function(item, fname)
+ return answer or failed
+
+ def checkline(self, filename, lineno):
+ """Check whether specified line seems to be executable.
+
+ Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
+ line or EOF). Warning: testing is not comprehensive.
+ """
+ line = linecache.getline(filename, lineno)
+ if not line:
+ print >>self.stdout, 'End of file'
+ return 0
+ line = line.strip()
+ # Don't allow setting breakpoint at a blank line
+ if (not line or (line[0] == '#') or
+ (line[:3] == '"""') or line[:3] == "'''"):
+ print >>self.stdout, '*** Blank or comment'
+ return 0
+ return lineno
+
+ def do_enable(self, arg):
+ args = arg.split()
+ for i in args:
+ try:
+ i = int(i)
+ except ValueError:
+ print >>self.stdout, 'Breakpoint index %r is not a number' % i
+ continue
+
+ if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
+ print >>self.stdout, 'No breakpoint numbered', i
+ continue
+
+ bp = bdb.Breakpoint.bpbynumber[i]
+ if bp:
+ bp.enable()
+
+ def do_disable(self, arg):
+ args = arg.split()
+ for i in args:
+ try:
+ i = int(i)
+ except ValueError:
+ print >>self.stdout, 'Breakpoint index %r is not a number' % i
+ continue
+
+ if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
+ print >>self.stdout, 'No breakpoint numbered', i
+ continue
+
+ bp = bdb.Breakpoint.bpbynumber[i]
+ if bp:
+ bp.disable()
+
+ def do_condition(self, arg):
+ # arg is breakpoint number and condition
+ args = arg.split(' ', 1)
+ try:
+ bpnum = int(args[0].strip())
+ except ValueError:
+ # something went wrong
+ print >>self.stdout, \
+ 'Breakpoint index %r is not a number' % args[0]
+ return
+ try:
+ cond = args[1]
+ except:
+ cond = None
+ try:
+ bp = bdb.Breakpoint.bpbynumber[bpnum]
+ except IndexError:
+ print >>self.stdout, 'Breakpoint index %r is not valid' % args[0]
+ return
+ if bp:
+ bp.cond = cond
+ if not cond:
+ print >>self.stdout, 'Breakpoint', bpnum,
+ print >>self.stdout, 'is now unconditional.'
+
+ def do_ignore(self,arg):
+ """arg is bp number followed by ignore count."""
+ args = arg.split()
+ try:
+ bpnum = int(args[0].strip())
+ except ValueError:
+ # something went wrong
+ print >>self.stdout, \
+ 'Breakpoint index %r is not a number' % args[0]
+ return
+ try:
+ count = int(args[1].strip())
+ except:
+ count = 0
+ try:
+ bp = bdb.Breakpoint.bpbynumber[bpnum]
+ except IndexError:
+ print >>self.stdout, 'Breakpoint index %r is not valid' % args[0]
+ return
+ if bp:
+ bp.ignore = count
+ if count > 0:
+ reply = 'Will ignore next '
+ if count > 1:
+ reply = reply + '%d crossings' % count
+ else:
+ reply = reply + '1 crossing'
+ print >>self.stdout, reply + ' of breakpoint %d.' % bpnum
+ else:
+ print >>self.stdout, 'Will stop next time breakpoint',
+ print >>self.stdout, bpnum, 'is reached.'
+
+ def do_clear(self, arg):
+ """Three possibilities, tried in this order:
+ clear -> clear all breaks, ask for confirmation
+ clear file:lineno -> clear all breaks at file:lineno
+ clear bpno bpno ... -> clear breakpoints by number"""
+ if not arg:
+ try:
+ reply = raw_input('Clear all breaks? ')
+ except EOFError:
+ reply = 'no'
+ reply = reply.strip().lower()
+ if reply in ('y', 'yes'):
+ self.clear_all_breaks()
+ return
+ if ':' in arg:
+ # Make sure it works for "clear C:\foo\bar.py:12"
+ i = arg.rfind(':')
+ filename = arg[:i]
+ arg = arg[i+1:]
+ try:
+ lineno = int(arg)
+ except ValueError:
+ err = "Invalid line number (%s)" % arg
+ else:
+ err = self.clear_break(filename, lineno)
+ if err: print >>self.stdout, '***', err
+ return
+ numberlist = arg.split()
+ for i in numberlist:
+ try:
+ i = int(i)
+ except ValueError:
+ print >>self.stdout, 'Breakpoint index %r is not a number' % i
+ continue
+
+ if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
+ print >>self.stdout, 'No breakpoint numbered', i
+ continue
+ err = self.clear_bpbynumber(i)
+ if err:
+ print >>self.stdout, '***', err
+ else:
+ print >>self.stdout, 'Deleted breakpoint', i
+ do_cl = do_clear # 'c' is already an abbreviation for 'continue'
+
+ def do_where(self, arg):
+ self.print_stack_trace()
+ do_w = do_where
+ do_bt = do_where
+
+ def do_up(self, arg):
+ if self.curindex == 0:
+ print >>self.stdout, '*** Oldest frame'
+ else:
+ self.curindex = self.curindex - 1
+ self.curframe = self.stack[self.curindex][0]
+ self.print_stack_entry(self.stack[self.curindex])
+ self.lineno = None
+ do_u = do_up
+
+ def do_down(self, arg):
+ if self.curindex + 1 == len(self.stack):
+ print >>self.stdout, '*** Newest frame'
+ else:
+ self.curindex = self.curindex + 1
+ self.curframe = self.stack[self.curindex][0]
+ self.print_stack_entry(self.stack[self.curindex])
+ self.lineno = None
+ do_d = do_down
+
+ def do_step(self, arg):
+ self.set_step()
+ return 1
+ do_s = do_step
+
+ def do_next(self, arg):
+ self.set_next(self.curframe)
+ return 1
+ do_n = do_next
+
+ def do_return(self, arg):
+ self.set_return(self.curframe)
+ return 1
+ do_r = do_return
+
+ def do_continue(self, arg):
+ self.set_continue()
+ return 1
+ do_c = do_cont = do_continue
+
+ def do_jump(self, arg):
+ if self.curindex + 1 != len(self.stack):
+ print >>self.stdout, "*** You can only jump within the bottom frame"
+ return
+ try:
+ arg = int(arg)
+ except ValueError:
+ print >>self.stdout, "*** The 'jump' command requires a line number."
+ else:
+ try:
+ # Do the jump, fix up our copy of the stack, and display the
+ # new position
+ self.curframe.f_lineno = arg
+ self.stack[self.curindex] = self.stack[self.curindex][0], arg
+ self.print_stack_entry(self.stack[self.curindex])
+ except ValueError, e:
+ print >>self.stdout, '*** Jump failed:', e
+ do_j = do_jump
+
+ def do_debug(self, arg):
+ sys.settrace(None)
+ globals = self.curframe.f_globals
+ locals = self.curframe.f_locals
+ p = Pdb(self.completekey, self.stdin, self.stdout)
+ p.prompt = "(%s) " % self.prompt.strip()
+ print >>self.stdout, "ENTERING RECURSIVE DEBUGGER"
+ sys.call_tracing(p.run, (arg, globals, locals))
+ print >>self.stdout, "LEAVING RECURSIVE DEBUGGER"
+ sys.settrace(self.trace_dispatch)
+ self.lastcmd = p.lastcmd
+
+ def do_quit(self, arg):
+ self._user_requested_quit = 1
+ self.set_quit()
+ return 1
+
+ do_q = do_quit
+ do_exit = do_quit
+
+ def do_EOF(self, arg):
+ print >>self.stdout
+ self._user_requested_quit = 1
+ self.set_quit()
+ return 1
+
+ def do_args(self, arg):
+ f = self.curframe
+ co = f.f_code
+ dict = f.f_locals
+ n = co.co_argcount
+ if co.co_flags & 4: n = n+1
+ if co.co_flags & 8: n = n+1
+ for i in range(n):
+ name = co.co_varnames[i]
+ print >>self.stdout, name, '=',
+ if name in dict: print >>self.stdout, dict[name]
+ else: print >>self.stdout, "*** undefined ***"
+ do_a = do_args
+
+ def do_retval(self, arg):
+ if '__return__' in self.curframe.f_locals:
+ print >>self.stdout, self.curframe.f_locals['__return__']
+ else:
+ print >>self.stdout, '*** Not yet returned!'
+ do_rv = do_retval
+
+ def _getval(self, arg):
+ try:
+ return eval(arg, self.curframe.f_globals,
+ self.curframe.f_locals)
+ except:
+ t, v = sys.exc_info()[:2]
+ if isinstance(t, str):
+ exc_type_name = t
+ else: exc_type_name = t.__name__
+ print >>self.stdout, '***', exc_type_name + ':', repr(v)
+ raise
+
+ def do_p(self, arg):
+ try:
+ print >>self.stdout, repr(self._getval(arg))
+ except:
+ pass
+
+ def do_pp(self, arg):
+ try:
+ pprint.pprint(self._getval(arg), self.stdout)
+ except:
+ pass
+
+ def do_list(self, arg):
+ self.lastcmd = 'list'
+ last = None
+ if arg:
+ try:
+ x = eval(arg, {}, {})
+ if type(x) == type(()):
+ first, last = x
+ first = int(first)
+ last = int(last)
+ if last < first:
+ # Assume it's a count
+ last = first + last
+ else:
+ first = max(1, int(x) - 5)
+ except:
+ print >>self.stdout, '*** Error in argument:', repr(arg)
+ return
+ elif self.lineno is None:
+ first = max(1, self.curframe.f_lineno - 5)
+ else:
+ first = self.lineno + 1
+ if last is None:
+ last = first + 10
+ filename = self.curframe.f_code.co_filename
+ breaklist = self.get_file_breaks(filename)
+ try:
+ for lineno in range(first, last+1):
+ line = linecache.getline(filename, lineno)
+ if not line:
+ print >>self.stdout, '[EOF]'
+ break
+ else:
+ s = repr(lineno).rjust(3)
+ if len(s) < 4: s = s + ' '
+ if lineno in breaklist: s = s + 'B'
+ else: s = s + ' '
+ if lineno == self.curframe.f_lineno:
+ s = s + '->'
+ print >>self.stdout, s + '\t' + line,
+ self.lineno = lineno
+ except KeyboardInterrupt:
+ pass
+ do_l = do_list
+
+ def do_whatis(self, arg):
+ try:
+ value = eval(arg, self.curframe.f_globals,
+ self.curframe.f_locals)
+ except:
+ t, v = sys.exc_info()[:2]
+ if type(t) == type(''):
+ exc_type_name = t
+ else: exc_type_name = t.__name__
+ print >>self.stdout, '***', exc_type_name + ':', repr(v)
+ return
+ code = None
+ # Is it a function?
+ try: code = value.func_code
+ except: pass
+ if code:
+ print >>self.stdout, 'Function', code.co_name
+ return
+ # Is it an instance method?
+ try: code = value.im_func.func_code
+ except: pass
+ if code:
+ print >>self.stdout, 'Method', code.co_name
+ return
+ # None of the above...
+ print >>self.stdout, type(value)
+
+ def do_alias(self, arg):
+ args = arg.split()
+ if len(args) == 0:
+ keys = self.aliases.keys()
+ keys.sort()
+ for alias in keys:
+ print >>self.stdout, "%s = %s" % (alias, self.aliases[alias])
+ return
+ if args[0] in self.aliases and len(args) == 1:
+ print >>self.stdout, "%s = %s" % (args[0], self.aliases[args[0]])
+ else:
+ self.aliases[args[0]] = ' '.join(args[1:])
+
+ def do_unalias(self, arg):
+ args = arg.split()
+ if len(args) == 0: return
+ if args[0] in self.aliases:
+ del self.aliases[args[0]]
+
+ #list of all the commands making the program resume execution.
+ commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return',
+ 'do_quit', 'do_jump']
+
+ # Print a traceback starting at the top stack frame.
+ # The most recently entered frame is printed last;
+ # this is different from dbx and gdb, but consistent with
+ # the Python interpreter's stack trace.
+ # It is also consistent with the up/down commands (which are
+ # compatible with dbx and gdb: up moves towards 'main()'
+ # and down moves towards the most recent stack frame).
+
+ def print_stack_trace(self):
+ try:
+ for frame_lineno in self.stack:
+ self.print_stack_entry(frame_lineno)
+ except KeyboardInterrupt:
+ pass
+
+ def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix):
+ frame, lineno = frame_lineno
+ if frame is self.curframe:
+ print >>self.stdout, '>',
+ else:
+ print >>self.stdout, ' ',
+ print >>self.stdout, self.format_stack_entry(frame_lineno,
+ prompt_prefix)
+
+
+ # Help methods (derived from pdb.doc)
+
+ def help_help(self):
+ self.help_h()
+
+ def help_h(self):
+ print >>self.stdout, """h(elp)
+Without argument, print the list of available commands.
+With a command name as argument, print help about that command
+"help pdb" pipes the full documentation file to the $PAGER
+"help exec" gives help on the ! command"""
+
+ def help_where(self):
+ self.help_w()
+
+ def help_w(self):
+ print >>self.stdout, """w(here)
+Print a stack trace, with the most recent frame at the bottom.
+An arrow indicates the "current frame", which determines the
+context of most commands. 'bt' is an alias for this command."""
+
+ help_bt = help_w
+
+ def help_down(self):
+ self.help_d()
+
+ def help_d(self):
+ print >>self.stdout, """d(own)
+Move the current frame one level down in the stack trace
+(to a newer frame)."""
+
+ def help_up(self):
+ self.help_u()
+
+ def help_u(self):
+ print >>self.stdout, """u(p)
+Move the current frame one level up in the stack trace
+(to an older frame)."""
+
+ def help_break(self):
+ self.help_b()
+
+ def help_b(self):
+ print >>self.stdout, """b(reak) ([file:]lineno | function) [, condition]
+With a line number argument, set a break there in the current
+file. With a function name, set a break at first executable line
+of that function. Without argument, list all breaks. If a second
+argument is present, it is a string specifying an expression
+which must evaluate to true before the breakpoint is honored.
+
+The line number may be prefixed with a filename and a colon,
+to specify a breakpoint in another file (probably one that
+hasn't been loaded yet). The file is searched for on sys.path;
+the .py suffix may be omitted."""
+
+ def help_clear(self):
+ self.help_cl()
+
+ def help_cl(self):
+ print >>self.stdout, "cl(ear) filename:lineno"
+ print >>self.stdout, """cl(ear) [bpnumber [bpnumber...]]
+With a space separated list of breakpoint numbers, clear
+those breakpoints. Without argument, clear all breaks (but
+first ask confirmation). With a filename:lineno argument,
+clear all breaks at that line in that file.
+
+Note that the argument is different from previous versions of
+the debugger (in python distributions 1.5.1 and before) where
+a linenumber was used instead of either filename:lineno or
+breakpoint numbers."""
+
+ def help_tbreak(self):
+ print >>self.stdout, """tbreak same arguments as break, but breakpoint is
+removed when first hit."""
+
+ def help_enable(self):
+ print >>self.stdout, """enable bpnumber [bpnumber ...]
+Enables the breakpoints given as a space separated list of
+bp numbers."""
+
+ def help_disable(self):
+ print >>self.stdout, """disable bpnumber [bpnumber ...]
+Disables the breakpoints given as a space separated list of
+bp numbers."""
+
+ def help_ignore(self):
+ print >>self.stdout, """ignore bpnumber count
+Sets the ignore count for the given breakpoint number. A breakpoint
+becomes active when the ignore count is zero. When non-zero, the
+count is decremented each time the breakpoint is reached and the
+breakpoint is not disabled and any associated condition evaluates
+to true."""
+
+ def help_condition(self):
+ print >>self.stdout, """condition bpnumber str_condition
+str_condition is a string specifying an expression which
+must evaluate to true before the breakpoint is honored.
+If str_condition is absent, any existing condition is removed;
+i.e., the breakpoint is made unconditional."""
+
+ def help_step(self):
+ self.help_s()
+
+ def help_s(self):
+ print >>self.stdout, """s(tep)
+Execute the current line, stop at the first possible occasion
+(either in a function that is called or in the current function)."""
+
+ def help_next(self):
+ self.help_n()
+
+ def help_n(self):
+ print >>self.stdout, """n(ext)
+Continue execution until the next line in the current function
+is reached or it returns."""
+
+ def help_return(self):
+ self.help_r()
+
+ def help_r(self):
+ print >>self.stdout, """r(eturn)
+Continue execution until the current function returns."""
+
+ def help_continue(self):
+ self.help_c()
+
+ def help_cont(self):
+ self.help_c()
+
+ def help_c(self):
+ print >>self.stdout, """c(ont(inue))
+Continue execution, only stop when a breakpoint is encountered."""
+
+ def help_jump(self):
+ self.help_j()
+
+ def help_j(self):
+ print >>self.stdout, """j(ump) lineno
+Set the next line that will be executed."""
+
+ def help_debug(self):
+ print >>self.stdout, """debug code
+Enter a recursive debugger that steps through the code argument
+(which is an arbitrary expression or statement to be executed
+in the current environment)."""
+
+ def help_list(self):
+ self.help_l()
+
+ def help_l(self):
+ print >>self.stdout, """l(ist) [first [,last]]
+List source code for the current file.
+Without arguments, list 11 lines around the current line
+or continue the previous listing.
+With one argument, list 11 lines starting at that line.
+With two arguments, list the given range;
+if the second argument is less than the first, it is a count."""
+
+ def help_args(self):
+ self.help_a()
+
+ def help_a(self):
+ print >>self.stdout, """a(rgs)
+Print the arguments of the current function."""
+
+ def help_p(self):
+ print >>self.stdout, """p expression
+Print the value of the expression."""
+
+ def help_pp(self):
+ print >>self.stdout, """pp expression
+Pretty-print the value of the expression."""
+
+ def help_exec(self):
+ print >>self.stdout, """(!) statement
+Execute the (one-line) statement in the context of
+the current stack frame.
+The exclamation point can be omitted unless the first word
+of the statement resembles a debugger command.
+To assign to a global variable you must always prefix the
+command with a 'global' command, e.g.:
+(Pdb) global list_options; list_options = ['-l']
+(Pdb)"""
+
+ def help_quit(self):
+ self.help_q()
+
+ def help_q(self):
+ print >>self.stdout, """q(uit) or exit - Quit from the debugger.
+The program being executed is aborted."""
+
+ help_exit = help_q
+
+ def help_whatis(self):
+ print >>self.stdout, """whatis arg
+Prints the type of the argument."""
+
+ def help_EOF(self):
+ print >>self.stdout, """EOF
+Handles the receipt of EOF as a command."""
+
+ def help_alias(self):
+ print >>self.stdout, """alias [name [command [parameter parameter ...] ]]
+Creates an alias called 'name' the executes 'command'. The command
+must *not* be enclosed in quotes. Replaceable parameters are
+indicated by %1, %2, and so on, while %* is replaced by all the
+parameters. If no command is given, the current alias for name
+is shown. If no name is given, all aliases are listed.
+
+Aliases may be nested and can contain anything that can be
+legally typed at the pdb prompt. Note! You *can* override
+internal pdb commands with aliases! Those internal commands
+are then hidden until the alias is removed. Aliasing is recursively
+applied to the first word of the command line; all other words
+in the line are left alone.
+
+Some useful aliases (especially when placed in the .pdbrc file) are:
+
+#Print instance variables (usage "pi classInst")
+alias pi for k in %1.__dict__.keys(): print "%1.",k,"=",%1.__dict__[k]
+
+#Print instance variables in self
+alias ps pi self
+"""
+
+ def help_unalias(self):
+ print >>self.stdout, """unalias name
+Deletes the specified alias."""
+
+ def help_commands(self):
+ print >>self.stdout, """commands [bpnumber]
+(com) ...
+(com) end
+(Pdb)
+
+Specify a list of commands for breakpoint number bpnumber. The
+commands themselves appear on the following lines. Type a line
+containing just 'end' to terminate the commands.
+
+To remove all commands from a breakpoint, type commands and
+follow it immediately with end; that is, give no commands.
+
+With no bpnumber argument, commands refers to the last
+breakpoint set.
+
+You can use breakpoint commands to start your program up again.
+Simply use the continue command, or step, or any other
+command that resumes execution.
+
+Specifying any command resuming execution (currently continue,
+step, next, return, jump, quit and their abbreviations) terminates
+the command list (as if that command was immediately followed by end).
+This is because any time you resume execution
+(even with a simple next or step), you may encounter
+another breakpoint--which could have its own command list, leading to
+ambiguities about which list to execute.
+
+ If you use the 'silent' command in the command list, the
+usual message about stopping at a breakpoint is not printed. This may
+be desirable for breakpoints that are to print a specific message and
+then continue. If none of the other commands print anything, you
+see no sign that the breakpoint was reached.
+"""
+
+ def help_pdb(self):
+ help()
+
+ def lookupmodule(self, filename):
+ """Helper function for break/clear parsing -- may be overridden.
+
+ lookupmodule() translates (possibly incomplete) file or module name
+ into an absolute file name.
+ """
+ if os.path.isabs(filename) and os.path.exists(filename):
+ return filename
+ f = os.path.join(sys.path[0], filename)
+ if os.path.exists(f) and self.canonic(f) == self.mainpyfile:
+ return f
+ root, ext = os.path.splitext(filename)
+ if ext == '':
+ filename = filename + '.py'
+ if os.path.isabs(filename):
+ return filename
+ for dirname in sys.path:
+ while os.path.islink(dirname):
+ dirname = os.readlink(dirname)
+ fullname = os.path.join(dirname, filename)
+ if os.path.exists(fullname):
+ return fullname
+ return None
+
+ def _runscript(self, filename):
+ # Start with fresh empty copy of globals and locals and tell the script
+ # that it's being run as __main__ to avoid scripts being able to access
+ # the pdb.py namespace.
+ globals_ = {"__name__" : "__main__", "__file__" : filename}
+ locals_ = globals_
+
+ # When bdb sets tracing, a number of call and line events happens
+ # BEFORE debugger even reaches user's code (and the exact sequence of
+ # events depends on python version). So we take special measures to
+ # avoid stopping before we reach the main script (see user_line and
+ # user_call for details).
+ self._wait_for_mainpyfile = 1
+ self.mainpyfile = self.canonic(filename)
+ self._user_requested_quit = 0
+ statement = 'execfile( "%s")' % filename
+ self.run(statement, globals=globals_, locals=locals_)
+
+# Simplified interface
+
+def run(statement, globals=None, locals=None):
+ Pdb().run(statement, globals, locals)
+
+def runeval(expression, globals=None, locals=None):
+ return Pdb().runeval(expression, globals, locals)
+
+def runctx(statement, globals, locals):
+ # B/W compatibility
+ run(statement, globals, locals)
+
+def runcall(*args, **kwds):
+ return Pdb().runcall(*args, **kwds)
+
+def set_trace():
+ Pdb().set_trace(sys._getframe().f_back)
+
+# Post-Mortem interface
+
+def post_mortem(t):
+ p = Pdb()
+ p.reset()
+ while t.tb_next is not None:
+ t = t.tb_next
+ p.interaction(t.tb_frame, t)
+
+def pm():
+ post_mortem(sys.last_traceback)
+
+
+# Main program for testing
+
+TESTCMD = 'import x; x.main()'
+
+def test():
+ run(TESTCMD)
+
+# print help
+def help():
+ for dirname in sys.path:
+ fullname = os.path.join(dirname, 'pdb.doc')
+ if os.path.exists(fullname):
+ sts = os.system('${PAGER-more} '+fullname)
+ if sts: print '*** Pager exit status:', sts
+ break
+ else:
+ print 'Sorry, can\'t find the help file "pdb.doc"',
+ print 'along the Python search path'
+
+def main():
+ if not sys.argv[1:]:
+ print "usage: pdb.py scriptfile [arg] ..."
+ sys.exit(2)
+
+ mainpyfile = sys.argv[1] # Get script filename
+ if not os.path.exists(mainpyfile):
+ print 'Error:', mainpyfile, 'does not exist'
+ sys.exit(1)
+
+ del sys.argv[0] # Hide "pdb.py" from argument list
+
+ # Replace pdb's dir with script's dir in front of module search path.
+ sys.path[0] = os.path.dirname(mainpyfile)
+
+ # Note on saving/restoring sys.argv: it's a good idea when sys.argv was
+ # modified by the script being debugged. It's a bad idea when it was
+ # changed by the user from the command line. The best approach would be to
+ # have a "restart" command which would allow explicit specification of
+ # command line arguments.
+ pdb = Pdb()
+ while 1:
+ try:
+ pdb._runscript(mainpyfile)
+ if pdb._user_requested_quit:
+ break
+ print "The program finished and will be restarted"
+ except SystemExit:
+ # In most cases SystemExit does not warrant a post-mortem session.
+ print "The program exited via sys.exit(). Exit status: ",
+ print sys.exc_info()[1]
+ except:
+ traceback.print_exc()
+ print "Uncaught exception. Entering post mortem debugging"
+ print "Running 'cont' or 'step' will restart the program"
+ t = sys.exc_info()[2]
+ while t.tb_next is not None:
+ t = t.tb_next
+ pdb.interaction(t.tb_frame,t)
+ print "Post mortem debugger finished. The "+mainpyfile+" will be restarted"
+
+
+# When invoked as main program, invoke the debugger on a script
+if __name__=='__main__':
+ main()
Modified: py/branch/py-compat-2.5.2/py/compat/subprocess.py
==============================================================================
--- py/trunk/py/compat/subprocess.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/subprocess.py Tue Sep 2 21:40:21 2008
@@ -109,7 +109,7 @@
This module also defines two shortcut functions:
-call(*args, **kwargs):
+call(*popenargs, **kwargs):
Run command with arguments. Wait for command to complete, then
return the returncode attribute.
@@ -117,6 +117,15 @@
retcode = call(["ls", "-l"])
+check_call(*popenargs, **kwargs):
+ Run command with arguments. Wait for command to complete. If the
+ exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
Exceptions
----------
@@ -132,6 +141,9 @@
A ValueError will be raised if Popen is called with invalid arguments.
+check_call() will raise CalledProcessError, if the called process
+returns a non-zero return code.
+
Security
--------
@@ -154,7 +166,7 @@
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
+ 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.
@@ -222,7 +234,7 @@
sts = os.system("mycmd" + " myarg")
==>
p = Popen("mycmd" + " myarg", shell=True)
-sts = os.waitpid(p.pid, 0)
+pid, sts = os.waitpid(p.pid, 0)
Note:
@@ -328,7 +340,7 @@
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,
+The popen2.Popen3 and popen2.Popen4 basically works as subprocess.Popen,
except that:
* subprocess.Popen raises an exception if the execution fails
@@ -346,6 +358,19 @@
import os
import types
import traceback
+import gc
+
+# Exception classes used by this module.
+class CalledProcessError(Exception):
+ """This exception is raised when a process run by check_call() returns
+ a non-zero exit status. The exit status will be stored in the
+ returncode attribute."""
+ def __init__(self, returncode, cmd):
+ self.returncode = returncode
+ self.cmd = cmd
+ def __str__(self):
+ return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
if mswindows:
import threading
@@ -378,7 +403,7 @@
import fcntl
import pickle
-__all__ = ["Popen", "PIPE", "STDOUT", "call"]
+__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
@@ -396,13 +421,19 @@
def _cleanup():
for inst in _active[:]:
- inst.poll()
+ if inst.poll(_deadstate=sys.maxint) >= 0:
+ try:
+ _active.remove(inst)
+ except ValueError:
+ # This can happen if two threads create a new Popen instance.
+ # It's harmless that it was already removed, so ignore.
+ pass
PIPE = -1
STDOUT = -2
-def call(*args, **kwargs):
+def call(*popenargs, **kwargs):
"""Run command with arguments. Wait for command to complete, then
return the returncode attribute.
@@ -410,7 +441,26 @@
retcode = call(["ls", "-l"])
"""
- return Popen(*args, **kwargs).wait()
+ return Popen(*popenargs, **kwargs).wait()
+
+
+def check_call(*popenargs, **kwargs):
+ """Run command with arguments. Wait for command to complete. If
+ the exit code was zero then return, otherwise raise
+ CalledProcessError. The CalledProcessError object will have the
+ return code in the returncode attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ check_call(["ls", "-l"])
+ """
+ retcode = call(*popenargs, **kwargs)
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ if retcode:
+ raise CalledProcessError(retcode, cmd)
+ return retcode
def list2cmdline(seq):
@@ -450,7 +500,7 @@
if result:
result.append(' ')
- needquote = (" " in arg) or ("\t" in arg)
+ needquote = (" " in arg) or ("\t" in arg) or arg == ""
if needquote:
result.append('"')
@@ -490,6 +540,7 @@
"""Create new Popen instance."""
_cleanup()
+ self._child_created = False
if not isinstance(bufsize, (int, long)):
raise TypeError("bufsize must be an integer")
@@ -542,6 +593,22 @@
c2pread, c2pwrite,
errread, errwrite)
+ # On Windows, you cannot just redirect one or two handles: You
+ # either have to redirect all three or none. If the subprocess
+ # user has only redirected one or two handles, we are
+ # automatically creating PIPEs for the rest. We should close
+ # these after the process is started. See bug #1124861.
+ if mswindows:
+ if stdin is None and p2cwrite is not None:
+ os.close(p2cwrite)
+ p2cwrite = None
+ if stdout is None and c2pread is not None:
+ os.close(c2pread)
+ c2pread = None
+ if stderr is None and errread is not None:
+ os.close(errread)
+ errread = None
+
if p2cwrite:
self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
if c2pread:
@@ -555,8 +622,6 @@
else:
self.stderr = os.fdopen(errread, 'rb', bufsize)
- _active.append(self)
-
def _translate_newlines(self, data):
data = data.replace("\r\n", "\n")
@@ -564,6 +629,45 @@
return data
+ def __del__(self, sys=sys):
+ if not self._child_created:
+ # We didn't get to successfully create a child process.
+ return
+ # In case the child hasn't been waited on, check if it's done.
+ self.poll(_deadstate=sys.maxint)
+ if self.returncode is None and _active is not None:
+ # Child is still running, keep us alive until we can wait on it.
+ _active.append(self)
+
+
+ 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)."""
+
+ # Optimization: If we are only using one pipe, or no pipe at
+ # all, using select() or threads is unnecessary.
+ if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
+ stdout = None
+ stderr = None
+ if self.stdin:
+ if input:
+ self.stdin.write(input)
+ self.stdin.close()
+ elif self.stdout:
+ stdout = self.stdout.read()
+ elif self.stderr:
+ stderr = self.stderr.read()
+ self.wait()
+ return (stdout, stderr)
+
+ return self._communicate(input)
+
+
if mswindows:
#
# Windows methods
@@ -572,51 +676,57 @@
"""Construct and return tupel with IO objects:
p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
"""
- if stdin == None and stdout == None and stderr == None:
+ if stdin is None and stdout is None and stderr is None:
return (None, None, None, None, None, None)
p2cread, p2cwrite = None, None
c2pread, c2pwrite = None, None
errread, errwrite = None, None
- if stdin == None:
+ if stdin is None:
p2cread = GetStdHandle(STD_INPUT_HANDLE)
- elif stdin == PIPE:
+ if p2cread is not None:
+ pass
+ elif stdin is None or 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:
+ elif isinstance(stdin, int):
p2cread = msvcrt.get_osfhandle(stdin)
else:
# Assuming file-like object
p2cread = msvcrt.get_osfhandle(stdin.fileno())
p2cread = self._make_inheritable(p2cread)
- if stdout == None:
+ if stdout is None:
c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
- elif stdout == PIPE:
+ if c2pwrite is not None:
+ pass
+ elif stdout is None or 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:
+ elif isinstance(stdout, int):
c2pwrite = msvcrt.get_osfhandle(stdout)
else:
# Assuming file-like object
c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
c2pwrite = self._make_inheritable(c2pwrite)
- if stderr == None:
+ if stderr is None:
errwrite = GetStdHandle(STD_ERROR_HANDLE)
- elif stderr == PIPE:
+ if errwrite is not None:
+ pass
+ elif stderr is None or 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:
+ elif isinstance(stderr, int):
errwrite = msvcrt.get_osfhandle(stderr)
else:
# Assuming file-like object
@@ -663,7 +773,7 @@
args = list2cmdline(args)
# Process startup details
- if startupinfo == None:
+ if startupinfo is None:
startupinfo = STARTUPINFO()
if None not in (p2cread, c2pwrite, errwrite):
startupinfo.dwFlags |= STARTF_USESTDHANDLES
@@ -712,6 +822,7 @@
raise WindowsError(*e.args)
# Retain the process handle, but close the thread handle
+ self._child_created = True
self._handle = hp
self.pid = pid
ht.Close()
@@ -722,31 +833,29 @@
# 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:
+ if p2cread is not None:
p2cread.Close()
- if c2pwrite != None:
+ if c2pwrite is not None:
c2pwrite.Close()
- if errwrite != None:
+ if errwrite is not None:
errwrite.Close()
- def poll(self):
+ def poll(self, _deadstate=None):
"""Check if child process has terminated. Returns returncode
attribute."""
- if self.returncode == None:
+ if self.returncode is 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:
+ if self.returncode is None:
obj = WaitForSingleObject(self._handle, INFINITE)
self.returncode = GetExitCodeProcess(self._handle)
- _active.remove(self)
return self.returncode
@@ -754,14 +863,7 @@
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)."""
+ def _communicate(self, input):
stdout = None # Return
stderr = None # Return
@@ -779,7 +881,7 @@
stderr_thread.start()
if self.stdin:
- if input != None:
+ if input is not None:
self.stdin.write(input)
self.stdin.close()
@@ -789,16 +891,16 @@
stderr_thread.join()
# All data exchanged. Translate lists into strings.
- if stdout != None:
+ if stdout is not None:
stdout = stdout[0]
- if stderr != None:
+ if stderr is not 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 self.universal_newlines and hasattr(file, 'newlines'):
if stdout:
stdout = self._translate_newlines(stdout)
if stderr:
@@ -819,33 +921,33 @@
c2pread, c2pwrite = None, None
errread, errwrite = None, None
- if stdin == None:
+ if stdin is None:
pass
elif stdin == PIPE:
p2cread, p2cwrite = os.pipe()
- elif type(stdin) == types.IntType:
+ elif isinstance(stdin, int):
p2cread = stdin
else:
# Assuming file-like object
p2cread = stdin.fileno()
- if stdout == None:
+ if stdout is None:
pass
elif stdout == PIPE:
c2pread, c2pwrite = os.pipe()
- elif type(stdout) == types.IntType:
+ elif isinstance(stdout, int):
c2pwrite = stdout
else:
# Assuming file-like object
c2pwrite = stdout.fileno()
- if stderr == None:
+ if stderr is None:
pass
elif stderr == PIPE:
errread, errwrite = os.pipe()
elif stderr == STDOUT:
errwrite = c2pwrite
- elif type(stderr) == types.IntType:
+ elif isinstance(stderr, int):
errwrite = stderr
else:
# Assuming file-like object
@@ -886,11 +988,13 @@
if isinstance(args, types.StringTypes):
args = [args]
+ else:
+ args = list(args)
if shell:
args = ["/bin/sh", "-c"] + args
- if executable == None:
+ if executable is None:
executable = args[0]
# For transferring possible exec failure from child to parent
@@ -899,7 +1003,17 @@
errpipe_read, errpipe_write = os.pipe()
self._set_cloexec_flag(errpipe_write)
- self.pid = os.fork()
+ gc_was_enabled = gc.isenabled()
+ # Disable gc to avoid bug where gc -> file_dealloc ->
+ # write to stderr -> hang. http://bugs.python.org/issue1336
+ gc.disable()
+ try:
+ self.pid = os.fork()
+ except:
+ if gc_was_enabled:
+ gc.enable()
+ raise
+ self._child_created = True
if self.pid == 0:
# Child
try:
@@ -920,26 +1034,26 @@
if errwrite:
os.dup2(errwrite, 2)
- # Close pipe fds. Make sure we doesn't close the same
- # fd more than once.
- if p2cread:
+ # Close pipe fds. Make sure we don't close the same
+ # fd more than once, or standard fds.
+ if p2cread and p2cread not in (0,):
os.close(p2cread)
- if c2pwrite and c2pwrite not in (p2cread,):
+ if c2pwrite and c2pwrite not in (p2cread, 1):
os.close(c2pwrite)
- if errwrite and errwrite not in (p2cread, c2pwrite):
+ if errwrite and errwrite not in (p2cread, c2pwrite, 2):
os.close(errwrite)
# Close all other fds, if asked for
if close_fds:
self._close_fds(but=errpipe_write)
- if cwd != None:
+ if cwd is not None:
os.chdir(cwd)
if preexec_fn:
apply(preexec_fn)
- if env == None:
+ if env is None:
os.execvp(executable, args)
else:
os.execvpe(executable, args, env)
@@ -958,6 +1072,8 @@
os._exit(255)
# Parent
+ if gc_was_enabled:
+ gc.enable()
os.close(errpipe_write)
if p2cread and p2cwrite:
os.close(p2cread)
@@ -984,39 +1100,31 @@
# Should never happen
raise RuntimeError("Unknown child exit status!")
- _active.remove(self)
-
- def poll(self):
+ def poll(self, _deadstate=None):
"""Check if child process has terminated. Returns returncode
attribute."""
- if self.returncode == None:
+ if self.returncode is None:
try:
pid, sts = os.waitpid(self.pid, os.WNOHANG)
if pid == self.pid:
self._handle_exitstatus(sts)
except os.error:
- pass
+ if _deadstate is not None:
+ self.returncode = _deadstate
return self.returncode
def wait(self):
"""Wait for child process to terminate. Returns returncode
attribute."""
- if self.returncode == None:
+ if self.returncode is 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)."""
+ def _communicate(self, input):
read_set = []
write_set = []
stdout = None # Return
@@ -1037,6 +1145,7 @@
read_set.append(self.stderr)
stderr = []
+ input_offset = 0
while read_set or write_set:
rlist, wlist, xlist = select.select(read_set, write_set, [])
@@ -1044,9 +1153,9 @@
# 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:
+ bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
+ input_offset += bytes_written
+ if input_offset >= len(input):
self.stdin.close()
write_set.remove(self.stdin)
@@ -1065,16 +1174,16 @@
stderr.append(data)
# All data exchanged. Translate lists into strings.
- if stdout != None:
+ if stdout is not None:
stdout = ''.join(stdout)
- if stderr != None:
+ if stderr is not 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 self.universal_newlines and hasattr(file, 'newlines'):
if stdout:
stdout = self._translate_newlines(stdout)
if stderr:
Modified: py/branch/py-compat-2.5.2/py/compat/testing/test_doctest.py
==============================================================================
--- py/trunk/py/compat/testing/test_doctest.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/testing/test_doctest.py Tue Sep 2 21:40:21 2008
@@ -3,15 +3,9 @@
"""
from test import test_support
+import doctest
import warnings
-import py
-doctest = py.compat.doctest
-
-import sys
-sys.modules['doctest'] = py.compat.doctest
-
-
######################################################################
## Sample Objects (used by test cases)
######################################################################
@@ -514,21 +508,20 @@
>>> 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)
+Empty Tests
+~~~~~~~~~~~
+By default, an object with no doctests doesn't create any tests:
+
+ >>> tests = doctest.DocTestFinder().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
@@ -537,8 +530,7 @@
is really to support backward compatibility in what doctest.master.summarize()
displays.
- >>> tests = doctest.DocTestFinder(_namefilter=namefilter,
- ... exclude_empty=False).find(SampleClass)
+ >>> tests = doctest.DocTestFinder(exclude_empty=False).find(SampleClass)
>>> for t in tests:
... print '%2s %s' % (len(t.examples), t.name)
3 SampleClass
@@ -547,35 +539,12 @@
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
@@ -603,7 +572,7 @@
... >>> for x in range(10):
... ... print x,
... 0 1 2 3 4 5 6 7 8 9
- ... >>> x/2
+ ... >>> x//2
... 6
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
@@ -678,7 +647,7 @@
... >>> x = 12
... >>> print x
... 12
- ... >>> x/2
+ ... >>> x//2
... 6
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
@@ -699,7 +668,7 @@
... >>> x = 12
... >>> print x
... 14
- ... >>> x/2
+ ... >>> x//2
... 6
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
@@ -722,7 +691,7 @@
Got:
12
Trying:
- x/2
+ x//2
Expecting:
6
ok
@@ -737,7 +706,7 @@
... >>> x = 12
... >>> print x
... 12
- ... >>> x/2
+ ... >>> x//2
... 6
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
@@ -753,7 +722,7 @@
12
ok
Trying:
- x/2
+ x//2
Expecting:
6
ok
@@ -783,7 +752,7 @@
12
ok
Trying:
- x/2
+ x//2
Expecting:
6
ok
@@ -805,7 +774,7 @@
>>> def f(x):
... '''
... >>> x = 12
- ... >>> print x/0
+ ... >>> print x//0
... Traceback (most recent call last):
... ZeroDivisionError: integer division or modulo by zero
... '''
@@ -821,7 +790,7 @@
>>> def f(x):
... '''
... >>> x = 12
- ... >>> print 'pre-exception output', x/0
+ ... >>> print 'pre-exception output', x//0
... pre-exception output
... Traceback (most recent call last):
... ZeroDivisionError: integer division or modulo by zero
@@ -832,7 +801,7 @@
**********************************************************************
File ..., line 4, in f
Failed example:
- print 'pre-exception output', x/0
+ print 'pre-exception output', x//0
Exception raised:
...
ZeroDivisionError: integer division or modulo by zero
@@ -919,7 +888,7 @@
>>> def f(x):
... r'''
- ... >>> 1/0
+ ... >>> 1//0
... 0
... '''
>>> test = doctest.DocTestFinder().find(f)[0]
@@ -928,7 +897,7 @@
**********************************************************************
File ..., line 3, in f
Failed example:
- 1/0
+ 1//0
Exception raised:
Traceback (most recent call last):
...
@@ -1078,6 +1047,25 @@
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
+The SKIP flag causes an example to be skipped entirely. I.e., the
+example is not run. It can be useful in contexts where doctest
+examples serve as both documentation and test cases, and an example
+should be included for documentation purposes, but should not be
+checked (e.g., because its output is random, or depends on resources
+which would be unavailable.) The SKIP flag can also be used for
+'commenting out' broken examples.
+
+ >>> import unavailable_resource # doctest: +SKIP
+ >>> unavailable_resource.do_something() # doctest: +SKIP
+ >>> unavailable_resource.blow_up() # doctest: +SKIP
+ Traceback (most recent call last):
+ ...
+ UncheckedBlowUpError: Nobody checks me.
+
+ >>> import random
+ >>> print random.random() # doctest: +SKIP
+ 0.721216923889
+
The REPORT_UDIFF flag causes failures that involve multi-line expected
and actual outputs to be displayed using a unified diff:
@@ -1578,11 +1566,11 @@
>>> try: doctest.debug_src(s)
... finally: sys.stdin = real_stdin
- > <string>(1)?()
+ > <string>(1)<module>()
(Pdb) next
12
--Return--
- > <string>(1)?()->None
+ > <string>(1)<module>()->None
(Pdb) print x
12
(Pdb) continue
@@ -1620,7 +1608,7 @@
>>> try: runner.run(test)
... finally: sys.stdin = real_stdin
--Return--
- > <doctest foo[1]>(1)?()->None
+ > <doctest foo[1]>(1)<module>()->None
-> import pdb; pdb.set_trace()
(Pdb) print x
42
@@ -1656,7 +1644,7 @@
(Pdb) print y
2
(Pdb) up
- > <doctest foo[1]>(1)?()
+ > <doctest foo[1]>(1)<module>()
-> calls_set_trace()
(Pdb) print x
1
@@ -1705,7 +1693,7 @@
[EOF]
(Pdb) next
--Return--
- > <doctest foo[2]>(1)?()->None
+ > <doctest foo[2]>(1)<module>()->None
-> f(3)
(Pdb) list
1 -> f(3)
@@ -1798,7 +1786,7 @@
(Pdb) print y
1
(Pdb) up
- > <doctest foo[1]>(1)?()
+ > <doctest foo[1]>(1)<module>()
-> calls_set_trace()
(Pdb) print foo
*** NameError: name 'foo' is not defined
@@ -1893,20 +1881,6 @@
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())
- <unittest.TestResult run=4 errors=0 failures=1>
"""
def test_DocFileSuite():
@@ -1917,9 +1891,10 @@
>>> import unittest
>>> suite = doctest.DocFileSuite('test_doctest.txt',
- ... 'test_doctest2.txt')
+ ... 'test_doctest2.txt',
+ ... 'test_doctest4.txt')
>>> suite.run(unittest.TestResult())
- <unittest.TestResult run=2 errors=0 failures=2>
+ <unittest.TestResult run=3 errors=0 failures=3>
The test files are looked for in the directory containing the
calling module. A package keyword argument can be provided to
@@ -1928,9 +1903,29 @@
>>> import unittest
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
... package='test')
>>> suite.run(unittest.TestResult())
- <unittest.TestResult run=2 errors=0 failures=2>
+ <unittest.TestResult run=3 errors=0 failures=3>
+
+ Support for using a package's __loader__.get_data() is also
+ provided.
+
+ >>> import unittest, pkgutil, test
+ >>> added_loader = False
+ >>> if not hasattr(test, '__loader__'):
+ ... test.__loader__ = pkgutil.get_loader(test)
+ ... added_loader = True
+ >>> try:
+ ... suite = doctest.DocFileSuite('test_doctest.txt',
+ ... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
+ ... package='test')
+ ... suite.run(unittest.TestResult())
+ ... finally:
+ ... if added_loader:
+ ... del test.__loader__
+ <unittest.TestResult run=3 errors=0 failures=3>
'/' should be used as a path separator. It will be converted
to a native separator at run time:
@@ -1975,19 +1970,21 @@
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
... globs={'favorite_color': 'blue'})
>>> suite.run(unittest.TestResult())
- <unittest.TestResult run=2 errors=0 failures=1>
+ <unittest.TestResult run=3 errors=0 failures=2>
In this case, we supplied a missing favorite color. You can
provide doctest options:
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
... optionflags=doctest.DONT_ACCEPT_BLANKLINE,
... globs={'favorite_color': 'blue'})
>>> suite.run(unittest.TestResult())
- <unittest.TestResult run=2 errors=0 failures=2>
+ <unittest.TestResult run=3 errors=0 failures=3>
And, you can provide setUp and tearDown functions:
@@ -2005,9 +2002,10 @@
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
... setUp=setUp, tearDown=tearDown)
>>> suite.run(unittest.TestResult())
- <unittest.TestResult run=2 errors=0 failures=1>
+ <unittest.TestResult run=3 errors=0 failures=2>
But the tearDown restores sanity:
@@ -2032,6 +2030,25 @@
modified the test globals. The test globals are
automatically cleared for us after a test.
+ Tests in a file run using `DocFileSuite` can also access the
+ `__file__` global, which is set to the name of the file
+ containing the tests:
+
+ >>> suite = doctest.DocFileSuite('test_doctest3.txt')
+ >>> suite.run(unittest.TestResult())
+ <unittest.TestResult run=1 errors=0 failures=0>
+
+ If the tests contain non-ASCII characters, we have to specify which
+ encoding the file is encoded with. We do so by using the `encoding`
+ parameter:
+
+ >>> suite = doctest.DocFileSuite('test_doctest.txt',
+ ... 'test_doctest2.txt',
+ ... 'test_doctest4.txt',
+ ... encoding='utf-8')
+ >>> suite.run(unittest.TestResult())
+ <unittest.TestResult run=3 errors=0 failures=2>
+
"""
def test_trailing_space_in_test():
@@ -2238,6 +2255,32 @@
Traceback (most recent call last):
UnexpectedException: ...
>>> doctest.master = None # Reset master.
+
+If the tests contain non-ASCII characters, the tests might fail, since
+it's unknown which encoding is used. The encoding can be specified
+using the optional keyword argument `encoding`:
+
+ >>> doctest.testfile('test_doctest4.txt') # doctest: +ELLIPSIS
+ **********************************************************************
+ File "...", line 7, in test_doctest4.txt
+ Failed example:
+ u'...'
+ Expected:
+ u'f\xf6\xf6'
+ Got:
+ u'f\xc3\xb6\xc3\xb6'
+ **********************************************************************
+ ...
+ **********************************************************************
+ 1 items had failures:
+ 2 of 4 in test_doctest4.txt
+ ***Test Failed*** 2 failures.
+ (2, 4)
+ >>> doctest.master = None # Reset master.
+
+ >>> doctest.testfile('test_doctest4.txt', encoding='utf-8')
+ (0, 4)
+ >>> doctest.master = None # Reset master.
"""
# old_test1, ... used to live in doctest.py, but cluttered it. Note
Modified: py/branch/py-compat-2.5.2/py/compat/testing/test_doctest2.py
==============================================================================
--- py/trunk/py/compat/testing/test_doctest2.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/testing/test_doctest2.py Tue Sep 2 21:40:21 2008
@@ -80,6 +80,7 @@
-12
""")
+ @staticmethod
def statm():
"""
A static method.
@@ -91,8 +92,7 @@
"""
return 666
- statm = staticmethod(statm)
-
+ @classmethod
def clsm(cls, val):
"""
A class method.
@@ -104,10 +104,8 @@
"""
return val
- clsm = classmethod(clsm)
-
def test_main():
- from py.__.compat.testing import test_doctest2
+ from test import test_doctest2
EXPECTED = 19
f, t = test_support.run_doctest(test_doctest2)
if t != EXPECTED:
@@ -116,7 +114,7 @@
# Pollute the namespace with a bunch of imported functions and classes,
# to make sure they don't get tested.
-from py.compat.doctest import *
+from doctest import *
if __name__ == '__main__':
test_main()
Modified: py/branch/py-compat-2.5.2/py/compat/testing/test_optparse.py
==============================================================================
--- py/trunk/py/compat/testing/test_optparse.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/testing/test_optparse.py Tue Sep 2 21:40:21 2008
@@ -1,31 +1,31 @@
-#!/usr/bin/env python
+#!/usr/bin/python
#
# Test suite for Optik. Supplied by Johannes Gijsbers
# (taradino at 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 $
+# $Id: test_optparse.py 50791 2006-07-23 16:05:51Z greg.ward $
#
import sys
import os
+import re
import copy
+import types
import unittest
-from cStringIO import StringIO
+from StringIO 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
+ BadOptionError, OptionValueError, Values
+from optparse import _match_abbrev
+from optparse import _parse_num
# Do the right thing with boolean values for all known Python versions.
try:
@@ -33,6 +33,7 @@
except NameError:
(True, False) = (1, 0)
+retype = type(re.compile(''))
class InterceptedError(Exception):
def __init__(self,
@@ -101,7 +102,8 @@
args -- positional arguments to `func`
kwargs -- keyword arguments to `func`
expected_exception -- exception that should be raised
- expected_output -- output we expect to see
+ expected_message -- expected exception message (or pattern
+ if a compiled regex object)
Returns the exception raised for further testing.
"""
@@ -114,14 +116,23 @@
func(*args, **kwargs)
except expected_exception, err:
actual_message = str(err)
- self.assertEqual(actual_message,
- expected_message,
+ if isinstance(expected_message, retype):
+ self.assert_(expected_message.search(actual_message),
"""\
+expected exception message pattern:
+/%s/
+actual exception message:
+'''%s'''
+""" % (expected_message.pattern, actual_message))
+ else:
+ self.assertEqual(actual_message,
+ expected_message,
+ """\
expected exception message:
-'''%(expected_message)s'''
+'''%s'''
actual exception message:
-'''%(actual_message)s'''
-""" % locals())
+'''%s'''
+""" % (expected_message, actual_message))
return err
else:
@@ -153,16 +164,26 @@
expected_error=None):
"""Assert the parser prints the expected output on stdout."""
save_stdout = sys.stdout
+ encoding = getattr(save_stdout, 'encoding', None)
try:
try:
sys.stdout = StringIO()
+ if encoding:
+ sys.stdout.encoding = encoding
self.parser.parse_args(cmdline_args)
finally:
output = sys.stdout.getvalue()
sys.stdout = save_stdout
except InterceptedError, err:
- self.assertEqual(output, expected_output)
+ self.assert_(
+ type(output) is types.StringType,
+ "expected output to be an ordinary string, not %r"
+ % type(output))
+
+ if output != expected_output:
+ self.fail("expected: \n'''\n" + expected_output +
+ "'''\nbut got \n'''\n" + output + "'''")
self.assertEqual(err.exit_status, expected_status)
self.assertEqual(err.exit_message, expected_error)
else:
@@ -371,6 +392,23 @@
self.assertRaises(self.parser.remove_option, ('foo',), None,
ValueError, "no such option 'foo'")
+ def test_refleak(self):
+ # If an OptionParser is carrying around a reference to a large
+ # object, various cycles can prevent it from being GC'd in
+ # a timely fashion. destroy() breaks the cycles to ensure stuff
+ # can be cleaned up.
+ big_thing = [42]
+ refcount = sys.getrefcount(big_thing)
+ parser = OptionParser()
+ parser.add_option("-a", "--aaarggh")
+ parser.big_thing = big_thing
+
+ parser.destroy()
+ #self.assertEqual(refcount, sys.getrefcount(big_thing))
+ del parser
+ self.assertEqual(refcount, sys.getrefcount(big_thing))
+
+
class TestOptionValues(BaseTest):
def setUp(self):
pass
@@ -396,13 +434,21 @@
def setUp(self):
self.parser = OptionParser()
- def test_type_aliases(self):
- self.parser.add_option("-x", type=int)
+ def test_str_aliases_string(self):
+ self.parser.add_option("-s", type="str")
+ self.assertEquals(self.parser.get_option("-s").type, "string")
+
+ def test_new_type_object(self):
self.parser.add_option("-s", type=str)
- self.parser.add_option("-t", type="str")
+ self.assertEquals(self.parser.get_option("-s").type, "string")
+ self.parser.add_option("-x", type=int)
self.assertEquals(self.parser.get_option("-x").type, "int")
+
+ def test_old_type_object(self):
+ self.parser.add_option("-s", type=types.StringType)
self.assertEquals(self.parser.get_option("-s").type, "string")
- self.assertEquals(self.parser.get_option("-t").type, "string")
+ self.parser.add_option("-x", type=types.IntType)
+ self.assertEquals(self.parser.get_option("-x").type, "int")
# Custom type for testing processing of default values.
@@ -492,13 +538,13 @@
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"
+ parser = OptionParser("%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"
+ "Options:\n"
" --version show program's version number and exit\n"
" -h, --help show this help message and exit\n")
finally:
@@ -510,7 +556,7 @@
usage="%prog arg arg")
parser.remove_option("-h")
parser.remove_option("--version")
- expected_usage = "usage: thingy arg arg\n"
+ expected_usage = "Usage: thingy arg arg\n"
self.assertUsage(parser, expected_usage)
self.assertVersion(parser, "thingy 0.1")
self.assertHelp(parser, expected_usage + "\n")
@@ -520,9 +566,9 @@
def setUp(self):
self.parser = OptionParser(prog="test")
self.help_prefix = """\
-usage: test [options]
+Usage: test [options]
-options:
+Options:
-h, --help show this help message and exit
"""
self.file_help = "read from FILE [default: %default]"
@@ -703,13 +749,16 @@
self.assertParseOK(["-a", "--", "foo", "bar"],
{'a': "--", 'boo': None, 'foo': None},
["foo", "bar"]),
+ self.assertParseOK(["-a", "--", "--foo", "bar"],
+ {'a': "--", 'boo': 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):
+ def test_hyphen_becomes_positional_arg(self):
self.assertParseOK(["-ab", "-", "--foo", "bar"],
{'a': "b", 'boo': None, 'foo': ["bar"]},
["-"])
@@ -874,6 +923,8 @@
type="float", dest="point")
self.parser.add_option("-f", "--foo", action="append", nargs=2,
type="int", dest="foo")
+ self.parser.add_option("-z", "--zero", action="append_const",
+ dest="foo", const=(0, 0))
def test_nargs_append(self):
self.assertParseOK(["-f", "4", "-3", "blah", "--foo", "1", "666"],
@@ -889,6 +940,11 @@
{'point': None, 'foo':[(3, 4)]},
[])
+ def test_nargs_append_const(self):
+ self.assertParseOK(["--zero", "--foo", "3", "4", "-z"],
+ {'point': None, 'foo':[(0, 0), (3, 4), (0, 0)]},
+ [])
+
class TestVersion(BaseTest):
def test_version(self):
self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
@@ -964,8 +1020,14 @@
self.parser.add_option("-a", None, type="string", dest="a")
self.parser.add_option("-f", "--file", type="file", dest="file")
+ def tearDown(self):
+ if os.path.isdir(test_support.TESTFN):
+ os.rmdir(test_support.TESTFN)
+ elif os.path.isfile(test_support.TESTFN):
+ os.unlink(test_support.TESTFN)
+
class MyOption (Option):
- def check_file (option, opt, value):
+ 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):
@@ -976,25 +1038,23 @@
TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
TYPE_CHECKER["file"] = check_file
- def test_extend_file(self):
+ def test_filetype_ok(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):
+ def test_filetype_noexist(self):
self.assertParseFail(["--file", test_support.TESTFN, "-afoo"],
"%s: file does not exist" %
test_support.TESTFN)
- def test_file_irregular(self):
+ def test_filetype_notfile(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):
@@ -1007,7 +1067,7 @@
STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
- def take_action (self, action, dest, opt, value, values, parser):
+ def take_action(self, action, dest, opt, value, values, parser):
if action == "extend":
lvalue = value.split(",")
values.ensure_value(dest, []).extend(lvalue)
@@ -1076,7 +1136,7 @@
callback=lambda: None, type="string",
help="foo")
- expected_help = ("options:\n"
+ expected_help = ("Options:\n"
" -t TEST, --test=TEST foo\n")
self.assertHelp(parser, expected_help)
@@ -1089,7 +1149,7 @@
dest="points", default=[])]
self.parser = OptionParser(option_list=options)
- def process_tuple (self, option, opt, value, parser_, len, type):
+ def process_tuple(self, option, opt, value, parser_, len, type):
self.assertEqual(len, 3)
self.assert_(type is int)
@@ -1114,7 +1174,7 @@
self.parser = OptionParser(option_list=options)
# Callback that meddles in rargs, largs
- def process_n (self, option, opt, value, parser_):
+ def process_n(self, option, opt, value, parser_):
# option is -3, -5, etc.
nargs = int(opt[1:])
rargs = parser_.rargs
@@ -1143,7 +1203,7 @@
callback=self.process_many, type="int")]
self.parser = OptionParser(option_list=options)
- def process_many (self, option, opt, value, parser_):
+ def process_many(self, option, opt, value, parser_):
if opt == "-a":
self.assertEqual(value, ("foo", "bar"))
elif opt == "--apple":
@@ -1166,7 +1226,7 @@
self.parser.add_option("--foo-bar", action="callback",
callback=self.check_abbrev)
- def check_abbrev (self, option, opt, value, parser):
+ def check_abbrev(self, option, opt, value, parser):
self.assertEqual(opt, "--foo-bar")
def test_abbrev_callback_expansion(self):
@@ -1181,7 +1241,7 @@
self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
option_list=options)
- def variable_args (self, option, opt, value, parser):
+ def variable_args(self, option, opt, value, parser):
self.assert_(value is None)
done = 0
value = []
@@ -1233,7 +1293,7 @@
self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
option_list=options)
- def show_version (self, option, opt, value, parser):
+ def show_version(self, option, opt, value, parser):
parser.values.show_version = 1
class TestConflict(ConflictBase):
@@ -1284,7 +1344,7 @@
def test_conflict_resolve_help(self):
self.assertOutput(["-h"], """\
-options:
+Options:
--verbose increment verbosity
-h, --help show this help message and exit
-v, --version show version
@@ -1323,7 +1383,7 @@
def test_conflict_override_help(self):
self.assertOutput(["-h"], """\
-options:
+Options:
-h, --help show this help message and exit
-n, --dry-run dry run mode
""")
@@ -1336,9 +1396,9 @@
# -- Other testing. ----------------------------------------------------
_expected_help_basic = """\
-usage: bar.py [options]
+Usage: bar.py [options]
-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)
@@ -1347,9 +1407,9 @@
"""
_expected_help_long_opts_first = """\
-usage: bar.py [options]
+Usage: bar.py [options]
-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)
@@ -1362,7 +1422,7 @@
=====
bar.py [options]
-options
+Options
=======
-a APPLE throw APPLEs at basket
--boo=NUM, -b NUM shout "boo!" NUM times (in order to frighten away all the
@@ -1372,9 +1432,9 @@
"""
_expected_help_short_lines = """\
-usage: bar.py [options]
+Usage: bar.py [options]
-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
@@ -1400,10 +1460,26 @@
make_option("--foo", action="append", type="string", dest='foo',
help="store FOO in the foo list for later fooing"),
]
+
+ # We need to set COLUMNS for the OptionParser constructor, but
+ # we must restore its original value -- otherwise, this test
+ # screws things up for other tests when it's part of the Python
+ # test suite.
+ orig_columns = os.environ.get('COLUMNS')
os.environ['COLUMNS'] = str(columns)
- return InterceptingOptionParser(option_list=options)
+ try:
+ return InterceptingOptionParser(option_list=options)
+ finally:
+ if orig_columns is None:
+ del os.environ['COLUMNS']
+ else:
+ os.environ['COLUMNS'] = orig_columns
def assertHelpEquals(self, expected_output):
+ if type(expected_output) is types.UnicodeType:
+ encoding = self.parser._get_encoding(sys.stdout)
+ expected_output = expected_output.encode(encoding, "replace")
+
save_argv = sys.argv[:]
try:
# Make optparse believe bar.py is being executed.
@@ -1416,7 +1492,7 @@
self.assertHelpEquals(_expected_help_basic)
def test_help_old_usage(self):
- self.parser.set_usage("usage: %prog [options]")
+ self.parser.set_usage("Usage: %prog [options]")
self.assertHelpEquals(_expected_help_basic)
def test_help_long_opts_first(self):
@@ -1434,6 +1510,27 @@
self.parser = self.make_parser(60)
self.assertHelpEquals(_expected_help_short_lines)
+ def test_help_unicode(self):
+ self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE)
+ self.parser.add_option("-a", action="store_true", help=u"ol\u00E9!")
+ expect = u"""\
+Options:
+ -h, --help show this help message and exit
+ -a ol\u00E9!
+"""
+ self.assertHelpEquals(expect)
+
+ def test_help_unicode_description(self):
+ self.parser = InterceptingOptionParser(usage=SUPPRESS_USAGE,
+ description=u"ol\u00E9!")
+ expect = u"""\
+ol\u00E9!
+
+Options:
+ -h, --help show this help message and exit
+"""
+ self.assertHelpEquals(expect)
+
def test_help_description_groups(self):
self.parser.set_description(
"This is the program description for %prog. %prog has "
@@ -1446,13 +1543,13 @@
group.add_option("-g", action="store_true", help="Group option.")
self.parser.add_option_group(group)
- self.assertHelpEquals("""\
-usage: bar.py [options]
+ expect = """\
+Usage: bar.py [options]
This is the program description for bar.py. bar.py has an option group as
well as single options.
-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)
@@ -1464,9 +1561,12 @@
that some of them bite.
-g Group option.
-""")
+"""
+ self.assertHelpEquals(expect)
+ self.parser.epilog = "Please report bugs to /dev/null."
+ self.assertHelpEquals(expect + "\nPlease report bugs to /dev/null.\n")
class TestMatchAbbrev(BaseTest):
@@ -1486,6 +1586,43 @@
BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)")
+class TestParseNumber(BaseTest):
+ def setUp(self):
+ self.parser = InterceptingOptionParser()
+ self.parser.add_option("-n", type=int)
+ self.parser.add_option("-l", type=long)
+
+ def test_parse_num_fail(self):
+ self.assertRaises(
+ _parse_num, ("", int), {},
+ ValueError,
+ re.compile(r"invalid literal for int().*: '?'?"))
+ self.assertRaises(
+ _parse_num, ("0xOoops", long), {},
+ ValueError,
+ re.compile(r"invalid literal for long().*: '?0xOoops'?"))
+
+ def test_parse_num_ok(self):
+ self.assertEqual(_parse_num("0", int), 0)
+ self.assertEqual(_parse_num("0x10", int), 16)
+ self.assertEqual(_parse_num("0XA", long), 10L)
+ self.assertEqual(_parse_num("010", long), 8L)
+ self.assertEqual(_parse_num("0b11", int), 3)
+ self.assertEqual(_parse_num("0b", long), 0L)
+
+ def test_numeric_options(self):
+ self.assertParseOK(["-n", "42", "-l", "0x20"],
+ { "n": 42, "l": 0x20 }, [])
+ self.assertParseOK(["-n", "0b0101", "-l010"],
+ { "n": 5, "l": 8 }, [])
+ self.assertParseFail(["-n008"],
+ "option -n: invalid integer value: '008'")
+ self.assertParseFail(["-l0b0123"],
+ "option -l: invalid long integer value: '0b0123'")
+ self.assertParseFail(["-l", "0x12x"],
+ "option -l: invalid long integer value: '0x12x'")
+
+
def _testclasses():
mod = sys.modules[__name__]
return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
Modified: py/branch/py-compat-2.5.2/py/compat/testing/test_textwrap.py
==============================================================================
--- py/trunk/py/compat/testing/test_textwrap.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/testing/test_textwrap.py Tue Sep 2 21:40:21 2008
@@ -5,18 +5,14 @@
# Converted to PyUnit by Peter Hansen <peter at engcorp.com>.
# Currently maintained by Greg Ward.
#
-# $Id: test_textwrap.py 38573 2005-03-05 02:38:33Z gward $
+# $Id: test_textwrap.py 46863 2006-06-11 19:42:51Z tim.peters $
#
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
+from textwrap import TextWrapper, wrap, fill, dedent
+
class BaseTestCase(unittest.TestCase):
'''Parent class with utility methods for textwrap tests.'''
@@ -332,17 +328,18 @@
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)
+ if test_support.have_unicode:
+ 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
@@ -463,38 +460,42 @@
# of IndentTestCase!
class DedentTestCase(unittest.TestCase):
+ def assertUnchanged(self, text):
+ """assert that dedent() has no effect on 'text'"""
+ self.assertEquals(text, dedent(text))
+
def test_dedent_nomargin(self):
# No lines indented.
text = "Hello there.\nHow are you?\nOh good, I'm glad."
- self.assertEquals(dedent(text), text)
+ self.assertUnchanged(text)
# Similar, with a blank line.
text = "Hello there.\n\nBoo!"
- self.assertEquals(dedent(text), text)
+ self.assertUnchanged(text)
# Some lines indented, but overall margin is still zero.
text = "Hello there.\n This is indented."
- self.assertEquals(dedent(text), text)
+ self.assertUnchanged(text)
# Again, add a blank line.
text = "Hello there.\n\n Boo!\n"
- self.assertEquals(dedent(text), text)
+ self.assertUnchanged(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)
+ self.assertEquals(expect, dedent(text))
# 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)
+ self.assertEquals(expect, dedent(text))
# 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)
+ self.assertEquals(expect, dedent(text))
def test_dedent_uneven(self):
# Lines indented unevenly.
@@ -508,18 +509,53 @@
while 1:
return foo
'''
- self.assertEquals(dedent(text), expect)
+ self.assertEquals(expect, dedent(text))
# 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)
+ self.assertEquals(expect, dedent(text))
# 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)
+ self.assertEquals(expect, dedent(text))
+ # dedent() should not mangle internal tabs
+ def test_dedent_preserve_internal_tabs(self):
+ text = " hello\tthere\n how are\tyou?"
+ expect = "hello\tthere\nhow are\tyou?"
+ self.assertEquals(expect, dedent(text))
+
+ # make sure that it preserves tabs when it's not making any
+ # changes at all
+ self.assertEquals(expect, dedent(expect))
+
+ # dedent() should not mangle tabs in the margin (i.e.
+ # tabs and spaces both count as margin, but are *not*
+ # considered equivalent)
+ def test_dedent_preserve_margin_tabs(self):
+ text = " hello there\n\thow are you?"
+ self.assertUnchanged(text)
+
+ # same effect even if we have 8 spaces
+ text = " hello there\n\thow are you?"
+ self.assertUnchanged(text)
+
+ # dedent() only removes whitespace that can be uniformly removed!
+ text = "\thello there\n\thow are you?"
+ expect = "hello there\nhow are you?"
+ self.assertEquals(expect, dedent(text))
+
+ text = " \thello there\n \thow are you?"
+ self.assertEquals(expect, dedent(text))
+
+ text = " \t hello there\n \t how are you?"
+ self.assertEquals(expect, dedent(text))
+
+ text = " \thello there\n \t how are you?"
+ expect = "hello there\n how are you?"
+ self.assertEquals(expect, dedent(text))
def test_main():
Modified: py/branch/py-compat-2.5.2/py/compat/textwrap.py
==============================================================================
--- py/trunk/py/compat/textwrap.py (original)
+++ py/branch/py-compat-2.5.2/py/compat/textwrap.py Tue Sep 2 21:40:21 2008
@@ -317,41 +317,58 @@
# -- Loosely related functionality -------------------------------------
-def dedent(text):
- """dedent(text : string) -> string
+_whitespace_only_re = re.compile('^[ \t]+$', re.MULTILINE)
+_leading_whitespace_re = re.compile('(^[ \t]*)(?:[^ \t\n])', re.MULTILINE)
- Remove any whitespace than can be uniformly removed from the left
- of every line in `text`.
+def dedent(text):
+ """Remove any common leading whitespace from 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'
+ This can be used to make triple-quoted strings line up with the left
+ edge of the display, while still presenting them in the source code
+ in indented form.
+
+ Note that tabs and spaces are both treated as whitespace, but they
+ are not equal: the lines " hello" and "\thello" are
+ considered to have no common leading whitespace. (This behaviour is
+ new in Python 2.5; older versions of this module incorrectly
+ expanded tabs before searching for common leading whitespace.)
"""
- lines = text.expandtabs().split('\n')
+ # Look for the longest leading string of spaces and tabs common to
+ # all lines.
margin = None
- for line in lines:
- content = line.lstrip()
- if not content:
- continue
- indent = len(line) - len(content)
+ text = _whitespace_only_re.sub('', text)
+ indents = _leading_whitespace_re.findall(text)
+ for indent in indents:
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:]
+ # Current line more deeply indented than previous winner:
+ # no change (previous winner is still on top).
+ elif indent.startswith(margin):
+ pass
+
+ # Current line consistent with and no deeper than previous winner:
+ # it's the new winner.
+ elif margin.startswith(indent):
+ margin = indent
+
+ # Current line and previous winner have no common whitespace:
+ # there is no margin.
+ else:
+ margin = ""
+ break
- return '\n'.join(lines)
+ # sanity check (testing/debugging only)
+ if 0 and margin:
+ for line in text.split("\n"):
+ assert not line or line.startswith(margin), \
+ "line = %r, margin = %r" % (line, margin)
+
+ if margin:
+ text = re.sub(r'(?m)^' + margin, '', text)
+ return text
+
+if __name__ == "__main__":
+ #print dedent("\tfoo\n\tbar")
+ #print dedent(" \thello there\n \t how are you?")
+ print dedent("Hello there.\n This is indented.")
Deleted: /py/trunk/py/test/dsession/testing/basetest.py
==============================================================================
--- /py/trunk/py/test/dsession/testing/basetest.py Tue Sep 2 21:40:21 2008
+++ (empty file)
@@ -1,32 +0,0 @@
-
-""" Support module for running tests
-"""
-
-import py
-from py.__.test.testing.setupdata import getexamplefile
-
-class DirSetup(object):
- def setup_method(self, method):
- name = "%s.%s" %(self.__class__.__name__, method.func_name)
- self.tmpdir = py.test.ensuretemp(name)
- self.source = self.tmpdir.ensure("source", dir=1)
- self.dest = self.tmpdir.join("dest")
-
-
-class BasicRsessionTest(object):
- def setup_class(cls):
- path = getexamplefile("test_funcexamples.py")
- cls.config = py.test.config._reparse([path.dirpath()])
- cls.modulecol = cls.config.getfsnode(path)
-
- def setup_method(self, method):
- self.session = self.config.initsession()
-
- def getfunc(self, name):
- funcname = "test_func" + name
- col = self.modulecol.join(funcname)
- assert col is not None, funcname
- return col
-
- def getdocexample(self):
- return getexamplefile("docexample.txt")
Deleted: /py/trunk/py/test/testing/setupdata.py
==============================================================================
--- /py/trunk/py/test/testing/setupdata.py Tue Sep 2 21:40:21 2008
+++ (empty file)
@@ -1,173 +0,0 @@
-import py
-
-#def setup_module(mod):
-# mod.datadir = setupdatadir()
-# mod.tmpdir = py.test.ensuretemp(mod.__name__)
-
-#def setupdatadir():
-# datadir = py.test.ensuretemp("datadir")
-# for name in namecontent:
-# getexamplefile(name)
-# return datadir
-
-def getexamplefile(basename, tmpdir=None):
- if tmpdir is None:
- tmpdir = py.test.ensuretemp("example")
- tmpdir.ensure("__init__.py")
- path = tmpdir.join(basename)
- if not path.check():
- path.write(py.code.Source(namecontent[basename]))
- print "creating testfile", path
- return path
-
-def getexamplecollector(names, tmpdir=None):
- fn = getexamplefile(names[0], tmpdir=tmpdir)
- config = py.test.config._reparse([fn.dirpath()])
- col = config.getfsnode(fn)
- return col._getitembynames(names[1:])
-
-namecontent = {
- 'syntax_error.py': "this is really not python\n",
-
- 'disabled_module.py': '''
- disabled = True
-
- def setup_module(mod):
- raise ValueError
-
- class TestClassOne:
- def test_func(self):
- raise ValueError
-
- class TestClassTwo:
- def setup_class(cls):
- raise ValueError
- def test_func(self):
- raise ValueError
- ''',
-
- 'brokenrepr.py': '''
- import py
-
- class BrokenRepr1:
- """A broken class with lots of broken methods. Let's try to make the test framework
- immune to these."""
- foo=0
- def __repr__(self):
- raise Exception("Ha Ha fooled you, I'm a broken repr().")
-
- class BrokenRepr2:
- """A broken class with lots of broken methods. Let's try to make the test framework
- immune to these."""
- foo=0
- def __repr__(self):
- raise "Ha Ha fooled you, I'm a broken repr()."
-
-
- class TestBrokenClass:
-
- def test_explicit_bad_repr(self):
- t = BrokenRepr1()
- py.test.raises(Exception, 'repr(t)')
-
- def test_implicit_bad_repr1(self):
- t = BrokenRepr1()
- assert t.foo == 1
-
- def test_implicit_bad_repr2(self):
- t = BrokenRepr2()
- assert t.foo == 1
- ''',
-
- 'failingimport.py': "import gruetzelmuetzel\n",
-
- 'test_mod.py': """
- class TestA:
- def test_m1(self):
- pass
- def test_f1():
- pass
- def test_g1():
- yield lambda x: None, 42
- """,
-
- 'file_test.py': """
- def test_one():
- assert 42 == 43
-
- class TestClass(object):
- def test_method_one(self):
- assert 42 == 43
-
- """,
-
- 'test_threepass.py': """
- def test_one():
- assert 1
-
- def test_two():
- assert 1
-
- def test_three():
- assert 1
- """,
-
- 'testspecial_importerror.py': """
-
- import asdasd
-
- """,
-
- 'disabled.py': """
- class TestDisabled:
- disabled = True
- def test_method(self):
- pass
- """,
-
- 'test_funcexamples.py': """
- import py
- import time
- def test_funcpassed():
- pass
-
- def test_funcfailed():
- raise AssertionError("hello world")
-
- def test_funcskipped():
- py.test.skip("skipped")
-
- def test_funcprint():
- print "samfing"
-
- def test_funcprinterr():
- print >>py.std.sys.stderr, "samfing"
-
- def test_funcprintfail():
- print "samfing elz"
- asddsa
-
- def test_funcexplicitfail():
- py.test.fail("3")
-
- def test_funcraisesfails():
- py.test.raises(ValueError, lambda: 123)
-
- def test_funcoptioncustom():
- assert py.test.config.getvalue("custom")
-
- def test_funchang():
- import time
- time.sleep(1000)
-
- def test_funckill15():
- import os
- os.kill(os.getpid(), 15)
- """,
-
- 'docexample.txt': """
- Aha!!!!!!
- =========
- """,
-
-}
Deleted: /py/trunk/py/test/testing/test_repevent.py
==============================================================================
--- /py/trunk/py/test/testing/test_repevent.py Tue Sep 2 21:40:21 2008
+++ (empty file)
@@ -1,30 +0,0 @@
-
-from py.__.test import event
-import setupdata, suptest
-from py.__.code.testing.test_excinfo import TWMock
-
-
-
-class TestItemTestReport(object):
-
- def test_toterminal(self):
- sorter = suptest.events_run_example("file_test.py")
- reports = sorter.get(event.ItemTestReport)
- ev = reports[0]
- assert ev.failed
- twmock = TWMock()
- ev.toterminal(twmock)
- assert twmock.lines
- twmock = TWMock()
- ev.outcome.longrepr = "hello"
- ev.toterminal(twmock)
- assert twmock.lines[0] == "hello"
- assert not twmock.lines[1:]
-
- ##assert ev.repr_run.find("AssertionError") != -1
- filepath = ev.colitem.fspath
- #filepath , modpath = ev.itemrepr_path
- assert str(filepath).endswith("file_test.py")
- #assert modpath.endswith("file_test.test_one")
-
-
More information about the pytest-commit
mailing list