ipdoctest.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. """Nose Plugin that supports IPython doctests.
  2. Limitations:
  3. - When generating examples for use as doctests, make sure that you have
  4. pretty-printing OFF. This can be done either by setting the
  5. ``PlainTextFormatter.pprint`` option in your configuration file to False, or
  6. by interactively disabling it with %Pprint. This is required so that IPython
  7. output matches that of normal Python, which is used by doctest for internal
  8. execution.
  9. - Do not rely on specific prompt numbers for results (such as using
  10. '_34==True', for example). For IPython tests run via an external process the
  11. prompt numbers may be different, and IPython tests run as normal python code
  12. won't even have these special _NN variables set at all.
  13. """
  14. #-----------------------------------------------------------------------------
  15. # Module imports
  16. # From the standard library
  17. import doctest
  18. import inspect
  19. import logging
  20. import os
  21. import re
  22. import sys
  23. from testpath import modified_env
  24. from inspect import getmodule
  25. # We are overriding the default doctest runner, so we need to import a few
  26. # things from doctest directly
  27. from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
  28. _unittest_reportflags, DocTestRunner,
  29. _extract_future_flags, pdb, _OutputRedirectingPdb,
  30. _exception_traceback,
  31. linecache)
  32. # Third-party modules
  33. from nose.plugins import doctests, Plugin
  34. from nose.util import anyp, tolist
  35. # Our own imports
  36. from IPython.utils.py3compat import builtin_mod, PY3, getcwd
  37. if PY3:
  38. from io import StringIO
  39. else:
  40. from StringIO import StringIO
  41. #-----------------------------------------------------------------------------
  42. # Module globals and other constants
  43. #-----------------------------------------------------------------------------
  44. log = logging.getLogger(__name__)
  45. #-----------------------------------------------------------------------------
  46. # Classes and functions
  47. #-----------------------------------------------------------------------------
  48. def is_extension_module(filename):
  49. """Return whether the given filename is an extension module.
  50. This simply checks that the extension is either .so or .pyd.
  51. """
  52. return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
  53. class DocTestSkip(object):
  54. """Object wrapper for doctests to be skipped."""
  55. ds_skip = """Doctest to skip.
  56. >>> 1 #doctest: +SKIP
  57. """
  58. def __init__(self,obj):
  59. self.obj = obj
  60. def __getattribute__(self,key):
  61. if key == '__doc__':
  62. return DocTestSkip.ds_skip
  63. else:
  64. return getattr(object.__getattribute__(self,'obj'),key)
  65. # Modified version of the one in the stdlib, that fixes a python bug (doctests
  66. # not found in extension modules, http://bugs.python.org/issue3158)
  67. class DocTestFinder(doctest.DocTestFinder):
  68. def _from_module(self, module, object):
  69. """
  70. Return true if the given object is defined in the given
  71. module.
  72. """
  73. if module is None:
  74. return True
  75. elif inspect.isfunction(object):
  76. return module.__dict__ is object.__globals__
  77. elif inspect.isbuiltin(object):
  78. return module.__name__ == object.__module__
  79. elif inspect.isclass(object):
  80. return module.__name__ == object.__module__
  81. elif inspect.ismethod(object):
  82. # This one may be a bug in cython that fails to correctly set the
  83. # __module__ attribute of methods, but since the same error is easy
  84. # to make by extension code writers, having this safety in place
  85. # isn't such a bad idea
  86. return module.__name__ == object.__self__.__class__.__module__
  87. elif inspect.getmodule(object) is not None:
  88. return module is inspect.getmodule(object)
  89. elif hasattr(object, '__module__'):
  90. return module.__name__ == object.__module__
  91. elif isinstance(object, property):
  92. return True # [XX] no way not be sure.
  93. elif inspect.ismethoddescriptor(object):
  94. # Unbound PyQt signals reach this point in Python 3.4b3, and we want
  95. # to avoid throwing an error. See also http://bugs.python.org/issue3158
  96. return False
  97. else:
  98. raise ValueError("object must be a class or function, got %r" % object)
  99. def _find(self, tests, obj, name, module, source_lines, globs, seen):
  100. """
  101. Find tests for the given object and any contained objects, and
  102. add them to `tests`.
  103. """
  104. print('_find for:', obj, name, module) # dbg
  105. if hasattr(obj,"skip_doctest"):
  106. #print 'SKIPPING DOCTEST FOR:',obj # dbg
  107. obj = DocTestSkip(obj)
  108. doctest.DocTestFinder._find(self,tests, obj, name, module,
  109. source_lines, globs, seen)
  110. # Below we re-run pieces of the above method with manual modifications,
  111. # because the original code is buggy and fails to correctly identify
  112. # doctests in extension modules.
  113. # Local shorthands
  114. from inspect import isroutine, isclass
  115. # Look for tests in a module's contained objects.
  116. if inspect.ismodule(obj) and self._recurse:
  117. for valname, val in obj.__dict__.items():
  118. valname1 = '%s.%s' % (name, valname)
  119. if ( (isroutine(val) or isclass(val))
  120. and self._from_module(module, val) ):
  121. self._find(tests, val, valname1, module, source_lines,
  122. globs, seen)
  123. # Look for tests in a class's contained objects.
  124. if inspect.isclass(obj) and self._recurse:
  125. #print 'RECURSE into class:',obj # dbg
  126. for valname, val in obj.__dict__.items():
  127. # Special handling for staticmethod/classmethod.
  128. if isinstance(val, staticmethod):
  129. val = getattr(obj, valname)
  130. if isinstance(val, classmethod):
  131. val = getattr(obj, valname).__func__
  132. # Recurse to methods, properties, and nested classes.
  133. if ((inspect.isfunction(val) or inspect.isclass(val) or
  134. inspect.ismethod(val) or
  135. isinstance(val, property)) and
  136. self._from_module(module, val)):
  137. valname = '%s.%s' % (name, valname)
  138. self._find(tests, val, valname, module, source_lines,
  139. globs, seen)
  140. class IPDoctestOutputChecker(doctest.OutputChecker):
  141. """Second-chance checker with support for random tests.
  142. If the default comparison doesn't pass, this checker looks in the expected
  143. output string for flags that tell us to ignore the output.
  144. """
  145. random_re = re.compile(r'#\s*random\s+')
  146. def check_output(self, want, got, optionflags):
  147. """Check output, accepting special markers embedded in the output.
  148. If the output didn't pass the default validation but the special string
  149. '#random' is included, we accept it."""
  150. # Let the original tester verify first, in case people have valid tests
  151. # that happen to have a comment saying '#random' embedded in.
  152. ret = doctest.OutputChecker.check_output(self, want, got,
  153. optionflags)
  154. if not ret and self.random_re.search(want):
  155. #print >> sys.stderr, 'RANDOM OK:',want # dbg
  156. return True
  157. return ret
  158. class DocTestCase(doctests.DocTestCase):
  159. """Proxy for DocTestCase: provides an address() method that
  160. returns the correct address for the doctest case. Otherwise
  161. acts as a proxy to the test case. To provide hints for address(),
  162. an obj may also be passed -- this will be used as the test object
  163. for purposes of determining the test address, if it is provided.
  164. """
  165. # Note: this method was taken from numpy's nosetester module.
  166. # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
  167. # its constructor that blocks non-default arguments from being passed
  168. # down into doctest.DocTestCase
  169. def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
  170. checker=None, obj=None, result_var='_'):
  171. self._result_var = result_var
  172. doctests.DocTestCase.__init__(self, test,
  173. optionflags=optionflags,
  174. setUp=setUp, tearDown=tearDown,
  175. checker=checker)
  176. # Now we must actually copy the original constructor from the stdlib
  177. # doctest class, because we can't call it directly and a bug in nose
  178. # means it never gets passed the right arguments.
  179. self._dt_optionflags = optionflags
  180. self._dt_checker = checker
  181. self._dt_test = test
  182. self._dt_test_globs_ori = test.globs
  183. self._dt_setUp = setUp
  184. self._dt_tearDown = tearDown
  185. # XXX - store this runner once in the object!
  186. runner = IPDocTestRunner(optionflags=optionflags,
  187. checker=checker, verbose=False)
  188. self._dt_runner = runner
  189. # Each doctest should remember the directory it was loaded from, so
  190. # things like %run work without too many contortions
  191. self._ori_dir = os.path.dirname(test.filename)
  192. # Modified runTest from the default stdlib
  193. def runTest(self):
  194. test = self._dt_test
  195. runner = self._dt_runner
  196. old = sys.stdout
  197. new = StringIO()
  198. optionflags = self._dt_optionflags
  199. if not (optionflags & REPORTING_FLAGS):
  200. # The option flags don't include any reporting flags,
  201. # so add the default reporting flags
  202. optionflags |= _unittest_reportflags
  203. try:
  204. # Save our current directory and switch out to the one where the
  205. # test was originally created, in case another doctest did a
  206. # directory change. We'll restore this in the finally clause.
  207. curdir = getcwd()
  208. #print 'runTest in dir:', self._ori_dir # dbg
  209. os.chdir(self._ori_dir)
  210. runner.DIVIDER = "-"*70
  211. failures, tries = runner.run(test,out=new.write,
  212. clear_globs=False)
  213. finally:
  214. sys.stdout = old
  215. os.chdir(curdir)
  216. if failures:
  217. raise self.failureException(self.format_failure(new.getvalue()))
  218. def setUp(self):
  219. """Modified test setup that syncs with ipython namespace"""
  220. #print "setUp test", self._dt_test.examples # dbg
  221. if isinstance(self._dt_test.examples[0], IPExample):
  222. # for IPython examples *only*, we swap the globals with the ipython
  223. # namespace, after updating it with the globals (which doctest
  224. # fills with the necessary info from the module being tested).
  225. self.user_ns_orig = {}
  226. self.user_ns_orig.update(_ip.user_ns)
  227. _ip.user_ns.update(self._dt_test.globs)
  228. # We must remove the _ key in the namespace, so that Python's
  229. # doctest code sets it naturally
  230. _ip.user_ns.pop('_', None)
  231. _ip.user_ns['__builtins__'] = builtin_mod
  232. self._dt_test.globs = _ip.user_ns
  233. super(DocTestCase, self).setUp()
  234. def tearDown(self):
  235. # Undo the test.globs reassignment we made, so that the parent class
  236. # teardown doesn't destroy the ipython namespace
  237. if isinstance(self._dt_test.examples[0], IPExample):
  238. self._dt_test.globs = self._dt_test_globs_ori
  239. _ip.user_ns.clear()
  240. _ip.user_ns.update(self.user_ns_orig)
  241. # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
  242. # it does look like one to me: its tearDown method tries to run
  243. #
  244. # delattr(builtin_mod, self._result_var)
  245. #
  246. # without checking that the attribute really is there; it implicitly
  247. # assumes it should have been set via displayhook. But if the
  248. # displayhook was never called, this doesn't necessarily happen. I
  249. # haven't been able to find a little self-contained example outside of
  250. # ipython that would show the problem so I can report it to the nose
  251. # team, but it does happen a lot in our code.
  252. #
  253. # So here, we just protect as narrowly as possible by trapping an
  254. # attribute error whose message would be the name of self._result_var,
  255. # and letting any other error propagate.
  256. try:
  257. super(DocTestCase, self).tearDown()
  258. except AttributeError as exc:
  259. if exc.args[0] != self._result_var:
  260. raise
  261. # A simple subclassing of the original with a different class name, so we can
  262. # distinguish and treat differently IPython examples from pure python ones.
  263. class IPExample(doctest.Example): pass
  264. class IPExternalExample(doctest.Example):
  265. """Doctest examples to be run in an external process."""
  266. def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
  267. options=None):
  268. # Parent constructor
  269. doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
  270. # An EXTRA newline is needed to prevent pexpect hangs
  271. self.source += '\n'
  272. class IPDocTestParser(doctest.DocTestParser):
  273. """
  274. A class used to parse strings containing doctest examples.
  275. Note: This is a version modified to properly recognize IPython input and
  276. convert any IPython examples into valid Python ones.
  277. """
  278. # This regular expression is used to find doctest examples in a
  279. # string. It defines three groups: `source` is the source code
  280. # (including leading indentation and prompts); `indent` is the
  281. # indentation of the first (PS1) line of the source code; and
  282. # `want` is the expected output (including leading indentation).
  283. # Classic Python prompts or default IPython ones
  284. _PS1_PY = r'>>>'
  285. _PS2_PY = r'\.\.\.'
  286. _PS1_IP = r'In\ \[\d+\]:'
  287. _PS2_IP = r'\ \ \ \.\.\.+:'
  288. _RE_TPL = r'''
  289. # Source consists of a PS1 line followed by zero or more PS2 lines.
  290. (?P<source>
  291. (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
  292. (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
  293. \n? # a newline
  294. # Want consists of any non-blank lines that do not start with PS1.
  295. (?P<want> (?:(?![ ]*$) # Not a blank line
  296. (?![ ]*%s) # Not a line starting with PS1
  297. (?![ ]*%s) # Not a line starting with PS2
  298. .*$\n? # But any other line
  299. )*)
  300. '''
  301. _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
  302. re.MULTILINE | re.VERBOSE)
  303. _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
  304. re.MULTILINE | re.VERBOSE)
  305. # Mark a test as being fully random. In this case, we simply append the
  306. # random marker ('#random') to each individual example's output. This way
  307. # we don't need to modify any other code.
  308. _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
  309. # Mark tests to be executed in an external process - currently unsupported.
  310. _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
  311. def ip2py(self,source):
  312. """Convert input IPython source into valid Python."""
  313. block = _ip.input_transformer_manager.transform_cell(source)
  314. if len(block.splitlines()) == 1:
  315. return _ip.prefilter(block)
  316. else:
  317. return block
  318. def parse(self, string, name='<string>'):
  319. """
  320. Divide the given string into examples and intervening text,
  321. and return them as a list of alternating Examples and strings.
  322. Line numbers for the Examples are 0-based. The optional
  323. argument `name` is a name identifying this string, and is only
  324. used for error messages.
  325. """
  326. #print 'Parse string:\n',string # dbg
  327. string = string.expandtabs()
  328. # If all lines begin with the same indentation, then strip it.
  329. min_indent = self._min_indent(string)
  330. if min_indent > 0:
  331. string = '\n'.join([l[min_indent:] for l in string.split('\n')])
  332. output = []
  333. charno, lineno = 0, 0
  334. # We make 'all random' tests by adding the '# random' mark to every
  335. # block of output in the test.
  336. if self._RANDOM_TEST.search(string):
  337. random_marker = '\n# random'
  338. else:
  339. random_marker = ''
  340. # Whether to convert the input from ipython to python syntax
  341. ip2py = False
  342. # Find all doctest examples in the string. First, try them as Python
  343. # examples, then as IPython ones
  344. terms = list(self._EXAMPLE_RE_PY.finditer(string))
  345. if terms:
  346. # Normal Python example
  347. #print '-'*70 # dbg
  348. #print 'PyExample, Source:\n',string # dbg
  349. #print '-'*70 # dbg
  350. Example = doctest.Example
  351. else:
  352. # It's an ipython example. Note that IPExamples are run
  353. # in-process, so their syntax must be turned into valid python.
  354. # IPExternalExamples are run out-of-process (via pexpect) so they
  355. # don't need any filtering (a real ipython will be executing them).
  356. terms = list(self._EXAMPLE_RE_IP.finditer(string))
  357. if self._EXTERNAL_IP.search(string):
  358. #print '-'*70 # dbg
  359. #print 'IPExternalExample, Source:\n',string # dbg
  360. #print '-'*70 # dbg
  361. Example = IPExternalExample
  362. else:
  363. #print '-'*70 # dbg
  364. #print 'IPExample, Source:\n',string # dbg
  365. #print '-'*70 # dbg
  366. Example = IPExample
  367. ip2py = True
  368. for m in terms:
  369. # Add the pre-example text to `output`.
  370. output.append(string[charno:m.start()])
  371. # Update lineno (lines before this example)
  372. lineno += string.count('\n', charno, m.start())
  373. # Extract info from the regexp match.
  374. (source, options, want, exc_msg) = \
  375. self._parse_example(m, name, lineno,ip2py)
  376. # Append the random-output marker (it defaults to empty in most
  377. # cases, it's only non-empty for 'all-random' tests):
  378. want += random_marker
  379. if Example is IPExternalExample:
  380. options[doctest.NORMALIZE_WHITESPACE] = True
  381. want += '\n'
  382. # Create an Example, and add it to the list.
  383. if not self._IS_BLANK_OR_COMMENT(source):
  384. output.append(Example(source, want, exc_msg,
  385. lineno=lineno,
  386. indent=min_indent+len(m.group('indent')),
  387. options=options))
  388. # Update lineno (lines inside this example)
  389. lineno += string.count('\n', m.start(), m.end())
  390. # Update charno.
  391. charno = m.end()
  392. # Add any remaining post-example text to `output`.
  393. output.append(string[charno:])
  394. return output
  395. def _parse_example(self, m, name, lineno,ip2py=False):
  396. """
  397. Given a regular expression match from `_EXAMPLE_RE` (`m`),
  398. return a pair `(source, want)`, where `source` is the matched
  399. example's source code (with prompts and indentation stripped);
  400. and `want` is the example's expected output (with indentation
  401. stripped).
  402. `name` is the string's name, and `lineno` is the line number
  403. where the example starts; both are used for error messages.
  404. Optional:
  405. `ip2py`: if true, filter the input via IPython to convert the syntax
  406. into valid python.
  407. """
  408. # Get the example's indentation level.
  409. indent = len(m.group('indent'))
  410. # Divide source into lines; check that they're properly
  411. # indented; and then strip their indentation & prompts.
  412. source_lines = m.group('source').split('\n')
  413. # We're using variable-length input prompts
  414. ps1 = m.group('ps1')
  415. ps2 = m.group('ps2')
  416. ps1_len = len(ps1)
  417. self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
  418. if ps2:
  419. self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
  420. source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
  421. if ip2py:
  422. # Convert source input from IPython into valid Python syntax
  423. source = self.ip2py(source)
  424. # Divide want into lines; check that it's properly indented; and
  425. # then strip the indentation. Spaces before the last newline should
  426. # be preserved, so plain rstrip() isn't good enough.
  427. want = m.group('want')
  428. want_lines = want.split('\n')
  429. if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
  430. del want_lines[-1] # forget final newline & spaces after it
  431. self._check_prefix(want_lines, ' '*indent, name,
  432. lineno + len(source_lines))
  433. # Remove ipython output prompt that might be present in the first line
  434. want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
  435. want = '\n'.join([wl[indent:] for wl in want_lines])
  436. # If `want` contains a traceback message, then extract it.
  437. m = self._EXCEPTION_RE.match(want)
  438. if m:
  439. exc_msg = m.group('msg')
  440. else:
  441. exc_msg = None
  442. # Extract options from the source.
  443. options = self._find_options(source, name, lineno)
  444. return source, options, want, exc_msg
  445. def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
  446. """
  447. Given the lines of a source string (including prompts and
  448. leading indentation), check to make sure that every prompt is
  449. followed by a space character. If any line is not followed by
  450. a space character, then raise ValueError.
  451. Note: IPython-modified version which takes the input prompt length as a
  452. parameter, so that prompts of variable length can be dealt with.
  453. """
  454. space_idx = indent+ps1_len
  455. min_len = space_idx+1
  456. for i, line in enumerate(lines):
  457. if len(line) >= min_len and line[space_idx] != ' ':
  458. raise ValueError('line %r of the docstring for %s '
  459. 'lacks blank after %s: %r' %
  460. (lineno+i+1, name,
  461. line[indent:space_idx], line))
  462. SKIP = doctest.register_optionflag('SKIP')
  463. class IPDocTestRunner(doctest.DocTestRunner,object):
  464. """Test runner that synchronizes the IPython namespace with test globals.
  465. """
  466. def run(self, test, compileflags=None, out=None, clear_globs=True):
  467. # Hack: ipython needs access to the execution context of the example,
  468. # so that it can propagate user variables loaded by %run into
  469. # test.globs. We put them here into our modified %run as a function
  470. # attribute. Our new %run will then only make the namespace update
  471. # when called (rather than unconconditionally updating test.globs here
  472. # for all examples, most of which won't be calling %run anyway).
  473. #_ip._ipdoctest_test_globs = test.globs
  474. #_ip._ipdoctest_test_filename = test.filename
  475. test.globs.update(_ip.user_ns)
  476. # Override terminal size to standardise traceback format
  477. with modified_env({'COLUMNS': '80', 'LINES': '24'}):
  478. return super(IPDocTestRunner,self).run(test,
  479. compileflags,out,clear_globs)
  480. class DocFileCase(doctest.DocFileCase):
  481. """Overrides to provide filename
  482. """
  483. def address(self):
  484. return (self._dt_test.filename, None, None)
  485. class ExtensionDoctest(doctests.Doctest):
  486. """Nose Plugin that supports doctests in extension modules.
  487. """
  488. name = 'extdoctest' # call nosetests with --with-extdoctest
  489. enabled = True
  490. def options(self, parser, env=os.environ):
  491. Plugin.options(self, parser, env)
  492. parser.add_option('--doctest-tests', action='store_true',
  493. dest='doctest_tests',
  494. default=env.get('NOSE_DOCTEST_TESTS',True),
  495. help="Also look for doctests in test modules. "
  496. "Note that classes, methods and functions should "
  497. "have either doctests or non-doctest tests, "
  498. "not both. [NOSE_DOCTEST_TESTS]")
  499. parser.add_option('--doctest-extension', action="append",
  500. dest="doctestExtension",
  501. help="Also look for doctests in files with "
  502. "this extension [NOSE_DOCTEST_EXTENSION]")
  503. # Set the default as a list, if given in env; otherwise
  504. # an additional value set on the command line will cause
  505. # an error.
  506. env_setting = env.get('NOSE_DOCTEST_EXTENSION')
  507. if env_setting is not None:
  508. parser.set_defaults(doctestExtension=tolist(env_setting))
  509. def configure(self, options, config):
  510. Plugin.configure(self, options, config)
  511. # Pull standard doctest plugin out of config; we will do doctesting
  512. config.plugins.plugins = [p for p in config.plugins.plugins
  513. if p.name != 'doctest']
  514. self.doctest_tests = options.doctest_tests
  515. self.extension = tolist(options.doctestExtension)
  516. self.parser = doctest.DocTestParser()
  517. self.finder = DocTestFinder()
  518. self.checker = IPDoctestOutputChecker()
  519. self.globs = None
  520. self.extraglobs = None
  521. def loadTestsFromExtensionModule(self,filename):
  522. bpath,mod = os.path.split(filename)
  523. modname = os.path.splitext(mod)[0]
  524. try:
  525. sys.path.append(bpath)
  526. module = __import__(modname)
  527. tests = list(self.loadTestsFromModule(module))
  528. finally:
  529. sys.path.pop()
  530. return tests
  531. # NOTE: the method below is almost a copy of the original one in nose, with
  532. # a few modifications to control output checking.
  533. def loadTestsFromModule(self, module):
  534. #print '*** ipdoctest - lTM',module # dbg
  535. if not self.matches(module.__name__):
  536. log.debug("Doctest doesn't want module %s", module)
  537. return
  538. tests = self.finder.find(module,globs=self.globs,
  539. extraglobs=self.extraglobs)
  540. if not tests:
  541. return
  542. # always use whitespace and ellipsis options
  543. optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
  544. tests.sort()
  545. module_file = module.__file__
  546. if module_file[-4:] in ('.pyc', '.pyo'):
  547. module_file = module_file[:-1]
  548. for test in tests:
  549. if not test.examples:
  550. continue
  551. if not test.filename:
  552. test.filename = module_file
  553. yield DocTestCase(test,
  554. optionflags=optionflags,
  555. checker=self.checker)
  556. def loadTestsFromFile(self, filename):
  557. #print "ipdoctest - from file", filename # dbg
  558. if is_extension_module(filename):
  559. for t in self.loadTestsFromExtensionModule(filename):
  560. yield t
  561. else:
  562. if self.extension and anyp(filename.endswith, self.extension):
  563. name = os.path.basename(filename)
  564. dh = open(filename)
  565. try:
  566. doc = dh.read()
  567. finally:
  568. dh.close()
  569. test = self.parser.get_doctest(
  570. doc, globs={'__file__': filename}, name=name,
  571. filename=filename, lineno=0)
  572. if test.examples:
  573. #print 'FileCase:',test.examples # dbg
  574. yield DocFileCase(test)
  575. else:
  576. yield False # no tests to load
  577. class IPythonDoctest(ExtensionDoctest):
  578. """Nose Plugin that supports doctests in extension modules.
  579. """
  580. name = 'ipdoctest' # call nosetests with --with-ipdoctest
  581. enabled = True
  582. def makeTest(self, obj, parent):
  583. """Look for doctests in the given object, which will be a
  584. function, method or class.
  585. """
  586. #print 'Plugin analyzing:', obj, parent # dbg
  587. # always use whitespace and ellipsis options
  588. optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
  589. doctests = self.finder.find(obj, module=getmodule(parent))
  590. if doctests:
  591. for test in doctests:
  592. if len(test.examples) == 0:
  593. continue
  594. yield DocTestCase(test, obj=obj,
  595. optionflags=optionflags,
  596. checker=self.checker)
  597. def options(self, parser, env=os.environ):
  598. #print "Options for nose plugin:", self.name # dbg
  599. Plugin.options(self, parser, env)
  600. parser.add_option('--ipdoctest-tests', action='store_true',
  601. dest='ipdoctest_tests',
  602. default=env.get('NOSE_IPDOCTEST_TESTS',True),
  603. help="Also look for doctests in test modules. "
  604. "Note that classes, methods and functions should "
  605. "have either doctests or non-doctest tests, "
  606. "not both. [NOSE_IPDOCTEST_TESTS]")
  607. parser.add_option('--ipdoctest-extension', action="append",
  608. dest="ipdoctest_extension",
  609. help="Also look for doctests in files with "
  610. "this extension [NOSE_IPDOCTEST_EXTENSION]")
  611. # Set the default as a list, if given in env; otherwise
  612. # an additional value set on the command line will cause
  613. # an error.
  614. env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
  615. if env_setting is not None:
  616. parser.set_defaults(ipdoctest_extension=tolist(env_setting))
  617. def configure(self, options, config):
  618. #print "Configuring nose plugin:", self.name # dbg
  619. Plugin.configure(self, options, config)
  620. # Pull standard doctest plugin out of config; we will do doctesting
  621. config.plugins.plugins = [p for p in config.plugins.plugins
  622. if p.name != 'doctest']
  623. self.doctest_tests = options.ipdoctest_tests
  624. self.extension = tolist(options.ipdoctest_extension)
  625. self.parser = IPDocTestParser()
  626. self.finder = DocTestFinder(parser=self.parser)
  627. self.checker = IPDoctestOutputChecker()
  628. self.globs = None
  629. self.extraglobs = None