[Python-checkins] python/dist/src/Lib doctest.py,1.55,1.56
edloper at users.sourceforge.net
edloper at users.sourceforge.net
Mon Aug 9 18:14:43 CEST 2004
Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30812
Modified Files:
doctest.py
Log Message:
- DocTest is now a simple container class; its constructor is no longer
responsible for parsing the string.
- Renamed Parser to DocTestParser
- DocTestParser.get_*() now accept the string & name as command-line
arguments; the parser's constructor is now empty.
- Added DocTestParser.get_doctest() method
- Replaced "doctest_factory" argument to DocTestFinder with a "parser"
argument (takes a DocTestParser).
- Changed _tag_msg to take an indentation string argument.
Index: doctest.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/doctest.py,v
retrieving revision 1.55
retrieving revision 1.56
diff -C2 -d -r1.55 -r1.56
*** doctest.py 9 Aug 2004 15:43:46 -0000 1.55
--- doctest.py 9 Aug 2004 16:14:41 -0000 1.56
***************
*** 315,328 ****
raise TypeError("Expected a module, string, or None")
! def _tag_msg(tag, msg, indent_msg=True):
"""
Return a string that displays a tag-and-message pair nicely,
keeping the tag and its message on the same line when that
! makes sense. If `indent_msg` is true, then messages that are
! put on separate lines will be indented.
"""
- # What string should we use to indent contents?
- INDENT = ' '
-
# If the message doesn't end in a newline, then add one.
if msg[-1:] != '\n':
--- 315,325 ----
raise TypeError("Expected a module, string, or None")
! def _tag_msg(tag, msg, indent=' '):
"""
Return a string that displays a tag-and-message pair nicely,
keeping the tag and its message on the same line when that
! makes sense. If the message is displayed on separate lines,
! then `indent` is added to the beginning of each line.
"""
# If the message doesn't end in a newline, then add one.
if msg[-1:] != '\n':
***************
*** 335,342 ****
return '%s: %s' % (tag, msg)
else:
! if indent_msg:
! msg = '\n'.join([INDENT+l for l in msg.split('\n')])
! msg = msg[:-len(INDENT)]
! return '%s:\n%s' % (tag, msg)
# Override some StringIO methods.
--- 332,337 ----
return '%s: %s' % (tag, msg)
else:
! msg = '\n'.join([indent+l for l in msg[:-1].split('\n')])
! return '%s:\n%s\n' % (tag, msg)
# Override some StringIO methods.
***************
*** 368,379 ****
## where the example was extracted from.
##
! ## - A "doctest" is a collection of examples extracted from a string
! ## (such as an object's docstring). The DocTest class also includes
! ## information about where the string was extracted from.
class Example:
"""
A single doctest example, consisting of source code and expected
! output. Example defines the following attributes:
- source: A single Python statement, always ending with a newline.
--- 363,374 ----
## where the example was extracted from.
##
! ## - A "doctest" is a collection of examples, typically extracted from
! ## a string (such as an object's docstring). The DocTest class also
! ## includes information about where the string was extracted from.
class Example:
"""
A single doctest example, consisting of source code and expected
! output. `Example` defines the following attributes:
- source: A single Python statement, always ending with a newline.
***************
*** 403,407 ****
"""
A collection of doctest examples that should be run in a single
! namespace. Each DocTest defines the following attributes:
- examples: the list of examples.
--- 398,402 ----
"""
A collection of doctest examples that should be run in a single
! namespace. Each `DocTest` defines the following attributes:
- examples: the list of examples.
***************
*** 413,439 ****
the object whose docstring this DocTest was extracted from).
- - docstring: The docstring being tested
-
- filename: The name of the file that this DocTest was extracted
! from.
- lineno: The line number within filename where this DocTest
! begins. This line number is zero-based, with respect to the
! beginning of the file.
"""
! def __init__(self, docstring, globs, name, filename, lineno):
"""
! Create a new DocTest, by extracting examples from `docstring`.
! The DocTest's globals are initialized with a copy of `globs`.
"""
! # Store a copy of the globals
self.globs = globs.copy()
- # Store identifying information
self.name = name
self.filename = filename
self.lineno = lineno
- # Parse the docstring.
- self.docstring = docstring
- self.examples = Parser(name, docstring).get_examples()
def __repr__(self):
--- 408,435 ----
the object whose docstring this DocTest was extracted from).
- filename: The name of the file that this DocTest was extracted
! from, or `None` if the filename is unknown.
- lineno: The line number within filename where this DocTest
! begins, or `None` if the line number is unavailable. This
! line number is zero-based, with respect to the beginning of
! the file.
!
! - docstring: The string that the examples were extracted from,
! or `None` if the string is unavailable.
"""
! def __init__(self, examples, globs, name, filename, lineno, docstring):
"""
! Create a new DocTest containing the given examples. The
! DocTest's globals are initialized with a copy of `globs`.
"""
! assert not isinstance(examples, basestring), \
! "DocTest no longer accepts str; use DocTestParser instead"
! self.examples = examples
! self.docstring = docstring
self.globs = globs.copy()
self.name = name
self.filename = filename
self.lineno = lineno
def __repr__(self):
***************
*** 456,476 ****
######################################################################
! ## 2. Example Parser
######################################################################
! class Parser:
"""
! Extract doctests from a string.
"""
- 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.string = string.expandtabs()
-
_EXAMPLE_RE = re.compile(r'''
# Source consists of a PS1 line followed by zero or more PS2 lines.
--- 452,462 ----
######################################################################
! ## 2. DocTestParser
######################################################################
! class DocTestParser:
"""
! A class used to parse strings containing doctest examples.
"""
_EXAMPLE_RE = re.compile(r'''
# Source consists of a PS1 line followed by zero or more PS2 lines.
***************
*** 487,497 ****
_IS_BLANK_OR_COMMENT = re.compile('^[ ]*(#.*)?$').match
! def get_examples(self):
"""
! Extract all doctest examples, from the string, and return them
! as a list of `Example` objects. Line numbers are 0-based,
! because it's most common in doctests that nothing interesting
! appears on the same line as opening triple-quote, and so the
! first interesting line is called \"line 1\" then.
>>> text = '''
--- 473,498 ----
_IS_BLANK_OR_COMMENT = re.compile('^[ ]*(#.*)?$').match
! def get_doctest(self, string, globs, name, filename, lineno):
"""
! Extract all doctest examples from the given string, and
! collect them into a `DocTest` object.
!
! `globs`, `name`, `filename`, and `lineno` are attributes for
! the new `DocTest` object. See the documentation for `DocTest`
! for more information.
! """
! return DocTest(self.get_examples(string, name), globs,
! name, filename, lineno, string)
!
! def get_examples(self, string, name='<string>'):
! """
! Extract all doctest examples from the given string, and return
! them as a list of `Example` objects. Line numbers are
! 0-based, because it's most common in doctests that nothing
! interesting appears on the same line as opening triple-quote,
! and so the first interesting line is called \"line 1\" then.
!
! The optional argument `name` is a name identifying this
! string, and is only used for error messages.
>>> text = '''
***************
*** 507,511 ****
... 5
... '''
! >>> for x in Parser('<string>', text).get_examples():
... print (x.source, x.want, x.lineno)
('x, y = 2, 3 # no output expected\\n', '', 1)
--- 508,512 ----
... 5
... '''
! >>> for x in DocTestParser().get_examples(text):
... print (x.source, x.want, x.lineno)
('x, y = 2, 3 # no output expected\\n', '', 1)
***************
*** 516,525 ****
charno, lineno = 0, 0
# Find all doctest examples in the string:
! for m in self._EXAMPLE_RE.finditer(self.string):
# Update lineno (lines before this example)
! lineno += self.string.count('\n', charno, m.start())
# Extract source/want from the regexp match.
! (source, want) = self._parse_example(m, lineno)
if self._IS_BLANK_OR_COMMENT(source):
continue
--- 517,526 ----
charno, lineno = 0, 0
# Find all doctest examples in the string:
! for m in self._EXAMPLE_RE.finditer(string.expandtabs()):
# Update lineno (lines before this example)
! lineno += string.count('\n', charno, m.start())
# Extract source/want from the regexp match.
! (source, want) = self._parse_example(m, name, lineno)
if self._IS_BLANK_OR_COMMENT(source):
continue
***************
*** 527,538 ****
# Update lineno (lines inside this example)
! lineno += self.string.count('\n', m.start(), m.end())
# Update charno.
charno = m.end()
return 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
--- 528,539 ----
# Update lineno (lines inside this example)
! lineno += string.count('\n', m.start(), m.end())
# Update charno.
charno = m.end()
return examples
! def get_program(self, string, name="<string>"):
"""
! Return an executable program from the given string, as a string.
The format of this isn't rigidly defined. In general, doctest
***************
*** 542,545 ****
--- 543,549 ----
a doctest test) is also placed in comments.
+ The optional argument `name` is a name identifying this
+ string, and is only used for error messages.
+
>>> text = '''
... >>> x, y = 2, 3 # no output expected
***************
*** 554,558 ****
... 5
... '''
! >>> print Parser('<string>', text).get_program()
x, y = 2, 3 # no output expected
if 1:
--- 558,562 ----
... 5
... '''
! >>> print DocTestParser().get_program(text)
x, y = 2, 3 # no output expected
if 1:
***************
*** 571,583 ****
charnum, lineno = 0, 0
# Find all doctest examples in the string:
! for m in self._EXAMPLE_RE.finditer(self.string):
# Add any text before this example, as a comment.
if m.start() > charnum:
! lines = self.string[charnum:m.start()-1].split('\n')
output.extend([self._comment_line(l) for l in lines])
lineno += len(lines)
# Extract source/want from the regexp match.
! (source, want) = self._parse_example(m, lineno, False)
# Display the source
output.append(source)
--- 575,587 ----
charnum, lineno = 0, 0
# Find all doctest examples in the string:
! for m in self._EXAMPLE_RE.finditer(string.expandtabs()):
# Add any text before this example, as a comment.
if m.start() > charnum:
! lines = string[charnum:m.start()-1].split('\n')
output.extend([self._comment_line(l) for l in lines])
lineno += len(lines)
# Extract source/want from the regexp match.
! (source, want) = self._parse_example(m, name, lineno, False)
# Display the source
output.append(source)
***************
*** 588,596 ****
# Update the line number & char number.
! lineno += self.string.count('\n', m.start(), m.end())
charnum = m.end()
# Add any remaining text, as comments.
output.extend([self._comment_line(l)
! for l in self.string[charnum:].split('\n')])
# Trim junk on both ends.
while output and output[-1] == '#':
--- 592,600 ----
# Update the line number & char number.
! lineno += string.count('\n', m.start(), m.end())
charnum = m.end()
# Add any remaining text, as comments.
output.extend([self._comment_line(l)
! for l in string[charnum:].split('\n')])
# Trim junk on both ends.
while output and output[-1] == '#':
***************
*** 601,605 ****
return '\n'.join(output)
! def _parse_example(self, m, lineno, add_newlines=True):
# Get the example's indentation level.
indent = len(m.group('indent'))
--- 605,609 ----
return '\n'.join(output)
! def _parse_example(self, m, name, lineno, add_newlines=True):
# Get the example's indentation level.
indent = len(m.group('indent'))
***************
*** 608,613 ****
# indented; and then strip their indentation & prompts.
source_lines = m.group('source').split('\n')
! self._check_prompt_blank(source_lines, indent, lineno)
! self._check_prefix(source_lines[1:], ' '*indent+'.', lineno)
source = '\n'.join([sl[indent+4:] for sl in source_lines])
if len(source_lines) > 1 and add_newlines:
--- 612,617 ----
# indented; and then strip their indentation & prompts.
source_lines = m.group('source').split('\n')
! self._check_prompt_blank(source_lines, indent, name, lineno)
! self._check_prefix(source_lines[1:], ' '*indent+'.', name, lineno)
source = '\n'.join([sl[indent+4:] for sl in source_lines])
if len(source_lines) > 1 and add_newlines:
***************
*** 617,621 ****
# indented; and then strip the indentation.
want_lines = m.group('want').rstrip().split('\n')
! self._check_prefix(want_lines, ' '*indent,
lineno+len(source_lines))
want = '\n'.join([wl[indent:] for wl in want_lines])
--- 621,625 ----
# indented; and then strip the indentation.
want_lines = m.group('want').rstrip().split('\n')
! self._check_prefix(want_lines, ' '*indent, name,
lineno+len(source_lines))
want = '\n'.join([wl[indent:] for wl in want_lines])
***************
*** 632,649 ****
return '#'
! def _check_prompt_blank(self, lines, indent, lineno):
for i, line in enumerate(lines):
if len(line) >= indent+4 and line[indent+3] != ' ':
raise ValueError('line %r of the docstring for %s '
'lacks blank after %s: %r' %
! (lineno+i+1, self.name,
line[indent:indent+3], line))
! def _check_prefix(self, lines, prefix, lineno):
for i, line in enumerate(lines):
if line and not line.startswith(prefix):
raise ValueError('line %r of the docstring for %s has '
'inconsistent leading whitespace: %r' %
! (lineno+i+1, self.name, line))
--- 636,653 ----
return '#'
! def _check_prompt_blank(self, lines, indent, name, lineno):
for i, line in enumerate(lines):
if len(line) >= indent+4 and line[indent+3] != ' ':
raise ValueError('line %r of the docstring for %s '
'lacks blank after %s: %r' %
! (lineno+i+1, name,
line[indent:indent+3], line))
! def _check_prefix(self, lines, prefix, name, lineno):
for i, line in enumerate(lines):
if line and not line.startswith(prefix):
raise ValueError('line %r of the docstring for %s has '
'inconsistent leading whitespace: %r' %
! (lineno+i+1, name, line))
***************
*** 661,670 ****
"""
! def __init__(self, verbose=False, doctest_factory=DocTest,
recurse=True, _namefilter=None):
"""
Create a new doctest finder.
! The optional argument `doctest_factory` specifies a class or
function that should be used to create new DocTest objects (or
objects that implement the same interface as DocTest). The
--- 665,674 ----
"""
! def __init__(self, verbose=False, parser=DocTestParser(),
recurse=True, _namefilter=None):
"""
Create a new doctest finder.
! The optional argument `parser` specifies a class or
function that should be used to create new DocTest objects (or
objects that implement the same interface as DocTest). The
***************
*** 675,679 ****
only examine the given object, and not any contained objects.
"""
! self._doctest_factory = doctest_factory
self._verbose = verbose
self._recurse = recurse
--- 679,683 ----
only examine the given object, and not any contained objects.
"""
! self._parser = parser
self._verbose = verbose
self._recurse = recurse
***************
*** 886,890 ****
else:
filename = getattr(module, '__file__', module.__name__)
! return self._doctest_factory(docstring, globs, name, filename, lineno)
def _find_lineno(self, obj, source_lines):
--- 890,895 ----
else:
filename = getattr(module, '__file__', module.__name__)
! return self._parser.get_doctest(docstring, globs, name,
! filename, lineno)
def _find_lineno(self, obj, source_lines):
***************
*** 1255,1282 ****
compileflags = _extract_future_flags(test.globs)
- save_stdout = sys.stdout
if out is None:
! out = save_stdout.write
! sys.stdout = self._fakeout
! # Patch pdb.set_trace to restore sys.stdout, so that interactive
! # debugging output is visible (not still redirected to self._fakeout).
! # Note that we run "the real" pdb.set_trace (captured at doctest
! # import time) in our replacement. Because the current run() may
! # run another doctest (and so on), the current pdb.set_trace may be
! # our set_trace function, which changes sys.stdout. If we called
! # a chain of those, we wouldn't be left with the save_stdout
! # *this* run() invocation wants.
def set_trace():
! sys.stdout = save_stdout
real_pdb_set_trace()
- save_set_trace = pdb.set_trace
- pdb.set_trace = set_trace
try:
return self.__run(test, compileflags, out)
finally:
! sys.stdout = save_stdout
! pdb.set_trace = save_set_trace
if clear_globs:
test.globs.clear()
--- 1260,1284 ----
compileflags = _extract_future_flags(test.globs)
if out is None:
! out = sys.stdout.write
! saveout = sys.stdout
! # Note that don't save away the previous pdb.set_trace. Rather,
! # we safe pdb.set_trace on import (see import section above).
! # We then call and restore that original cersion. We do it this
! # way to make this feature testable. If we kept and called the
! # previous version, we'd end up restoring the original stdout,
! # which is not what we want.
def set_trace():
! sys.stdout = saveout
real_pdb_set_trace()
try:
+ sys.stdout = self._fakeout
+ pdb.set_trace = set_trace
return self.__run(test, compileflags, out)
finally:
! sys.stdout = saveout
! pdb.set_trace = real_pdb_set_trace
if clear_globs:
test.globs.clear()
***************
*** 1493,1497 ****
>>> runner = DebugRunner(verbose=False)
! >>> test = DocTest('>>> raise KeyError\n42', {}, 'foo', 'foo.py', 0)
>>> try:
... runner.run(test)
--- 1495,1500 ----
>>> runner = DebugRunner(verbose=False)
! >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42',
! ... {}, 'foo', 'foo.py', 0)
>>> try:
... runner.run(test)
***************
*** 1516,1520 ****
If the output doesn't match, then a DocTestFailure is raised:
! >>> test = DocTest('''
... >>> x = 1
... >>> x
--- 1519,1523 ----
If the output doesn't match, then a DocTestFailure is raised:
! >>> test = DocTestParser().get_doctest('''
... >>> x = 1
... >>> x
***************
*** 1548,1552 ****
{'x': 1}
! >>> test = DocTest('''
... >>> x = 2
... >>> raise KeyError
--- 1551,1555 ----
{'x': 1}
! >>> test = DocTestParser().get_doctest('''
... >>> x = 2
... >>> raise KeyError
***************
*** 1564,1568 ****
But the globals are cleared if there is no error:
! >>> test = DocTest('''
... >>> x = 2
... ''', {}, 'foo', 'foo.py', 0)
--- 1567,1571 ----
But the globals are cleared if there is no error:
! >>> test = DocTestParser().get_doctest('''
... >>> x = 2
... ''', {}, 'foo', 'foo.py', 0)
***************
*** 1780,1784 ****
def runstring(self, s, name):
! test = DocTest(s, self.globs, name, None, None)
if self.verbose:
print "Running string", name
--- 1783,1787 ----
def runstring(self, s, name):
! test = DocTestParser().get_doctest(s, self.globs, name, None, None)
if self.verbose:
print "Running string", name
***************
*** 1888,1892 ****
exception:
! >>> test = DocTest('>>> raise KeyError\n42',
... {}, 'foo', 'foo.py', 0)
>>> case = DocTestCase(test)
--- 1891,1895 ----
exception:
! >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42',
... {}, 'foo', 'foo.py', 0)
>>> case = DocTestCase(test)
***************
*** 1913,1917 ****
If the output doesn't match, then a DocTestFailure is raised:
! >>> test = DocTest('''
... >>> x = 1
... >>> x
--- 1916,1920 ----
If the output doesn't match, then a DocTestFailure is raised:
! >>> test = DocTestParser().get_doctest('''
... >>> x = 1
... >>> x
***************
*** 2033,2037 ****
globs = {}
! test = DocTest(doc, globs, name, path, 0)
return DocFileCase(test, optionflags, setUp, tearDown)
--- 2036,2040 ----
globs = {}
! test = DocTestParser().get_doctest(doc, globs, name, path, 0)
return DocFileCase(test, optionflags, setUp, tearDown)
***************
*** 2139,2143 ****
"""
! return Parser('<string>', s).get_program()
def _want_comment(example):
--- 2142,2146 ----
"""
! return DocTestParser().get_program(s)
def _want_comment(example):
More information about the Python-checkins
mailing list