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
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__, 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__, 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__, 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__, self._result_var)