[Python-checkins] python/dist/src/Lib doctest.py,1.35,1.36

dcjim at users.sourceforge.net dcjim at users.sourceforge.net
Wed Jul 14 21:06:52 CEST 2004


Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15309/Lib

Modified Files:
	doctest.py 
Log Message:
Ported some features from zope:

- Fixed the display of tests in verbose output
- Allow setUp and tearDown functions to be provided for DocTestSuites.


Index: doctest.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** doctest.py	7 Jul 2004 20:54:45 -0000	1.35
--- doctest.py	14 Jul 2004 19:06:50 -0000	1.36
***************
*** 290,294 ****
      'is_private',
      'Tester',
-     'DocTestTestFailure',
      'DocTestSuite',
      'testsource',
--- 290,293 ----
***************
*** 1290,1353 ****
      return tests
  
! # unittest helpers.
  
! # A function passed to unittest, for unittest to drive.
! # tester is doctest Tester instance.  doc is the docstring whose
! # doctests are to be run.
  
! def _utest(tester, name, doc, filename, lineno):
!     import sys
!     from StringIO import StringIO
  
!     old = sys.stdout
!     sys.stdout = new = StringIO()
!     try:
!         failures, tries = tester.runstring(doc, name)
!     finally:
!         sys.stdout = old
  
!     if failures:
!         msg = new.getvalue()
!         lname = '.'.join(name.split('.')[-1:])
!         if not lineno:
!             lineno = "0 (don't know line number)"
!         # Don't change this format!  It was designed so that Emacs can
!         # parse it naturally.
!         raise DocTestTestFailure('Failed doctest test for %s\n'
!                                  '  File "%s", line %s, in %s\n\n%s' %
!                                  (name, filename, lineno, lname, msg))
  
! class DocTestTestFailure(Exception):
!     """A doctest test failed"""
  
! def DocTestSuite(module=None):
!     """Convert doctest tests for a module to a unittest TestSuite.
  
!     The returned TestSuite is to be run by the unittest framework, and
!     runs each doctest in the module.  If any of the doctests fail,
!     then the synthesized unit test fails, and an error is raised showing
!     the name of the file containing the test and a (sometimes approximate)
!     line number.
  
!     The optional module argument provides the module to be tested.  It
!     can be a module object or a (possibly dotted) module name.  If not
!     specified, the module calling DocTestSuite() is used.
  
!     Example (although note that unittest supplies many ways to use the
!     TestSuite returned; see the unittest docs):
  
!         import unittest
!         import doctest
!         import my_module_with_doctests
  
!         suite = doctest.DocTestSuite(my_module_with_doctests)
!         runner = unittest.TextTestRunner()
!         runner.run(suite)
!     """
  
!     import unittest
  
-     module = _normalize_module(module)
-     tests = _find_tests(module)
      if not tests:
          raise ValueError(module, "has no tests")
--- 1289,1378 ----
      return tests
  
! ###############################################################################
! # unitest support
  
! from StringIO import StringIO
! import os
! import sys
! import tempfile
! import unittest
  
! class DocTestTestCase(unittest.TestCase):
!     """A test case that wraps a test function.
  
!     This is useful for slipping pre-existing test functions into the
!     PyUnit framework. Optionally, set-up and tidy-up functions can be
!     supplied. As with TestCase, the tidy-up ('tearDown') function will
!     always be called if the set-up ('setUp') function ran successfully.
!     """
  
!     def __init__(self, tester, name, doc, filename, lineno,
!                  setUp=None, tearDown=None):
!         unittest.TestCase.__init__(self)
!         (self.__tester, self.__name, self.__doc,
!          self.__filename, self.__lineno,
!          self.__setUp, self.__tearDown
!          ) = tester, name, doc, filename, lineno, setUp, tearDown
  
!     def setUp(self):
!         if self.__setUp is not None:
!             self.__setUp()
  
!     def tearDown(self):
!         if self.__tearDown is not None:
!             self.__tearDown()
  
!     def runTest(self):
!         old = sys.stdout
!         new = StringIO()
!         try:
!             sys.stdout = new
!             failures, tries = self.__tester.runstring(self.__doc, self.__name)
!         finally:
!             sys.stdout = old
  
