[Python-checkins] python/dist/src/Lib doctest.py,1.46,1.47

edloper at users.sourceforge.net edloper at users.sourceforge.net
Mon Aug 9 04:45:43 CEST 2004


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

Modified Files:
	doctest.py 
Log Message:
- Split DocTestRunner's check_output and output_difference methods off
  into their own class, OutputChecker.
- Added optional OutputChecker arguments to DocTestRunner,
  DocTestCase, DocTestSuite.


Index: doctest.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v
retrieving revision 1.46
retrieving revision 1.47
diff -C2 -d -r1.46 -r1.47
*** doctest.py	9 Aug 2004 02:06:06 -0000	1.46
--- doctest.py	9 Aug 2004 02:45:41 -0000	1.47
***************
*** 1063,1069 ****
  ######################################################################
  
- # [XX] Should overridable methods (eg DocTestRunner.check_output) be
- # named with a leading underscore?
- 
  class DocTestRunner:
      """
--- 1063,1066 ----
***************
*** 1106,1115 ****
  
      The comparison between expected outputs and actual outputs is done
!     by the `check_output` method.  This comparison may be customized
!     with a number of option flags; see the documentation for `testmod`
!     for more information.  If the option flags are insufficient, then
!     the comparison may also be customized by subclassing
!     DocTestRunner, and overriding the methods `check_output` and
!     `output_difference`.
  
      The test runner's display output can be controlled in two ways.
--- 1103,1111 ----
  
      The comparison between expected outputs and actual outputs is done
!     by an `OutputChecker`.  This comparison may be customized with a
!     number of option flags; see the documentation for `testmod` for
!     more information.  If the option flags are insufficient, then the
!     comparison may also be customized by passing a subclass of
!     `OutputChecker` to the constructor.
  
      The test runner's display output can be controlled in two ways.
***************
*** 1126,1133 ****
      DIVIDER = "*" * 70
  
!     def __init__(self, verbose=None, optionflags=0):
          """
          Create a new test runner.
  
          Optional keyword arg 'verbose' prints lots of stuff if true,
          only failures if false; by default, it's true iff '-v' is in
--- 1122,1133 ----
      DIVIDER = "*" * 70
  
!     def __init__(self, checker=None, verbose=None, optionflags=0):
          """
          Create a new test runner.
  
+         Optional keyword arg `checker` is the `OutputChecker` that
+         should be used to compare the expected outputs and actual
+         outputs of doctest examples.
+ 
          Optional keyword arg 'verbose' prints lots of stuff if true,
          only failures if false; by default, it's true iff '-v' is in
***************
*** 1139,1142 ****
--- 1139,1143 ----
          more information.
          """
+         self._checker = checker or OutputChecker()
          if verbose is None:
              verbose = '-v' in sys.argv
***************
*** 1153,1264 ****
  
      #/////////////////////////////////////////////////////////////////
