~plane1/maus/devel_624

« back to all changes in this revision

Viewing changes to third_party/nose-0.11.3/lib/python/nose/plugins/doctests.py

  • Committer: tunnell
  • Date: 2010-09-30 13:56:05 UTC
  • Revision ID: tunnell@itchy-20100930135605-wxbkfgy75p0sndk3
add third party

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
6
 
 
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
 
10
extension(s) to load.
 
11
 
 
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
 
17
``widgets_fixt.py``.
 
18
 
 
19
A fixtures module may define any or all of the following functions:
 
20
 
 
21
* setup([module]) or setup_module([module])
 
22
   
 
23
  Called before the test runs. You may raise SkipTest to skip all tests.
 
24
  
 
25
* teardown([module]) or teardown_module([module])
 
26
 
 
27
  Called after the test runs, if setup/setup_module did not raise an
 
28
  unhandled exception.
 
29
 
 
30
* setup_test(test)
 
31
 
 
32
  Called before the test. NOTE: the argument passed is a
 
33
  doctest.DocTest instance, *not* a unittest.TestCase.
 
34
  
 
35
* teardown_test(test)
 
36
 
 
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.
 
39
  
 
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
 
42
test.
 
43
 
 
44
.. note ::
 
45
 
 
46
   See :doc:`../doc_tests/test_doctest_fixtures/doctest_fixtures` for
 
47
   additional documentation and examples.
 
48
 
 
49
"""
 
50
from __future__ import generators
 
51
 
 
52
import logging
 
53
import os
 
54
import sys
 
55
import unittest
 
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
 
61
try:
 
62
    from cStringIO import StringIO
 
63
except ImportError:
 
64
    from StringIO import StringIO
 
65
import sys
 
66
import __builtin__
 
67
 
 
68
log = logging.getLogger(__name__)
 
69
 
 
70
try:
 
71
    import doctest
 
72
    doctest.DocTestCase
 
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
 
77
 
 
78
 
 
79
#
 
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.
 
83
#
 
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
 
86
#
 
87
_orp = doctest._OutputRedirectingPdb
 
88
 
 
89
class NoseOutputRedirectingPdb(_orp):
 
90
    def __init__(self, out):
 
91
        self.__debugger_used = False
 
92
        _orp.__init__(self, out)
 
93
 
 
94
    def set_trace(self):
 
95
        self.__debugger_used = True
 
96
        _orp.set_trace(self, sys._getframe().f_back)
 
97
 
 
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    
 
104
 
 
105
 
 
106
class DoctestSuite(unittest.TestSuite):
 
107
    """
 
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.
 
112
 
 
113
    This class is used only if the plugin is not fully prepared;
 
114
    in normal use, the loader's suiteClass is used.
 
115
    
 
116
    """
 
117
    can_split = False
 
118
    
 
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)
 
123
 
 
124
    def address(self):
 
125
        return test_address(self.context)
 
126
 
 
127
    def __iter__(self):
 
128
        # 2.3 compat
 
129
        return iter(self._tests)
 
130
 
 
131
    def __str__(self):
 
132
        return str(self._tests)
 
133
 
 
134
        
 
135
class Doctest(Plugin):
 
136
    """
 
137
    Activate doctest plugin to find and run doctests in non-test modules.
 
138
    """
 
139
    extension = None
 
140
    suiteClass = DoctestSuite
 
141
    
 
142
    def options(self, parser, env):
 
143
        """Register commmandline options.
 
144
        """
 
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",
 
155
                          metavar="EXT",
 
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'),
 
161
                          metavar="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",
 
169
                          metavar="SUFFIX",
 
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
 
175
        # an error.
 
176
        env_setting = env.get('NOSE_DOCTEST_EXTENSION')
 
177
        if env_setting is not None:
 
178
            parser.set_defaults(doctestExtension=tolist(env_setting))
 
179
 
 
180
    def configure(self, options, config):
 
181
        """Configure plugin.
 
182
        """
 
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()
 
189
        
 
190
    def prepareTestLoader(self, loader):
 
191
        """Capture loader's suiteClass.
 
192
 
 
193
        This is used to create test suites from doctest files.
 
194
        
 
195
        """
 
196
        self.suiteClass = loader.suiteClass
 
197
 
 
198
    def loadTestsFromModule(self, module):
 
199
        """Load doctests from the module.
 
200
        """
 
201
        log.debug("loading from %s", module)
 
202
        if not self.matches(module.__name__):
 
203
            log.debug("Doctest doesn't want module %s", module)
 
204
            return
 
205
        try:
 
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
 
210
            # AttributeError
 
211
            return
 
212
        if not tests:
 
213
            log.debug("No tests found in %s", module)
 
214
            return
 
215
        tests.sort()
 
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)
 
219
        cases = []
 
220
        for test in tests:
 
221
            if not test.examples:
 
222
                continue
 
223
            if not test.filename:
 
224
                test.filename = module_file
 
225
            cases.append(DocTestCase(test, result_var=self.doctest_result_var))
 
226
        if cases:
 
227
            yield self.suiteClass(cases, context=module, can_split=False)
 
228
            
 
229
    def loadTestsFromFile(self, filename):
 
230
        """Load doctests from the file.
 
231
 
 
232
        Tests are loaded only if filename's extension matches
 
