[Python-checkins] python/dist/src/Lib doctest.py, 1.36.2.16, 1.36.2.17

tim_one at users.sourceforge.net tim_one at users.sourceforge.net
Fri Aug 6 03:01:58 CEST 2004


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

Modified Files:
      Tag: tim-doctest-branch
	doctest.py 
Log Message:
Moved the parsing into a new utility Parser class.  This was primarily
to squash massive code duplication in script_from_examples(), which is
now a 1-liner.


Index: doctest.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v
retrieving revision 1.36.2.16
retrieving revision 1.36.2.17
diff -C2 -d -r1.36.2.16 -r1.36.2.17
*** doctest.py	5 Aug 2004 23:23:26 -0000	1.36.2.16
--- doctest.py	6 Aug 2004 01:01:55 -0000	1.36.2.17
***************
*** 450,453 ****
--- 450,655 ----
              del self.softspace
  
+ class Parser:
+     """
+     Extract doctests from a string.
+     """
+ 
+     _PS1 = ">>>"
+     _PS2 = "..."
+     _isPS1 = re.compile(r"(\s*)" + re.escape(_PS1)).match
+     _isPS2 = re.compile(r"(\s*)" + re.escape(_PS2)).match
+     _isEmpty = re.compile(r"\s*$").match
+     _isComment = re.compile(r"\s*#").match
+ 
+     def __init__(self, name, string):
+         """
+         Prepare to extract doctests from string `string`.
+ 
+         `name` is an arbitrary (string) name associated with the string,
+         and is used only in error messages.
+         """
+         self.name = name
+         self.source = string
+ 
+     def get_examples(self):
+         """
+         Return the doctest examples from the string.
+ 
+         This is a list of doctest.Example instances, one Example
+         per doctest test in the string.
+ 
+         >>> text = '''
+         ...        >>> x, y = 2, 3  # no output expected
+         ...        >>> if 1:
+         ...        ...     print x
+         ...        ...     print y
+         ...        2
+         ...        3
+         ...
+         ...        Some text.
+         ...        >>> x+y
+         ...        5
+         ...        '''
+         >>> for x in Parser('<string>', text).get_examples():
+         ...     x.source
+         ...     x.want
+         ...     x.lineno
+         'x, y = 2, 3  # no output expected'
+         ''
+         1
+         'if 1:\\n    print x\\n    print y\\n'
+         '2\\n3\\n'
+         2
+         'x+y'
+         '5\\n'
+         9
+         """
+         return self._parse(kind='examples')
+ 
+     def get_program(self):
+         """
+         Return an executable program from the string, as a string.
+ 
+         The format of this isn't rigidly defined.  In general, doctest
+         examples become the executable statements in the result, and
+         and their expected outputs become comments, preceded by an
+         "#Expected:" comment.  Everything else (text, comments,
+         everything not part of a doctest test) is also placed in comments.
+ 
+         >>> text = '''
+         ...        >>> x, y = 2, 3  # no output expected
+         ...        >>> if 1:
+         ...        ...     print x
+         ...        ...     print y
+         ...        2
+         ...        3
+         ...
+         ...        Some text.
+         ...        >>> x+y
+         ...        5
+         ...        '''
+         >>> print Parser('<string>', text).get_program()
+         x, y = 2, 3  # no output expected
+         if 1:
+             print x
+             print y
+         # Expected:
+         #     2
+         #     3
+         #
+         #         Some text.
+         x+y
+         # Expected:
+         #     5
+         """
+         return self._parse(kind='program')
+ 
+     def _parse(self,   kind):
+         assert kind in ('examples', 'program')
+         do_program = kind == 'program'
+         output = []
+         push = output.append
+ 
+         string = self.source
+         if not string.endswith('\n'):
+             string += '\n'
+ 
+         isPS1, isPS2 = self._isPS1, self._isPS2
+         isEmpty, isComment = self._isEmpty, self._isComment
+         lines = string.split("\n")
+         i, n = 0, len(lines)
+         while i < n:
+             # Search for an example (a PS1 line).
+             line = lines[i]
+             i += 1
+             m = isPS1(line)
+             if m is None:
+                 if do_program:
+                     line = line.rstrip()
+                     if line:
+                         line = '  ' + line
+                     push('#' + line)
+                 continue
+             # line is a PS1 line.
+             j = m.end(0)  # beyond the prompt
+             if isEmpty(line, j) or isComment(line, j):
+                 # a bare prompt or comment -- not interesting
+                 if do_program:
+                     push("#  " + line[j:])
+                 continue
+             # line is a non-trivial PS1 line.
+             lineno = i - 1
+             if line[j] != " ":
+                 raise ValueError('line %r of the docstring for %s lacks '
+                                  'blank after %s: %r' %
+                                  (lineno, self.name, self._PS1, line))
+ 
+             j += 1
+             blanks = m.group(1)
+             nblanks = len(blanks)
+             # suck up this and following PS2 lines
+             source = []
+             while 1:
+                 source.append(line[j:])
+                 line = lines[i]
+                 m = isPS2(line)
+                 if m:
+                     if m.group(1) != blanks:
+                         raise ValueError('line %r of the docstring for %s '
+                             'has inconsistent leading whitespace: %r' %
+                             (i, self.name, line))
+                     i += 1
+                 else:
+                     break
+ 
+             if do_program:
+                 output.extend(source)
+             else:
+                 # get rid of useless null line from trailing empty "..."
+                 if source[-1] == "":
+                     assert len(source) > 1
+                     del source[-1]
+                 if len(source) == 1:
+                     source = source[0]
+                 else:
+                     source = "\n".join(source) + "\n"
+ 
+             # suck up response
+             if isPS1(line) or isEmpty(line):
+                 if not do_program:
+                     push(Example(source, "", lineno))
+                 continue
+ 
+             # There is a response.
+             want = []
+             if do_program:
+                 push("# Expected:")
+             while 1:
+                 if line[:nblanks] != blanks:
+                     raise ValueError('line %r of the docstring for %s '
+                         'has inconsistent leading whitespace: %r' %
+                         (i, self.name, line))
+                 want.append(line[nblanks:])
+                 i += 1
+                 line = lines[i]
+                 if isPS1(line) or isEmpty(line):
+                     break
+ 
+             if do_program:
+                 output.extend(['#     ' + x for x in want])
+             else:
+                 want = "\n".join(want) + "\n"
+                 push(Example(source, want, lineno))
+ 
+         if do_program:
+             # Trim junk on both ends.
+             while output and output[-1] == '#':
+                 output.pop()
+             while output and output[0] == '#':
+                 output.pop(0)
+             output = '\n'.join(output)
+ 
+         return output
+ 
  ######################################################################
  ## 2. Example & DocTest