-     # Output verification methods
-     #/////////////////////////////////////////////////////////////////
-     # These two methods should be updated together, since the
-     # output_difference method needs to know what should be considered
-     # to match by check_output.
- 
-     def check_output(self, want, got):
-         """
-         Return True iff the actual output (`got`) matches the expected
-         output (`want`).  These strings are always considered to match
-         if they are identical; but depending on what option flags the
-         test runner is using, several non-exact match types are also
-         possible.  See the documentation for `TestRunner` for more
-         information about option flags.
-         """
-         # Handle the common case first, for efficiency:
-         # if they're string-identical, always return true.
-         if got == want:
-             return True
- 
-         # The values True and False replaced 1 and 0 as the return
-         # value for boolean comparisons in Python 2.3.
-         if not (self.optionflags & DONT_ACCEPT_TRUE_FOR_1):
-             if (got,want) == ("True\n", "1\n"):
-                 return True
-             if (got,want) == ("False\n", "0\n"):
-                 return True
- 
-         # <BLANKLINE> can be used as a special sequence to signify a
-         # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used.
-         if not (self.optionflags & DONT_ACCEPT_BLANKLINE):
-             # Replace <BLANKLINE> in want with a blank line.
-             want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER),
-                           '', want)
-             # If a line in got contains only spaces, then remove the
-             # spaces.
-             got = re.sub('(?m)^\s*?$', '', got)
-             if got == want:
-                 return True
- 
-         # This flag causes doctest to ignore any differences in the
-         # contents of whitespace strings.  Note that this can be used
-         # in conjunction with the ELLISPIS flag.
-         if (self.optionflags & NORMALIZE_WHITESPACE):
-             got = ' '.join(got.split())
-             want = ' '.join(want.split())
-             if got == want:
-                 return True
- 
-         # The ELLIPSIS flag says to let the sequence "..." in `want`
-         # match any substring in `got`.  We implement this by
-         # transforming `want` into a regular expression.
-         if (self.optionflags & ELLIPSIS):
-             # Escape any special regexp characters
-             want_re = re.escape(want)
-             # Replace ellipsis markers ('...') with .*
-             want_re = want_re.replace(re.escape(ELLIPSIS_MARKER), '.*')
-             # Require that it matches the entire string; and set the
-             # re.DOTALL flag (with '(?s)').
-             want_re = '(?s)^%s$' % want_re
-             # Check if the `want_re` regexp matches got.
-             if re.match(want_re, got):
-                 return True
- 
-         # We didn't find any match; return false.
-         return False
- 
-     def output_difference(self, want, got):
-         """
-         Return a string describing the differences between the
-         expected output (`want`) and the actual output (`got`).
-         """
-         # If <BLANKLINE>s are being used, then replace <BLANKLINE>
-         # with blank lines in the expected output string.
-         if not (self.optionflags & DONT_ACCEPT_BLANKLINE):
-             want = re.sub('(?m)^%s$' % re.escape(BLANKLINE_MARKER), '', want)
- 
-         # Check if we should use diff.  Don't use diff if the actual
-         # or expected outputs are too short, or if the expected output
-         # contains an ellipsis marker.
-         if ((self.optionflags & (UNIFIED_DIFF | CONTEXT_DIFF)) and
-             want.count('\n') > 2 and got.count('\n') > 2 and
-             not (self.optionflags & ELLIPSIS and '...' in want)):
-             # Split want & got into lines.
-             want_lines = [l+'\n' for l in want.split('\n')]
-             got_lines = [l+'\n' for l in got.split('\n')]
-             # Use difflib to find their differences.
-             if self.optionflags & UNIFIED_DIFF:
-                 diff = difflib.unified_diff(want_lines, got_lines, n=2,
-                                             fromfile='Expected', tofile='Got')
-                 kind = 'unified'
-             elif self.optionflags & CONTEXT_DIFF:
-                 diff = difflib.context_diff(want_lines, got_lines, n=2,
-                                             fromfile='Expected', tofile='Got')
-                 kind = 'context'
-             else:
-                 assert 0, 'Bad diff option'
-             # Remove trailing whitespace on diff output.
-             diff = [line.rstrip() + '\n' for line in diff]
-             return _tag_msg("Differences (" + kind + " diff)",
-                             ''.join(diff))
- 
-         # If we're not using diff, then simply list the expected
-         # output followed by the actual output.
-         return (_tag_msg("Expected", want or "Nothing") +
-                 _tag_msg("Got", got))
- 
-     #/////////////////////////////////////////////////////////////////
      # Reporting methods
      #/////////////////////////////////////////////////////////////////
--- 1154,1157 ----
***************
*** 1287,1291 ****
          # Print an error message.
          out(self.__failure_header(test, example) +
!             self.output_difference(example.want, got))
  
      def report_unexpected_exception(self, out, test, example, exc_info):
--- 1180,1185 ----
          # Print an error message.
          out(self.__failure_header(test, example) +
!             self._checker.output_difference(example.want, got,
!                                             self.optionflags))
  
      def report_unexpected_exception(self, out, test, example, exc_info):
***************
*** 1413,1417 ****
              # then verify its output and report its outcome.
              if exception is None:
!                 if self.check_output(example.want, got):
                      self.report_success(out, test, example, got)
                  else:
--- 1307,1312 ----
              # then verify its output and report its outcome.
              if exception is None:
!                 if self._checker.check_output(example.want, got,
!                                               self.optionflags):
                      self.report_success(out, test, example, got)
                  else:
***************
*** 1437,1442 ****
                      # the exception description match the values given
                      # in `want`.
!                     if (self.check_output(m.group('out'), got) and
!                         self.check_output(m.group('exc'), exc_msg)):
                          # Is +exc_msg the right thing here??
                          self.report_success(out, test, example,
--- 1332,1339 ----
                      # the exception description match the values given
                      # in `want`.
!                     if (self._checker.check_output(m.group('out'), got,
!                                                    self.optionflags) and
!                         self._checker.check_output(m.group('exc'), exc_msg,
!                                                    self.optionflags)):
                          # Is +exc_msg the right thing here??
                          self.report_success(out, test, example,
***************
*** 1555,1558 ****
--- 1452,1564 ----
          return totalf, totalt
  
+ class OutputChecker:
+     """
+     A class used to check the whether the actual output from a doctest
+     example matches the expected output.  `OutputChecker` defines two
+     methods: `check_output`, which compares a given pair of outputs,
+     and returns true if they match; and `output_difference`, which
+     returns a string describing the differences between two outputs.
+     """
+     def check_output(self, want, got, optionflags):
+         """
+         Return True iff the actual output (`got`) matches the expected
+         output (`want`).  These strings are always considered to match
+         if they are identical; but depending on what option flags the
+         test runner is using, several non-exact match types are also
+         possible.  See the documentation for `TestRunner` for more
+         information about option flags.
+         """
+         # Handle the common case first, for efficiency:
+         # if they're string-identical, always return true.
+         if got == want:
+             return True
+ 
+         # The values True and False replaced 1 and 0 as the return
+         # value for boolean comparisons in Python 2.3.
+         if not (optionflags & DONT_ACCEPT_TRUE_FOR_1):
+             if (got,want) == ("True\n", "1\n"):
+                 return True
+             if (got,want) == ("False\n", "0\n"):
+                 return True
+ 
+         # <BLANKLINE> can be used as a special sequence to signify a
+         # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used.
+         if not (optionflags & DONT_ACCEPT_BLANKLINE):
+             # Replace <BLANKLINE> in want with a blank line.
+             want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER),
+                           '', want)
+             # If a line in got contains only spaces, then remove the
+             # spaces.
+             got = re.sub('(?m)^\s*?$', '', got)
+             if got == want:
+                 return True
+ 
+         # This flag causes doctest to ignore any differences in the
+         # contents of whitespace strings.  Note that this can be used
+         # in conjunction with the ELLISPIS flag.
+         if (optionflags & NORMALIZE_WHITESPACE):
+             got = ' '.join(got.split())
+             want = ' '.join(want.split())
+             if got == want:
+                 return True
+ 
+         # The ELLIPSIS flag says to let the sequence "..." in `want`
+         # match any substring in `got`.  We implement this by
+         # transforming `want` into a regular expression.
+         if (optionflags & ELLIPSIS):
+             # Escape any special regexp characters
+             want_re = re.escape(want)
+             # Replace ellipsis markers ('...') with .*
+             want_re = want_re.replace(re.escape(ELLIPSIS_MARKER), '.*')
+             # Require that it matches the entire string; and set the
+             # re.DOTALL flag (with '(?s)').
+             want_re = '(?s)^%s$' % want_re
+             # Check if the `want_re` regexp matches got.
+             if re.match(want_re, got):
+                 return True
+ 
+         # We didn't find any match; return false.
+         return False
+ 
+     def output_difference(self, want, got, optionflags):
+         """
+         Return a string describing the differences between the
+         expected output (`want`) and the actual output (`got`).
+         """
+         # If <BLANKLINE>s are being used, then replace <BLANKLINE>
+         # with blank lines in the expected output string.
+         if not (optionflags & DONT_ACCEPT_BLANKLINE):
+             want = re.sub('(?m)^%s$' % re.escape(BLANKLINE_MARKER), '', want)
+ 
+         # Check if we should use diff.  Don't use diff if the actual
+         # or expected outputs are too short, or if the expected output
+         # contains an ellipsis marker.
+         if ((optionflags & (UNIFIED_DIFF | CONTEXT_DIFF)) and
+             want.count('\n') > 2 and got.count('\n') > 2 and
+             not (optionflags & ELLIPSIS and '...' in want)):
+             # Split want & got into lines.
+             want_lines = [l+'\n' for l in want.split('\n')]
+             got_lines = [l+'\n' for l in got.split('\n')]
+             # Use difflib to find their differences.
+             if optionflags & UNIFIED_DIFF:
+                 diff = difflib.unified_diff(want_lines, got_lines, n=2,
+                                             fromfile='Expected', tofile='Got')
+                 kind = 'unified'
+             elif optionflags & CONTEXT_DIFF:
+                 diff = difflib.context_diff(want_lines, got_lines, n=2,
+                                             fromfile='Expected', tofile='Got')
+                 kind = 'context'
+             else:
+                 assert 0, 'Bad diff option'
+             # Remove trailing whitespace on diff output.
+             diff = [line.rstrip() + '\n' for line in diff]
+             return _tag_msg("Differences (" + kind + " diff)",
+                             ''.join(diff))
+ 
+         # If we're not using diff, then simply list the expected
+         # output followed by the actual output.
+         return (_tag_msg("Expected", want or "Nothing") +
+                 _tag_msg("Got", got))
+ 
  class DocTestFailure(Exception):
      """A DocTest example has failed in debugging mode.
***************
*** 1938,1944 ****
  class DocTestCase(unittest.TestCase):
  
!     def __init__(self, test, optionflags=0, setUp=None, tearDown=None):
          unittest.TestCase.__init__(self)
          self._dt_optionflags = optionflags
          self._dt_test = test
          self._dt_setUp = setUp
--- 1944,1952 ----
  class DocTestCase(unittest.TestCase):
  
!     def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
!                  checker=None):
          unittest.TestCase.__init__(self)
          self._dt_optionflags = optionflags
+         self._dt_checker = checker
          self._dt_test = test
          self._dt_setUp = setUp
***************
*** 1957,1961 ****
          old = sys.stdout
          new = StringIO()
!         runner = DocTestRunner(optionflags=self._dt_optionflags, verbose=False)
  
          try:
--- 1965,1970 ----
          old = sys.stdout
          new = StringIO()
!         runner = DocTestRunner(optionflags=self._dt_optionflags,
!                                checker=self._dt_checker, verbose=False)
  
          try:
***************
*** 2046,2050 ****
             """
  