233
        configured doctest extension.
 
234
 
 
235
        """
 
236
        if self.extension and anyp(filename.endswith, self.extension):
 
237
            name = os.path.basename(filename)
 
238
            dh = open(filename)
 
239
            try:
 
240
                doc = dh.read()
 
241
            finally:
 
242
                dh.close()
 
243
 
 
244
            fixture_context = None
 
245
            globs = {'__file__': filename}
 
246
            if self.fixtures:
 
247
                base, ext = os.path.splitext(name)
 
248
                dirname = os.path.dirname(filename)
 
249
                sys.path.append(dirname)
 
250
                fixt_mod = base + self.fixtures
 
251
                try:
 
252
                    fixture_context = __import__(
 
253
                        fixt_mod, globals(), locals(), ["nop"])
 
254
                except ImportError, e:
 
255
                    log.debug(
 
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)
 
265
            if test.examples:
 
266
                case = DocFileCase(
 
267
                    test,
 
268
                    setUp=getattr(fixture_context, 'setup_test', None),
 
269
                    tearDown=getattr(fixture_context, 'teardown_test', None),
 
270
                    result_var=self.doctest_result_var)
 
271
                if fixture_context:
 
272
                    yield ContextList((case,), context=fixture_context)
 
273
                else:
 
274
                    yield case
 
275
            else:
 
276
                yield False # no tests to load
 
277
            
 
278
    def makeTest(self, obj, parent):
 
279
        """Look for doctests in the given object, which will be a
 
280
        function, method or class.
 
281
        """
 
282
        name = getattr(obj, '__name__', 'Unnammed %s' % type(obj))
 
283
        doctests = self.finder.find(obj, module=getmodule(parent), name=name)
 
284
        if doctests:
 
285
            for test in doctests:
 
286
                if len(test.examples) == 0:
 
287
                    continue
 
288
                yield DocTestCase(test, obj=obj,
 
289
                                  result_var=self.doctest_result_var)
 
290
    
 
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':
 
295
            return False
 
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 
 
299
                     and filter(None,
 
300
                                [inc.search(name)
 
301
                                 for inc in self.conf.include])))
 
302
                and (not self.conf.exclude 
 
303
                     or not filter(None,
 
304
                                   [exc.search(name)
 
305
                                    for exc in self.conf.exclude])))
 
306
    
 
307
    def wantFile(self, file):
 
308
        """Override to select all modules and any file ending with
 
309
        configured doctest extension.
 
310
        """
 
311
        # always want .py files
 
312
        if file.endswith('.py'):
 
313
            return True
 
314
        # also want files that match my extension
 
315
        if (self.extension
 
316
            and anyp(file.endswith, self.extension)
 
317
            and (not self.conf.exclude
 
318
                 or not filter(None, 
 
319
                               [exc.search(file)
 
320
                                for exc in self.conf.exclude]))):
 
321
            return True
 
322
        return None
 
323
 
 
324
 
 
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.
 
331
    """
 
332
    def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
 
333
                 checker=None, obj=None, result_var='_'):
 
334
        self._result_var = result_var
 
335
        self._nose_obj = obj
 
336
        super(DocTestCase, self).__init__(
 
337
            test, optionflags=optionflags, setUp=setUp, tearDown=tearDown,
 
338
            checker=checker)
 
339
    
 
340
    def address(self):
 
341
        if self._nose_obj is not None:
 
342
            return test_address(self._nose_obj)
 
343
        obj = resolve_name(self._dt_test.name)
 
344
 
 
345
        if isproperty(obj):
 
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]]))
 
355
        else:
 
356
            return test_address(obj)
 
357
    
 
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
 
361
    def id(self):
 
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)
 
368
        return name
 
369
    
 
370
    def __repr__(self):
 
371
        name = self.id()
 
372
        name = name.split('.')
 
373
        return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
 
374
    __str__ = __repr__
 
375
 
 
376
    def shortDescription(self):
 
377
        return 'Doctest: %s' % self.id()
 
378
 
 
379
    def setUp(self):
 
380
        if self._result_var is not None:
 
381
            self._old_displayhook = sys.displayhook
 
382
            sys.displayhook = self._displayhook
 
383
        super(DocTestCase, self).setUp()
 
384
 
 
385
    def _displayhook(self, value):
 
386
        if value is None:
 
387
            return
 
388
        setattr(__builtin__, self._result_var,  value)
 
389
        print repr(value)
 
390
 
 
391
    def tearDown(self):
 
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)
 
396
 
 
397
 
 
398
class DocFileCase(doctest.DocFileCase):
 
399
    """Overrides to provide address() method that returns the correct
 
400
    address for the doc file case.
 
401
    """
 
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,
 
407
            checker=None)
 
408
 
 
409
    def address(self):
 
410
        return (self._dt_test.filename, None, None)
 
411
 
 
412
    def setUp(self):
 
413
        if self._result_var is not None:
 
414
            self._old_displayhook = sys.displayhook
 
415
            sys.displayhook = self._displayhook
 
416
        super(DocFileCase, self).setUp()
 
417
 
 
418
    def _displayhook(self, value):
 
419
        if value is None:
 
420
            return
 
421
        setattr(__builtin__, self._result_var, value)
 
422
        print repr(value)
 
423
 
 
424
    def tearDown(self):
 
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)