***************
*** 521,603 ****
          # Parse the docstring.
          self.docstring = docstring
!         self.examples = self._parse(docstring)
! 
!     _PS1 = ">>>"
!     _PS2 = "..."
!     _isPS1 = re.compile(r"(\s*)" + re.escape(_PS1)).match
!     _isPS2 = re.compile(r"(\s*)" + re.escape(_PS2)).match
!     _isEmpty = re.compile(r"\s*$").match
!     _isComment = re.compile(r"\s*#").match
! 
!     def _parse(self, string):
!         if not string.endswith('\n'):
!             string += '\n'
!         examples = []
!         isPS1, isPS2 = self._isPS1, self._isPS2
!         isEmpty, isComment = self._isEmpty, self._isComment
!         lines = string.split("\n")
!         i, n = 0, len(lines)
!         while i < n:
!             # Search for an example (a PS1 line).
!             line = lines[i]
!             i += 1
!             m = isPS1(line)
!             if m is None:
!                 continue
!             # line is a PS1 line.
!             j = m.end(0)  # beyond the prompt
!             if isEmpty(line, j) or isComment(line, j):
!                 # a bare prompt or comment -- not interesting
!                 continue
!             # line is a non-trivial PS1 line.
!             lineno = i - 1
!             if line[j] != " ":
!                 raise ValueError('line %r of the docstring for %s lacks '
!                                  'blank after %s: %r' %
!                                  (lineno, self.name, self._PS1, line))
! 
!             j += 1
!             blanks = m.group(1)
!             nblanks = len(blanks)
!             # suck up this and following PS2 lines
!             source = []
!             while 1:
!                 source.append(line[j:])
!                 line = lines[i]
!                 m = isPS2(line)
!                 if m:
!                     if m.group(1) != blanks:
!                         raise ValueError('line %r of the docstring for %s '
!                             'has inconsistent leading whitespace: %r' %
!                             (i, self.name, line))
!                     i += 1
!                 else:
!                     break
!             # get rid of useless null line from trailing empty "..."
!             if source[-1] == "":
!                 assert len(source) > 1
!                 del source[-1]
!             if len(source) == 1:
!                 source = source[0]
!             else:
!                 source = "\n".join(source) + "\n"
!             # suck up response
!             if isPS1(line) or isEmpty(line):
!                 want = ""
!             else:
!                 want = []
!                 while 1:
!                     if line[:nblanks] != blanks:
!                         raise ValueError('line %r of the docstring for %s '
!                             'has inconsistent leading whitespace: %r' %
!                             (i, self.name, line))
!                     want.append(line[nblanks:])
!                     i += 1
!                     line = lines[i]
!                     if isPS1(line) or isEmpty(line):
!                         break
!                 want = "\n".join(want) + "\n"
!             examples.append(Example(source, want, lineno))
!         return examples
  
      def __repr__(self):