!         runner = DebugRunner(verbose = False, optionflags=self._dt_optionflags)
          runner.run(self._dt_test, out=nooutput)
  
--- 2055,2060 ----
             """
  
!         runner = DebugRunner(optionflags=self._dt_optionflags,
!                              checker=self._dt_checker, verbose=False)
          runner.run(self._dt_test, out=nooutput)
  
***************
*** 2066,2070 ****
  def DocTestSuite(module=None, globs=None, extraglobs=None,
                   optionflags=0, test_finder=None,
!                  setUp=lambda: None, tearDown=lambda: None):
      """
      Convert doctest tests for a mudule to a unittest test suite.
--- 2076,2081 ----
  def DocTestSuite(module=None, globs=None, extraglobs=None,
                   optionflags=0, test_finder=None,
!                  setUp=lambda: None, tearDown=lambda: None,
!                  checker=None):
      """
      Convert doctest tests for a mudule to a unittest test suite.
***************
*** 2104,2108 ****
                  filename = filename[:-1]
              test.filename = filename
!         suite.addTest(DocTestCase(test, optionflags, setUp, tearDown))
  
      return suite
--- 2115,2120 ----
                  filename = filename[:-1]
              test.filename = filename
!         suite.addTest(DocTestCase(test, optionflags, setUp, tearDown,
!                                   checker))
  
      return suite



More information about the Python-checkins mailing list