from test import support
from test.support import import_helper
+from test.support.pty_helper import FakeInput # used in doctests
import doctest
import functools
import os
"""
return self.val
+ def setter(self, val):
+ """
+ >>> s = SampleClass(-5)
+ >>> s.setter(1)
+ >>> print(s.val)
+ 1
+ """
+ self.val = val
+
def a_staticmethod(v):
"""
>>> print(SampleClass.a_staticmethod(10))
return v+2
a_classmethod = classmethod(a_classmethod)
- a_property = property(get, doc="""
+ a_property = property(get, setter, doc="""
>>> print(SampleClass(22).a_property)
22
""")
"""
return self.val
-######################################################################
-## Fake stdin (for testing interactive debugging)
-######################################################################
-
-class _FakeInput:
- """
- A fake input stream for pdb's interactive debugger. Whenever a
- line is read, print it (to simulate the user typing it), and then
- return it. The set of lines to return is specified in the
- constructor; they should not have trailing newlines.
- """
- def __init__(self, lines):
- self.lines = lines
-
- def readline(self):
- line = self.lines.pop(0)
- print(line)
- return line+'\n'
-
######################################################################
## Test Cases
######################################################################
We'll simulate a __file__ attr that ends in pyc:
- >>> import test.test_doctest
- >>> old = test.test_doctest.__file__
- >>> test.test_doctest.__file__ = 'test_doctest.pyc'
+ >>> from test.test_doctest import test_doctest
+ >>> old = test_doctest.__file__
+ >>> test_doctest.__file__ = 'test_doctest.pyc'
>>> tests = finder.find(sample_func)
>>> print(tests) # doctest: +ELLIPSIS
- [<DocTest sample_func from test_doctest.py:32 (1 example)>]
+ [<DocTest sample_func from test_doctest.py:33 (1 example)>]
The exact name depends on how test_doctest was invoked, so allow for
leading path components.
>>> tests[0].filename # doctest: +ELLIPSIS
'...test_doctest.py'
- >>> test.test_doctest.__file__ = old
+ >>> test_doctest.__file__ = old
>>> e = tests[0].examples[0]
1 SampleClass.a_staticmethod
1 SampleClass.double
1 SampleClass.get
+ 3 SampleClass.setter
New-style classes are also supported:
... 'c': triple}})
>>> finder = doctest.DocTestFinder()
- >>> # Use module=test.test_doctest, to prevent doctest from
+ >>> # Use module=test_doctest, to prevent doctest from
>>> # ignoring the objects since they weren't defined in m.
- >>> import test.test_doctest
- >>> tests = finder.find(m, module=test.test_doctest)
+ >>> from test.test_doctest import test_doctest
+ >>> tests = finder.find(m, module=test_doctest)
>>> for t in tests:
... print('%2s %s' % (len(t.examples), t.name))
1 some_module
1 some_module.SampleClass.a_staticmethod
1 some_module.SampleClass.double
1 some_module.SampleClass.get
+ 3 some_module.SampleClass.setter
1 some_module.__test__.c
2 some_module.__test__.d
1 some_module.sample_func
+However, doctest will ignore imported objects from other modules
+(without proper `module=`):
+
+ >>> import types
+ >>> m = types.ModuleType('poluted_namespace')
+ >>> m.__dict__.update({
+ ... 'sample_func': sample_func,
+ ... 'SampleClass': SampleClass,
+ ... })
+
+ >>> finder = doctest.DocTestFinder()
+ >>> finder.find(m)
+ []
+
Duplicate Removal
~~~~~~~~~~~~~~~~~
If a single object is listed twice (under different names), then tests
will only be generated for it once:
- >>> from test import doctest_aliases
+ >>> from test.test_doctest import doctest_aliases
>>> assert doctest_aliases.TwoNames.f
>>> assert doctest_aliases.TwoNames.g
>>> tests = excl_empty_finder.find(doctest_aliases)
>>> print(len(tests))
2
>>> print(tests[0].name)
- test.doctest_aliases.TwoNames
+ test.test_doctest.doctest_aliases.TwoNames
TwoNames.f and TwoNames.g are bound to the same object.
We can't guess which will be found in doctest's traversal of
1 SampleClass.a_staticmethod
1 SampleClass.double
1 SampleClass.get
+ 3 SampleClass.setter
By default, that excluded objects with no doctests. exclude_empty=False
tells it to include (empty) tests for objects with no doctests. This feature
1 SampleClass.a_staticmethod
1 SampleClass.double
1 SampleClass.get
+ 3 SampleClass.setter
When used with `exclude_empty=False` we are also interested in line numbers
of doctests that are empty.
It used to be broken for quite some time until `bpo-28249`.
- >>> from test import doctest_lineno
+ >>> from test.test_doctest import doctest_lineno
>>> tests = doctest.DocTestFinder(exclude_empty=False).find(doctest_lineno)
>>> for t in tests:
... print('%5s %s' % (t.lineno, t.name))
- None test.doctest_lineno
- 22 test.doctest_lineno.ClassWithDocstring
- 30 test.doctest_lineno.ClassWithDoctest
- None test.doctest_lineno.ClassWithoutDocstring
- None test.doctest_lineno.MethodWrapper
- 53 test.doctest_lineno.MethodWrapper.classmethod_with_doctest
- 39 test.doctest_lineno.MethodWrapper.method_with_docstring
- 45 test.doctest_lineno.MethodWrapper.method_with_doctest
- None test.doctest_lineno.MethodWrapper.method_without_docstring
- 61 test.doctest_lineno.MethodWrapper.property_with_doctest
- 4 test.doctest_lineno.func_with_docstring
- 12 test.doctest_lineno.func_with_doctest
- None test.doctest_lineno.func_without_docstring
+ None test.test_doctest.doctest_lineno
+ 22 test.test_doctest.doctest_lineno.ClassWithDocstring
+ 30 test.test_doctest.doctest_lineno.ClassWithDoctest
+ None test.test_doctest.doctest_lineno.ClassWithoutDocstring
+ None test.test_doctest.doctest_lineno.MethodWrapper
+ 53 test.test_doctest.doctest_lineno.MethodWrapper.classmethod_with_doctest
+ 39 test.test_doctest.doctest_lineno.MethodWrapper.method_with_docstring
+ 45 test.test_doctest.doctest_lineno.MethodWrapper.method_with_doctest
+ None test.test_doctest.doctest_lineno.MethodWrapper.method_without_docstring
+ 61 test.test_doctest.doctest_lineno.MethodWrapper.property_with_doctest
+ 4 test.test_doctest.doctest_lineno.func_with_docstring
+ 12 test.test_doctest.doctest_lineno.func_with_doctest
+ None test.test_doctest.doctest_lineno.func_without_docstring
Turning off Recursion
~~~~~~~~~~~~~~~~~~~~~
example code is converted to regular Python code. The surrounding
words and expected output are converted to comments:
- >>> import test.test_doctest
- >>> name = 'test.test_doctest.sample_func'
- >>> print(doctest.testsource(test.test_doctest, name))
+ >>> from test.test_doctest import test_doctest
+ >>> name = 'test.test_doctest.test_doctest.sample_func'
+ >>> print(doctest.testsource(test_doctest, name))
# Blah blah
#
print(sample_func(22))
# Yee ha!
<BLANKLINE>
- >>> name = 'test.test_doctest.SampleNewStyleClass'
- >>> print(doctest.testsource(test.test_doctest, name))
+ >>> name = 'test.test_doctest.test_doctest.SampleNewStyleClass'
+ >>> print(doctest.testsource(test_doctest, name))
print('1\n2\n3')
# Expected:
## 1
## 3
<BLANKLINE>
- >>> name = 'test.test_doctest.SampleClass.a_classmethod'
- >>> print(doctest.testsource(test.test_doctest, name))
+ >>> name = 'test.test_doctest.test_doctest.SampleClass.a_classmethod'
+ >>> print(doctest.testsource(test_doctest, name))
print(SampleClass.a_classmethod(10))
# Expected:
## 12
Create some fake stdin input, to feed to the debugger:
>>> real_stdin = sys.stdin
- >>> sys.stdin = _FakeInput(['next', 'print(x)', 'continue'])
+ >>> sys.stdin = FakeInput(['next', 'print(x)', 'continue'])
Run the debugger on the docstring, and then restore sys.stdin.
captures our debugger input:
>>> real_stdin = sys.stdin
- >>> sys.stdin = _FakeInput([
+ >>> sys.stdin = FakeInput([
... 'print(x)', # print data defined by the example
... 'continue', # stop debugging
... ''])
... '''
>>> test = parser.get_doctest(doc, globals(), "foo-bar@baz", "foo-bar@baz.py", 0)
>>> real_stdin = sys.stdin
- >>> sys.stdin = _FakeInput([
+ >>> sys.stdin = FakeInput([
... 'print(y)', # print data defined in the function
... 'up', # out of function
... 'print(x)', # print data defined by the example
... finally:
... sys.stdin = real_stdin
--Return--
- > <doctest test.test_doctest.test_pdb_set_trace[7]>(3)calls_set_trace()->None
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace[7]>(3)calls_set_trace()->None
-> import pdb; pdb.set_trace()
(Pdb) print(y)
2
... '''
>>> test = parser.get_doctest(doc, globals(), "foo-bar@baz", "foo-bar@baz.py", 0)
>>> real_stdin = sys.stdin
- >>> sys.stdin = _FakeInput([
+ >>> sys.stdin = FakeInput([
... 'list', # list source from example 2
... 'next', # return from g()
... 'list', # list source from example 1
>>> runner = doctest.DocTestRunner(verbose=False)
>>> test = parser.get_doctest(doc, globals(), "foo-bar@baz", "foo-bar@baz.py", 0)
>>> real_stdin = sys.stdin
- >>> sys.stdin = _FakeInput([
+ >>> sys.stdin = FakeInput([
... 'print(y)', # print data defined in the function
... 'step', 'step', 'step', 'step', 'step', 'step', 'print(z)',
... 'up', 'print(x)',
... finally:
... sys.stdin = real_stdin
... # doctest: +REPORT_NDIFF
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(5)calls_set_trace()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(5)calls_set_trace()
-> self.f1()
(Pdb) print(y)
1
(Pdb) step
--Call--
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(7)f1()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(7)f1()
-> def f1(self):
(Pdb) step
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(8)f1()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(8)f1()
-> x = 1
(Pdb) step
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(9)f1()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(9)f1()
-> self.f2()
(Pdb) step
--Call--
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(11)f2()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(11)f2()
-> def f2(self):
(Pdb) step
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(12)f2()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(12)f2()
-> z = 1
(Pdb) step
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(13)f2()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(13)f2()
-> z = 2
(Pdb) print(z)
1
(Pdb) up
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(9)f1()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(9)f1()
-> self.f2()
(Pdb) print(x)
1
(Pdb) up
- > <doctest test.test_doctest.test_pdb_set_trace_nested[0]>(5)calls_set_trace()
+ > <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(5)calls_set_trace()
-> self.f1()
(Pdb) print(y)
1
by passing a module object:
>>> import unittest
- >>> import test.sample_doctest
- >>> suite = doctest.DocTestSuite(test.sample_doctest)
+ >>> import test.test_doctest.sample_doctest
+ >>> suite = doctest.DocTestSuite(test.test_doctest.sample_doctest)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=4>
We can also supply the module by name:
- >>> suite = doctest.DocTestSuite('test.sample_doctest')
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest')
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=4>
The module need not contain any doctest examples:
- >>> suite = doctest.DocTestSuite('test.sample_doctest_no_doctests')
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest_no_doctests')
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=0 errors=0 failures=0>
The module need not contain any docstrings either:
- >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings')
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest_no_docstrings')
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=0 errors=0 failures=0>
We can use the current module:
- >>> suite = test.sample_doctest.test_suite()
+ >>> suite = test.test_doctest.sample_doctest.test_suite()
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=4>
We can also provide a DocTestFinder:
>>> finder = doctest.DocTestFinder()
- >>> suite = doctest.DocTestSuite('test.sample_doctest',
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
... test_finder=finder)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=4>
The DocTestFinder need not return any tests:
>>> finder = doctest.DocTestFinder()
- >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings',
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest_no_docstrings',
... test_finder=finder)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=0 errors=0 failures=0>
used instead of the module globals. Here we'll pass an empty
globals, triggering an extra error:
- >>> suite = doctest.DocTestSuite('test.sample_doctest', globs={})
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest', globs={})
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=5>
Alternatively, we can provide extra globals. Here we'll make an
error go away by providing an extra global variable:
- >>> suite = doctest.DocTestSuite('test.sample_doctest',
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
... extraglobs={'y': 1})
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=3>
You can pass option flags. Here we'll cause an extra error
by disabling the blank-line feature:
- >>> suite = doctest.DocTestSuite('test.sample_doctest',
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
... optionflags=doctest.DONT_ACCEPT_BLANKLINE)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=5>
You can supply setUp and tearDown functions:
>>> def setUp(t):
- ... import test.test_doctest
- ... test.test_doctest.sillySetup = True
+ ... from test.test_doctest import test_doctest
+ ... test_doctest.sillySetup = True
>>> def tearDown(t):
- ... import test.test_doctest
- ... del test.test_doctest.sillySetup
+ ... from test.test_doctest import test_doctest
+ ... del test_doctest.sillySetup
Here, we installed a silly variable that the test expects:
- >>> suite = doctest.DocTestSuite('test.sample_doctest',
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
... setUp=setUp, tearDown=tearDown)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=3>
But the tearDown restores sanity:
- >>> import test.test_doctest
- >>> test.test_doctest.sillySetup
+ >>> from test.test_doctest import test_doctest
+ >>> test_doctest.sillySetup
Traceback (most recent call last):
...
- AttributeError: module 'test.test_doctest' has no attribute 'sillySetup'
+ AttributeError: module 'test.test_doctest.test_doctest' has no attribute 'sillySetup'
The setUp and tearDown functions are passed test objects. Here
we'll use the setUp function to supply the missing variable y:
>>> def setUp(test):
... test.globs['y'] = 1
- >>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp)
+ >>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest', setUp=setUp)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=3>
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
... 'test_doctest4.txt',
- ... package='test')
+ ... package='test.test_doctest')
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=3 errors=0 failures=2>
... suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
... 'test_doctest4.txt',
- ... package='test')
+ ... package='test.test_doctest')
... suite.run(unittest.TestResult())
... finally:
... if added_loader:
'/' should be used as a path separator. It will be converted
to a native separator at run time:
- >>> suite = doctest.DocFileSuite('../test/test_doctest.txt')
+ >>> suite = doctest.DocFileSuite('../test_doctest/test_doctest.txt')
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=1 errors=0 failures=1>
If DocFileSuite is used from an interactive session, then files
are resolved relative to the directory of sys.argv[0]:
- >>> import types, os.path, test.test_doctest
+ >>> import types, os.path
+ >>> from test.test_doctest import test_doctest
>>> save_argv = sys.argv
- >>> sys.argv = [test.test_doctest.__file__]
+ >>> sys.argv = [test_doctest.__file__]
>>> suite = doctest.DocFileSuite('test_doctest.txt',
... package=types.ModuleType('__main__'))
>>> sys.argv = save_argv
working directory):
>>> # Get the absolute path of the test package.
- >>> test_doctest_path = os.path.abspath(test.test_doctest.__file__)
+ >>> test_doctest_path = os.path.abspath(test_doctest.__file__)
>>> test_pkg_path = os.path.split(test_doctest_path)[0]
>>> # Use it to find the absolute path of test_doctest.txt.
And, you can provide setUp and tearDown functions:
>>> def setUp(t):
- ... import test.test_doctest
- ... test.test_doctest.sillySetup = True
+ ... from test.test_doctest import test_doctest
+ ... test_doctest.sillySetup = True
>>> def tearDown(t):
- ... import test.test_doctest
- ... del test.test_doctest.sillySetup
+ ... from test.test_doctest import test_doctest
+ ... del test_doctest.sillySetup
Here, we installed a silly variable that the test expects:
But the tearDown restores sanity:
- >>> import test.test_doctest
- >>> test.test_doctest.sillySetup
+ >>> from test.test_doctest import test_doctest
+ >>> test_doctest.sillySetup
Traceback (most recent call last):
...
- AttributeError: module 'test.test_doctest' has no attribute 'sillySetup'
+ AttributeError: module 'test.test_doctest.test_doctest' has no attribute 'sillySetup'
The setUp and tearDown functions are passed test objects.
Here, we'll use a setUp function to set the favorite color in
http://bugs.python.org/issue9736
>>> import unittest
- >>> import test.sample_doctest
- >>> suite = doctest.DocTestSuite(test.sample_doctest)
+ >>> import test.test_doctest.sample_doctest
+ >>> suite = doctest.DocTestSuite(test.test_doctest.sample_doctest)
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=9 errors=0 failures=4>
>>> suite.run(unittest.TestResult())
if __name__ == '__main__':
- unittest.main(module='test.test_doctest')
+ unittest.main(module='test.test_doctest.test_doctest')