--- 723,727 ----
          # Parse the docstring.
          self.docstring = docstring
!         self.examples = Parser(name, docstring).get_examples()
  
      def __repr__(self):
***************
*** 1937,1941 ****
  
         >>> print script_from_examples(text)
-        #
         #        Here are examples of simple math.
         #
--- 2061,2064 ----
***************
*** 1962,2022 ****
         #
         #            Ho hum
-        #
         """
-     isPS1, isPS2 = DocTest._isPS1, DocTest._isPS2
-     isEmpty, isComment = DocTest._isEmpty, DocTest._isComment
-     output = []
-     lines = s.split("\n")
-     i, n = 0, len(lines)
-     while i < n:
-         line = lines[i]
-         i = i + 1
-         m = isPS1(line)
-         if m is None:
-             line = line.rstrip()
-             if line:
-                 line = '  ' + line
-             output.append('#'+line)
-             continue
-         j = m.end(0)  # beyond the prompt
-         if isEmpty(line, j) or isComment(line, j):
-             # a bare prompt or comment -- not interesting
-             output.append('#  '+line[j:])
- 
-         lineno = i - 1
-         if line[j] != " ":
-             raise ValueError("line %r of docstring lacks blank after %s: %s" %
-                              (lineno, PS1, line))
-         j = j + 1
-         blanks = m.group(1)
-         nblanks = len(blanks)
-         # suck up this and following PS2 lines
-         while 1:
-             output.append(line[j:])
-             line = lines[i]
-             m = isPS2(line)
-             if m:
-                 if m.group(1) != blanks:
-                     raise ValueError("inconsistent leading whitespace "
-                         "in line %r of docstring: %s" % (i, line))
-                 i = i + 1
-             else:
-                 break
- 
-         # suck up response
-         if not (isPS1(line) or isEmpty(line)):
-             output.append('# Expected:')
-             while 1:
-                 if line[:nblanks] != blanks:
-                     raise ValueError("inconsistent leading whitespace "
-                         "in line %r of docstring: %s" % (i, line))
-                 output.append('#     '+line[nblanks:])
-                 i = i + 1
-                 line = lines[i]
-                 if isPS1(line) or isEmpty(line):
-                     break
- 
-     return '\n'.join(output)
  
  
  def _want_comment(example):
--- 2085,2091 ----
         #
         #            Ho hum
         """
  
+     return Parser('<string>', s).get_program()
  
  def _want_comment(example):



More information about the Python-checkins mailing list