!         if failures:
!             lname = '.'.join(self.__name.split('.')[-1:])
!             lineno = self.__lineno or "0 (don't know line no)"
!             raise self.failureException(
!                 'Failed doctest test for %s\n'
!                 '  File "%s", line %s, in %s\n\n%s'
!                 % (self.__name, self.__filename, lineno, lname, new.getvalue())
!                 )
  
!     def id(self):
!         return self.__name
  
!     def __repr__(self):
!         name = self.__name.split('.')
!         return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
  
!     __str__ = __repr__
  
!     def shortDescription(self):
!         return "Doctest: " + self.__name
! 
! 
! def DocTestSuite(module=None,
!                  setUp=lambda: None,
!                  tearDown=lambda: None,
!                  ):
!     """Convert doctest tests for a mudule to a unittest test suite
! 
!     This tests convers each documentation string in a module that
!     contains doctest tests to a unittest test case. If any of the
!     tests in a doc string fail, then the test case fails. An error is
!     raised showing the name of the file containing the test and a
!     (sometimes approximate) line number.
! 
!     A module argument provides the module to be tested. The argument
!     can be either a module or a module name.
! 
!     If no argument is given, the calling module is used.
! 
!     """
!     module = _normalizeModule(module)
!     tests = _findTests(module)
  
      if not tests:
          raise ValueError(module, "has no tests")
***************
*** 1363,1378 ****
              elif filename.endswith(".pyo"):
                  filename = filename[:-1]
!         def runit(name=name, doc=doc, filename=filename, lineno=lineno):
!             _utest(tester, name, doc, filename, lineno)
!         suite.addTest(unittest.FunctionTestCase(
!                                     runit,
!                                     description="doctest of " + name))
      return suite
  
! # Debugging support.
  
  def _expect(expect):
!     # Return the expected output (if any), formatted as a Python
!     # comment block.
      if expect:
          expect = "\n# ".join(expect.split("\n"))
--- 1388,1472 ----
              elif filename.endswith(".pyo"):
                  filename = filename[:-1]
! 
!         suite.addTest(DocTestTestCase(
!             tester, name, doc, filename, lineno,
!             setUp, tearDown))
! 
      return suite
  
! def _normalizeModule(module):
!     # Normalize a module
!     if module is None:
!         # Test the calling module
!         module = sys._getframe(2).f_globals['__name__']
!         module = sys.modules[module]
! 
!     elif isinstance(module, (str, unicode)):
!         module = __import__(module, globals(), locals(), ["*"])
! 
!     return module
! 
! def _doc(name, object, tests, prefix, filename='', lineno=''):
!     doc = getattr(object, '__doc__', '')
!     if doc and doc.find('>>>') >= 0:
!         tests.append((prefix+name, doc, filename, lineno))
! 
! 
! def _findTests(module, prefix=None):
!     if prefix is None:
!         prefix = module.__name__
!     dict = module.__dict__
!     tests = []
!     _doc(prefix, module, tests, '',
!          lineno="1 (or below)")
!     prefix = prefix and (prefix + ".")
!     _find(dict.items(), module, dict, tests, prefix)
!     return tests
! 
! def _find(items, module, dict, tests, prefix, minlineno=0):
!     for name, object in items:
! 
!         # Only interested in named objects
!         if not hasattr(object, '__name__'):
!             continue
! 
!         if hasattr(object, 'func_globals'):
!             # Looks like a func
!             if object.func_globals is not dict:
!                 # Non-local func
!                 continue
!             code = getattr(object, 'func_code', None)
!             filename = getattr(code, 'co_filename', '')
!             lineno = getattr(code, 'co_firstlineno', -1) + 1
!             if minlineno:
!                 minlineno = min(lineno, minlineno)
!             else:
!                 minlineno = lineno
!             _doc(name, object, tests, prefix, filename, lineno)
! 
!         elif hasattr(object, "__module__"):
!             # Maybe a class-like things. In which case, we care
!             if object.__module__ != module.__name__:
!                 continue # not the same module
!             if not (hasattr(object, '__dict__')
!                     and hasattr(object, '__bases__')):
!                 continue # not a class
! 
!             lineno = _find(object.__dict__.items(), module, dict, tests,
!                            prefix+name+".")
! 
!             _doc(name, object, tests, prefix,
!                  lineno="%s (or above)" % (lineno-3))
! 
!     return minlineno
! 
! # end unitest support
! ###############################################################################
! 
! ###############################################################################
! # debugger
  
  def _expect(expect):
