1 """Use the Doctest plugin with ``--with-doctest`` or the NOSE_WITH_DOCTEST
2 environment variable to enable collection and execution of :mod:`doctests
3 <doctest>`. Because doctests are usually included in the tested package
4 (instead of being grouped into packages or modules of their own), nose only
5 looks for them in the non-test packages it discovers in the working directory.
7 Doctests may also be placed into files other than python modules, in which
8 case they can be collected and executed by using the ``--doctest-extension``
9 switch or NOSE_DOCTEST_EXTENSION environment variable to indicate which file
12 When loading doctests from non-module files, use the ``--doctest-fixtures``
13 switch to specify how to find modules containing fixtures for the tests. A
14 module name will be produced by appending the value of that switch to the base
15 name of each doctest file loaded. For example, a doctest file "widgets.rst"
16 with the switch ``--doctest_fixtures=_fixt`` will load fixtures from the module
19 A fixtures module may define any or all of the following functions:
21 * setup([module]) or setup_module([module])
23 Called before the test runs. You may raise SkipTest to skip all tests.
25 * teardown([module]) or teardown_module([module])
27 Called after the test runs, if setup/setup_module did not raise an
32 Called before the test. NOTE: the argument passed is a
33 doctest.DocTest instance, *not* a unittest.TestCase.
37 Called after the test, if setup_test did not raise an exception. NOTE: the
38 argument passed is a doctest.DocTest instance, *not* a unittest.TestCase.
40 Doctests are run like any other test, with the exception that output
41 capture does not work; doctest does its own output capture while running a
46 See :doc:`../doc_tests/test_doctest_fixtures/doctest_fixtures` for
47 additional documentation and examples.
50 from __future__ import generators
56 from inspect import getmodule
57 from nose.plugins.base import Plugin
58 from nose.suite import ContextList
59 from nose.util import anyp, getpackage, test_address, resolve_name, \
60 src, tolist, isproperty
62 from cStringIO import StringIO
64 from StringIO import StringIO
66 import __builtin__ as builtin_mod
68 log = logging.getLogger(__name__)
73 # system version of doctest is acceptable, but needs a monkeypatch
74 except (ImportError, AttributeError):
75 # system version is too old
76 import nose.ext.dtcompat as doctest
80 # Doctest and coverage don't get along, so we need to create
81 # a monkeypatch that will replace the part of doctest that
82 # interferes with coverage reports.
84 # The monkeypatch is based on this zope patch:
85 # http://svn.zope.org/Zope3/trunk/src/zope/testing/doctest.py?rev=28679&r1=28703&r2=28705
87 _orp = doctest._OutputRedirectingPdb
89 class NoseOutputRedirectingPdb(_orp):
90 def __init__(self, out):
91 self.__debugger_used = False
92 _orp.__init__(self, out)
95 self.__debugger_used = True
96 _orp.set_trace(self, sys._getframe().f_back)
98 def set_continue(self):
99 # Calling set_continue unconditionally would break unit test
100 # coverage reporting, as Bdb.set_continue calls sys.settrace(None).
101 if self.__debugger_used:
102 _orp.set_continue(self)
103 doctest._OutputRedirectingPdb = NoseOutputRedirectingPdb
106 class DoctestSuite(unittest.TestSuite):
108 Doctest suites are parallelizable at the module or file level only,
109 since they may be attached to objects that are not individually
110 addressable (like properties). This suite subclass is used when
111 loading doctests from a module to ensure that behavior.
113 This class is used only if the plugin is not fully prepared;
114 in normal use, the loader's suiteClass is used.
119 def __init__(self, tests=(), context=None, can_split=False):
120 self.context = context
121 self.can_split = can_split
122 unittest.TestSuite.__init__(self, tests=tests)
125 return test_address(self.context)
129 return iter(self._tests)
132 return str(self._tests)
135 class Doctest(Plugin):
137 Activate doctest plugin to find and run doctests in non-test modules.
140 suiteClass = DoctestSuite
142 def options(self, parser, env):
143 """Register commmandline options.
145 Plugin.options(self, parser, env)
146 parser.add_option('--doctest-tests', action='store_true',
147 dest='doctest_tests',
148 default=env.get('NOSE_DOCTEST_TESTS'),
149 help="Also look for doctests in test modules. "
150 "Note that classes, methods and functions should "
151 "have either doctests or non-doctest tests, "
152 "not both. [NOSE_DOCTEST_TESTS]")
153 parser.add_option('--doctest-extension', action="append",
154 dest="doctestExtension",
156 help="Also look for doctests in files with "
157 "this extension [NOSE_DOCTEST_EXTENSION]")
158 parser.add_option('--doctest-result-variable',
159 dest='doctest_result_var',
160 default=env.get('NOSE_DOCTEST_RESULT_VAR'),
162 help="Change the variable name set to the result of "
163 "the last interpreter command from the default '_'. "
164 "Can be used to avoid conflicts with the _() "
165 "function used for text translation. "
166 "[NOSE_DOCTEST_RESULT_VAR]")
167 parser.add_option('--doctest-fixtures', action="store",
168 dest="doctestFixtures",
170 help="Find fixtures for a doctest file in module "
171 "with this name appended to the base name "
172 "of the doctest file")
173 # Set the default as a list, if given in env; otherwise
174 # an additional value set on the command line will cause
176 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
177 if env_setting is not None:
178 parser.set_defaults(doctestExtension=tolist(env_setting))
180 def configure(self, options, config):
183 Plugin.configure(self, options, config)
184 self.doctest_result_var = options.doctest_result_var
185 self.doctest_tests = options.doctest_tests
186 self.extension = tolist(options.doctestExtension)
187 self.fixtures = options.doctestFixtures
188 self.finder = doctest.DocTestFinder()
190 def prepareTestLoader(self, loader):
191 """Capture loader's suiteClass.
193 This is used to create test suites from doctest files.
196 self.suiteClass = loader.suiteClass
198 def loadTestsFromModule(self, module):
199 """Load doctests from the module.
201 log.debug("loading from %s", module)
202 if not self.matches(module.__name__):
203 log.debug("Doctest doesn't want module %s", module)
206 tests = self.finder.find(module)
207 except AttributeError:
208 log.exception("Attribute error loading from %s", module)
209 # nose allows module.__test__ = False; doctest does not and throws
213 log.debug("No tests found in %s", module)
216 module_file = src(module.__file__)
217 # FIXME this breaks the id plugin somehow (tests probably don't
218 # get wrapped in result proxy or something)
221 if not test.examples:
223 if not test.filename:
224 test.filename = module_file
225 cases.append(DocTestCase(test, result_var=self.doctest_result_var))
227 yield self.suiteClass(cases, context=module, can_split=False)
229 def loadTestsFromFile(self, filename):
230 """Load doctests from the file.
232 Tests are loaded only if filename's extension matches
233 configured doctest extension.
236 if self.extension and anyp(filename.endswith, self.extension):
237 name = os.path.basename(filename)
244 fixture_context = None
245 globs = {'__file__': filename}
247 base, ext = os.path.splitext(name)
248 dirname = os.path.dirname(filename)
249 sys.path.append(dirname)
250 fixt_mod = base + self.fixtures
252 fixture_context = __import__(
253 fixt_mod, globals(), locals(), ["nop"])
254 except ImportError, e:
256 "Could not import %s: %s (%s)", fixt_mod, e, sys.path)
257 log.debug("Fixture module %s resolved to %s",
258 fixt_mod, fixture_context)
259 if hasattr(fixture_context, 'globs'):
260 globs = fixture_context.globs(globs)
261 parser = doctest.DocTestParser()
262 test = parser.get_doctest(
263 doc, globs=globs, name=name,
264 filename=filename, lineno=0)
268 setUp=getattr(fixture_context, 'setup_test', None),
269 tearDown=getattr(fixture_context, 'teardown_test', None),
270 result_var=self.doctest_result_var)
272 yield ContextList((case,), context=fixture_context)
276 yield False # no tests to load
278 def makeTest(self, obj, parent):
279 """Look for doctests in the given object, which will be a
280 function, method or class.
282 name = getattr(obj, '__name__', 'Unnammed %s' % type(obj))
283 doctests = self.finder.find(obj, module=getmodule(parent), name=name)
285 for test in doctests:
286 if len(test.examples) == 0:
288 yield DocTestCase(test, obj=obj,
289 result_var=self.doctest_result_var)
291 def matches(self, name):
292 # FIXME this seems wrong -- nothing is ever going to
293 # fail this test, since we're given a module NAME not FILE
294 if name == '__init__.py':
296 # FIXME don't think we need include/exclude checks here?
297 return ((self.doctest_tests or not self.conf.testMatch.search(name)
298 or (self.conf.include
301 for inc in self.conf.include])))
302 and (not self.conf.exclude
305 for exc in self.conf.exclude])))
307 def wantFile(self, file):
308 """Override to select all modules and any file ending with
309 configured doctest extension.
311 # always want .py files
312 if file.endswith('.py'):
314 # also want files that match my extension
316 and anyp(file.endswith, self.extension)
317 and (not self.conf.exclude
320 for exc in self.conf.exclude]))):
325 class DocTestCase(doctest.DocTestCase):
326 """Overrides DocTestCase to
327 provide an address() method that returns the correct address for
328 the doctest case. To provide hints for address(), an obj may also
329 be passed -- this will be used as the test object for purposes of
330 determining the test address, if it is provided.
332 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
333 checker=None, obj=None, result_var='_'):
334 self._result_var = result_var
336 super(DocTestCase, self).__init__(
337 test, optionflags=optionflags, setUp=setUp, tearDown=tearDown,
341 if self._nose_obj is not None:
342 return test_address(self._nose_obj)
343 obj = resolve_name(self._dt_test.name)
346 # properties have no connection to the class they are in
347 # so we can't just look 'em up, we have to first look up
348 # the class, then stick the prop on the end
349 parts = self._dt_test.name.split('.')
350 class_name = '.'.join(parts[:-1])
351 cls = resolve_name(class_name)
352 base_addr = test_address(cls)
353 return (base_addr[0], base_addr[1],
354 '.'.join([base_addr[2], parts[-1]]))
356 return test_address(obj)
358 # doctests loaded via find(obj) omit the module name
359 # so we need to override id, __repr__ and shortDescription
360 # bonus: this will squash a 2.3 vs 2.4 incompatiblity
362 name = self._dt_test.name
363 filename = self._dt_test.filename
364 if filename is not None:
365 pk = getpackage(filename)
366 if not name.startswith(pk):
367 name = "%s.%s" % (pk, name)
372 name = name.split('.')
373 return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
376 def shortDescription(self):
377 return 'Doctest: %s' % self.id()
380 if self._result_var is not None:
381 self._old_displayhook = sys.displayhook
382 sys.displayhook = self._displayhook
383 super(DocTestCase, self).setUp()
385 def _displayhook(self, value):
388 setattr(builtin_mod, self._result_var, value)
392 super(DocTestCase, self).tearDown()
393 if self._result_var is not None:
394 sys.displayhook = self._old_displayhook
395 delattr(builtin_mod, self._result_var)
398 class DocFileCase(doctest.DocFileCase):
399 """Overrides to provide address() method that returns the correct
400 address for the doc file case.
402 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
403 checker=None, result_var='_'):
404 self._result_var = result_var
405 super(DocFileCase, self).__init__(
406 test, optionflags=optionflags, setUp=setUp, tearDown=tearDown,
410 return (self._dt_test.filename, None, None)
413 if self._result_var is not None:
414 self._old_displayhook = sys.displayhook
415 sys.displayhook = self._displayhook
416 super(DocFileCase, self).setUp()
418 def _displayhook(self, value):
421 setattr(builtin_mod, self._result_var, value)
425 super(DocFileCase, self).tearDown()
426 if self._result_var is not None:
427 sys.displayhook = self._old_displayhook
428 delattr(builtin_mod, self._result_var)