[Jython-checkins] jython: Update doctest.py from CPythonLib at r85505

frank.wierzbicki jython-checkins at python.org
Wed Mar 14 04:09:31 CET 2012


http://hg.python.org/jython/rev/d5f2af98d603
changeset:   6342:d5f2af98d603
user:        Alex Grönholm <alex.gronholm at nextday.fi>
date:        Tue Mar 13 13:19:21 2012 -0700
summary:
  Update doctest.py from CPythonLib at r85505

files:
  Lib/doctest.py |  168 +++++++++++++++++++++++++-----------
  1 files changed, 117 insertions(+), 51 deletions(-)


diff --git a/Lib/doctest.py b/Lib/doctest.py
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -99,6 +99,9 @@
 import unittest, difflib, pdb, tempfile
 import warnings
 from StringIO import StringIO
+from collections import namedtuple
+
+TestResults = namedtuple('TestResults', 'failed attempted')
 
 # There are 4 basic classes:
 #  - Example: a <source, want> pair, plus an intra-docstring line number.
@@ -213,13 +216,21 @@
                 # 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
+    with open(filename) as f:
+        return f.read(), filename
+
+# Use sys.stdout encoding for ouput.
+_encoding = getattr(sys.__stdout__, 'encoding', None) or 'utf-8'
 
 def _indent(s, indent=4):
     """
-    Add the given number of space characters to the beginning every
-    non-blank line in `s`, and return the result.
+    Add the given number of space characters to the beginning of
+    every non-blank line in `s`, and return the result.
+    If the string `s` is Unicode, it is encoded using the stdout
+    encoding and the `backslashreplace` error handler.
     """
+    if isinstance(s, unicode):
+        s = s.encode(_encoding, 'backslashreplace')
     # This regexp matches the start of non-blank lines:
     return re.sub('(?m)^(?!$)', indent*' ', s)
 
@@ -253,6 +264,9 @@
         StringIO.truncate(self, size)
         if hasattr(self, "softspace"):
             del self.softspace
+        if not self.buf:
+            # Reset it to an empty string, to make sure it's not unicode.
+            self.buf = ''
 
 # Worst-case linear-time ellipsis matching.
 def _ellipsis_match(want, got):
@@ -322,6 +336,8 @@
         self.__out = out
         self.__debugger_used = False
         pdb.Pdb.__init__(self, stdout=out)
+        # still use input() to get user input
+        self.use_rawinput = 1
 
     def set_trace(self, frame=None):
         self.__debugger_used = True
@@ -817,7 +833,15 @@
         # given object's docstring.
         try:
             file = inspect.getsourcefile(obj) or inspect.getfile(obj)
-            source_lines = linecache.getlines(file)
+            if module is not None:
+                # Supply the module globals in case the module was
+                # originally loaded via a PEP 302 loader and
+                # file is not a valid filesystem path
+                source_lines = linecache.getlines(file, module.__dict__)
+            else:
+                # No access to a loader, so assume it's a normal
+                # filesystem path
+                source_lines = linecache.getlines(file)
             if not source_lines:
                 source_lines = None
         except TypeError:
@@ -833,6 +857,8 @@
             globs = globs.copy()
         if extraglobs is not None:
             globs.update(extraglobs)
+        if '__name__' not in globs:
+            globs['__name__'] = '__main__'  # provide a default module name
 
         # Recursively expore `obj`, extracting DocTests.
         tests = []