!     # Return the expected output, if any
      if expect:
          expect = "\n# ".join(expect.split("\n"))
***************
*** 1381,1438 ****
  
  def testsource(module, name):
!     """Extract the doctest examples from a docstring.
  
      Provide the module (or dotted name of the module) containing the
!     tests to be extracted, and the name (within the module) of the object
!     with the docstring containing the tests to be extracted.
  
-     The doctest examples are returned as a string containing Python
-     code.  The expected output blocks in the examples are converted
-     to Python comments.
      """
! 
!     module = _normalize_module(module)
!     tests = _find_tests(module, "")
!     test = [doc for (tname, doc, dummy, dummy) in tests
!                 if tname == name]
      if not test:
          raise ValueError(name, "not found in tests")
      test = test[0]
!     examples = [source + _expect(expect)
!                 for source, expect, dummy in _extract_examples(test)]
!     return '\n'.join(examples)
! 
! def debug(module, name):
!     """Debug a single docstring containing doctests.
  
!     Provide the module (or dotted name of the module) containing the
!     docstring to be debugged, and the name (within the module) of the
!     object with the docstring to be debugged.
  
!     The doctest examples are extracted (see function testsource()),
!     and written to a temp file.  The Python debugger (pdb) is then
!     invoked on that file.
      """
  
!     import os
      import pdb
-     import tempfile
  
-     module = _normalize_module(module)
-     testsrc = testsource(module, name)
      srcfilename = tempfile.mktemp("doctestdebug.py")
!     f = file(srcfilename, 'w')
!     f.write(testsrc)
!     f.close()
  
-     globs = {}
-     globs.update(module.__dict__)
      try:
!         # Note that %r is vital here.  '%s' instead can, e.g., cause
!         # backslashes to get treated as metacharacters on Windows.
!         pdb.run("execfile(%r)" % srcfilename, globs, globs)
      finally:
          os.remove(srcfilename)
  
  
  
--- 1475,1551 ----
  
  def testsource(module, name):
!     """Extract the test sources from a doctest test docstring as a script
  
      Provide the module (or dotted name of the module) containing the
!     test to be debugged and the name (within the module) of the object
!     with the doc string with tests to be debugged.
  
      """
!     module = _normalizeModule(module)
!     tests = _findTests(module, "")
!     test = [doc for (tname, doc, f, l) in tests if tname == name]
      if not test:
          raise ValueError(name, "not found in tests")
      test = test[0]
!     # XXX we rely on an internal doctest function:
!     examples = _extract_examples(test)
!     testsrc = '\n'.join([
!         "%s%s" % (source, _expect(expect))
!         for (source, expect, lineno) in examples
!         ])
!     return testsrc
  
! def debug_src(src, pm=False, globs=None):
!     """Debug a single doctest test doc string
  
!     The string is provided directly
      """
+     # XXX we rely on an internal doctest function:
+     examples = _extract_examples(src)
+     src = '\n'.join([
+         "%s%s" % (source, _expect(expect))
+         for (source, expect, lineno) in examples
+         ])
+     debug_script(src, pm, globs)
  
! def debug_script(src, pm=False, globs=None):
!     "Debug a test script"
      import pdb
  
      srcfilename = tempfile.mktemp("doctestdebug.py")
!     open(srcfilename, 'w').write(src)
!     if globs:
!         globs = globs.copy()
!     else:
!         globs = {}
  
      try:
!         if pm:
!             try:
!                 execfile(srcfilename, globs, globs)
!             except:
!                 print sys.exc_info()[1]
!                 pdb.post_mortem(sys.exc_info()[2])
!         else:
!             # Note that %r is vital here.  '%s' instead can, e.g., cause
!             # backslashes to get treated as metacharacters on Windows.
!             pdb.run("execfile(%r)" % srcfilename, globs, globs)
      finally:
          os.remove(srcfilename)
  
+ def debug(module, name, pm=False):
+     """Debug a single doctest test doc string
+ 
+     Provide the module (or dotted name of the module) containing the
+     test to be debugged and the name (within the module) of the object
+     with the doc string with tests to be debugged.
+ 
+     """
+     module = _normalizeModule(module)
+     testsrc = testsource(module, name)
+     debug_script(testsrc, pm, module.__dict__)
+ 
+ # end debugger
+ ###############################################################################
  
  



More information about the Python-checkins mailing list