@@ -851,18 +877,12 @@
         """
         if module is None:
             return True
+        elif inspect.getmodule(object) is not None:
+            return module is inspect.getmodule(object)
         elif inspect.isfunction(object):
             return module.__dict__ is object.func_globals
         elif inspect.isclass(object):
-            # XXX: Jython transition 2.5
-            # Java classes appear as Python classes to inspect, but they
-            # have no __module__ http://jython.org/bugs/1758279
-            # org.python.modules uses Java classes to masq
-            if not hasattr(object, '__module__'):
-                return False
             return module.__name__ == object.__module__
-        elif inspect.getmodule(object) is not None:
-            return module is inspect.getmodule(object)
         elif hasattr(object, '__module__'):
             return module.__name__ == object.__module__
         elif isinstance(object, property):
@@ -967,8 +987,6 @@
             filename = getattr(module, '__file__', module.__name__)
             if filename[-4:] in (".pyc", ".pyo"):
                 filename = filename[:-1]
-            elif filename.endswith('$py.class'):
-                filename = '%s.py' % filename[:-9]
         return self._parser.get_doctest(docstring, globs, name,
                                         filename, lineno)
 
@@ -1036,10 +1054,10 @@
         >>> tests.sort(key = lambda test: test.name)
         >>> for test in tests:
         ...     print test.name, '->', runner.run(test)
-        _TestClass -> (0, 2)
-        _TestClass.__init__ -> (0, 2)
-        _TestClass.get -> (0, 2)
-        _TestClass.square -> (0, 1)
+        _TestClass -> TestResults(failed=0, attempted=2)
+        _TestClass.__init__ -> TestResults(failed=0, attempted=2)
+        _TestClass.get -> TestResults(failed=0, attempted=2)
+        _TestClass.square -> TestResults(failed=0, attempted=1)
 
     The `summarize` method prints a summary of all the test cases that
     have been run by the runner, and returns an aggregated `(f, t)`
@@ -1054,7 +1072,7 @@
         7 tests in 4 items.
         7 passed and 0 failed.
         Test passed.
-        (0, 7)
+        TestResults(failed=0, attempted=7)
 
     The aggregated number of tried examples and failed examples is
     also available via the `tries` and `failures` attributes:
@@ -1270,9 +1288,9 @@
 
                 # Another chance if they didn't care about the detail.
                 elif self.optionflags & IGNORE_EXCEPTION_DETAIL:
-                    m1 = re.match(r'[^:]*:', example.exc_msg)
-                    m2 = re.match(r'[^:]*:', exc_msg)
-                    if m1 and m2 and check(m1.group(0), m2.group(0),
+                    m1 = re.match(r'(?:[^:]*\.)?([^:]*:)', example.exc_msg)
+                    m2 = re.match(r'(?:[^:]*\.)?([^:]*:)', exc_msg)
+                    if m1 and m2 and check(m1.group(1), m2.group(1),
                                            self.optionflags):
                         outcome = SUCCESS
 
@@ -1297,7 +1315,7 @@
 
         # Record and return the number of failures and tries.
         self.__record_outcome(test, failures, tries)
-        return failures, tries
+        return TestResults(failures, tries)
 
     def __record_outcome(self, test, f, t):
         """
@@ -1310,13 +1328,16 @@
         self.tries += t
 
     __LINECACHE_FILENAME_RE = re.compile(r'<doctest '
-                                         r'(?P<name>[\w\.]+)'
+                                         r'(?P<name>.+)'
                                          r'\[(?P<examplenum>\d+)\]>$')
     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)
+            source = example.source
+            if isinstance(source, unicode):
+                source = source.encode('ascii', 'backslashreplace')
+            return source.splitlines(True)
         else:
             return self.save_linecache_getlines(filename, module_globals)
 
@@ -1365,12 +1386,17 @@
         self.save_linecache_getlines = linecache.getlines
         linecache.getlines = self.__patched_linecache_getlines
 
+        # Make sure sys.displayhook just prints the value to stdout
+        save_displayhook = sys.displayhook
+        sys.displayhook = sys.__displayhook__
+
         try:
             return self.__run(test, compileflags, out)
         finally:
             sys.stdout = save_stdout
             pdb.set_trace = save_set_trace
             linecache.getlines = self.save_linecache_getlines
+            sys.displayhook = save_displayhook
             if clear_globs:
                 test.globs.clear()
 
@@ -1429,7 +1455,7 @@
             print "***Test Failed***", totalf, "failures."
         elif verbose:
             print "Test passed."
-        return totalf, totalt
+        return TestResults(totalf, totalt)
 
     #/////////////////////////////////////////////////////////////////
     # Backward compatibility cruft to maintain doctest.master.
@@ -1438,8 +1464,10 @@
         d = self._name2ft
         for name, (f, t) in other._name2ft.items():
             if name in d:
-                print "*** DocTestRunner.merge: '" + name + "' in both" \
-                    " testers; summing outcomes."
+                # Don't print here by default, since doing
+                #     so breaks some of the buildbots
+                #print "*** DocTestRunner.merge: '" + name + "' in both" \
+                #    " testers; summing outcomes."
                 f2, t2 = d[name]
                 f = f + f2
                 t = t + t2
@@ -1675,8 +1703,7 @@
 
        If a failure or error occurs, the globals are left intact:
 
-         >>> if '__builtins__' in test.globs:
-         ...     del test.globs['__builtins__']
+         >>> del test.globs['__builtins__']
          >>> test.globs
          {'x': 1}
 
@@ -1690,8 +1717,7 @@
          ...
          UnexpectedException: <DocTest foo from foo.py:0 (2 examples)>
 
-         >>> if '__builtins__' in test.globs:
-         ...     del test.globs['__builtins__']
+         >>> del test.globs['__builtins__']
          >>> test.globs
          {'x': 2}
 
@@ -1702,7 +1728,7 @@
          ...      ''', {}, 'foo', 'foo.py', 0)
 
          >>> runner.run(test)
-         (0, 1)
+         TestResults(failed=0, attempted=1)
 
          >>> test.globs
          {}
@@ -1748,7 +1774,7 @@
 
     Return (#failures, #tests).
 
-    See doctest.__doc__ for an overview.
+    See help(doctest) for an overview.
 
     Optional keyword arg "name" gives the name of the module; by default
     use m.__name__.
@@ -1832,7 +1858,7 @@
     else:
         master.merge(runner)
 
-    return runner.failures, runner.tries
+    return TestResults(runner.failures, runner.tries)
 
 def testfile(filename, module_relative=True, name=None, package=None,
              globs=None, verbose=None, report=True, optionflags=0,
@@ -1934,6 +1960,8 @@
         globs = globs.copy()
     if extraglobs is not None:
         globs.update(extraglobs)
+    if '__name__' not in globs:
+        globs['__name__'] = '__main__'
 
     if raise_on_error:
         runner = DebugRunner(verbose=verbose, optionflags=optionflags)
@@ -1955,7 +1983,7 @@
     else:
         master.merge(runner)
 
-    return runner.failures, runner.tries
+    return TestResults(runner.failures, runner.tries)
 
 def run_docstring_examples(f, globs, verbose=False, name="NoName",
                            compileflags=None, optionflags=0):
@@ -2014,7 +2042,7 @@
         (f,t) = self.testrunner.run(test)
         if self.verbose:
             print f, "of", t, "examples failed in string", name
-        return (f,t)
+        return TestResults(f,t)
 
     def rundoc(self, object, name=None, module=None):
         f = t = 0
@@ -2023,19 +2051,19 @@
         for test in tests:
             (f2, t2) = self.testrunner.run(test)
             (f,t) = (f+f2, t+t2)
-        return (f,t)
+        return TestResults(f,t)
 
     def rundict(self, d, name, module=None):
-        import new
-        m = new.module(name)
+        import types
+        m = types.ModuleType(name)
         m.__dict__.update(d)
         if module is None:
             module = False
         return self.rundoc(m, name, module)
 
     def run__test__(self, d, name):
-        import new
-        m = new.module(name)
+        import types
+        m = types.ModuleType(name)
         m.__test__ = d
         return self.rundoc(m, name)
 
@@ -2218,7 +2246,7 @@
         self.setUp()
         runner = DebugRunner(optionflags=self._dt_optionflags,
                              checker=self._dt_checker, verbose=False)
-        runner.run(self._dt_test)
+        runner.run(self._dt_test, clear_globs=False)
         self.tearDown()
 
     def id(self):
@@ -2233,6 +2261,19 @@
     def shortDescription(self):
         return "Doctest: " + self._dt_test.name
 
+class SkipDocTestCase(DocTestCase):
+    def __init__(self):
+        DocTestCase.__init__(self, None)
+
+    def setUp(self):
+        self.skipTest("DocTestSuite will not work with -O2 and above")
+
+    def test_skip(self):
+        pass
+
+    def shortDescription(self):
+        return "Skipping tests from %s" % module.__name__
+
 def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
                  **options):
     """
@@ -2275,15 +2316,20 @@
 
     module = _normalize_module(module)
     tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
-    if globs is None:
-        globs = module.__dict__
-    if not tests:
+
+    if not tests and sys.flags.optimize >=2:
+        # Skip doctests when running with -O2
+        suite = unittest.TestSuite()
+        suite.addTest(SkipDocTestCase())
+        return suite
+    elif not tests:
         # Why do we want to do this? Because it reveals a bug that might
         # otherwise be hidden.
         raise ValueError(module, "has no tests")
 
     tests.sort()
     suite = unittest.TestSuite()
+
     for test in tests:
         if len(test.examples) == 0:
             continue
@@ -2291,8 +2337,6 @@
             filename = module.__file__
             if filename[-4:] in (".pyc", ".pyo"):
                 filename = filename[:-1]
-            elif filename.endswith('$py.class'):
-                filename = '%s.py' % filename[:-9]
             test.filename = filename
         suite.addTest(DocTestCase(test, **options))
 
@@ -2657,9 +2701,31 @@
             """,
            }
 
+
 def _test():
-    r = unittest.TextTestRunner()
-    r.run(DocTestSuite())
+    testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-']
+    if not testfiles:
+        name = os.path.basename(sys.argv[0])
+        if '__loader__' in globals():          # python -m
+            name, _ = os.path.splitext(name)
+        print("usage: {0} [-v] file ...".format(name))
+        return 2
+    for filename in testfiles:
+        if filename.endswith(".py"):
+            # It is a module -- insert its dir into sys.path and try to
+            # import it. If it is part of a package, that possibly
+            # won't work because of package imports.
+            dirname, filename = os.path.split(filename)
+            sys.path.insert(0, dirname)
+            m = __import__(filename[:-3])
+            del sys.path[0]
+            failures, _ = testmod(m)
+        else:
+            failures, _ = testfile(filename, module_relative=False)
+        if failures:
+            return 1
+    return 0
+
 
 if __name__ == "__main__":
-    _test()
+    sys.exit(_test())

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list