From 06ef375ccb39961624a54f2b4618a8ab2eb03291 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 16 Jun 2009 23:13:01 +0200 Subject: [PATCH] #130: Fix obscure IndexError in doctest extension. --- CHANGES | 2 ++ sphinx/ext/doctest.py | 35 +++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index b7168ed82..c69cc057c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 0.6.2 (in development) ============================== +* #130: Fix obscure IndexError in doctest extension. + * #167: Make glossary sorting case-independent. * #196: Add a warning if an extension module doesn't have a diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 51463661a..9bf086f74 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -159,6 +159,23 @@ class SphinxDocTestRunner(doctest.DocTestRunner): out(io.getvalue()) return res + def _DocTestRunner__patched_linecache_getlines(self, filename, + module_globals=None): + # this is overridden from DocTestRunner adding the try-except below + m = self._DocTestRunner__LINECACHE_FILENAME_RE.match(filename) + if m and m.group('name') == self.test.name: + try: + example = self.test.examples[int(m.group('examplenum'))] + # because we compile multiple doctest blocks with the same name + # (viz. the group name) this might, for outer stack frames in a + # traceback, get the wrong test which might not have enough examples + except IndexError: + pass + else: + return example.source.splitlines(True) + return self.save_linecache_getlines(filename, module_globals) + + # the new builder -- use sphinx-build.py -b doctest to run class DocTestBuilder(Builder): @@ -302,13 +319,13 @@ Doctest summary def test_group(self, group, filename): ns = {} - examples = [] + setup_examples = [] for setup in group.setup: - examples.append(doctest.Example(setup.code, '', - lineno=setup.lineno)) - if examples: + setup_examples.append(doctest.Example(setup.code, '', + lineno=setup.lineno)) + if setup_examples: # simulate a doctest with the setup code - setup_doctest = doctest.DocTest(examples, {}, + setup_doctest = doctest.DocTest(setup_examples, {}, '%s (setup code)' % group.name, filename, 0, None) setup_doctest.globs = ns @@ -321,8 +338,9 @@ Doctest summary return for code in group.tests: if len(code) == 1: - test = parser.get_doctest(code[0].code, {}, - group.name, filename, code[0].lineno) + # ordinary doctests (code/output interleaved) + test = parser.get_doctest(code[0].code, {}, group.name, + filename, code[0].lineno) if not test.examples: continue for example in test.examples: @@ -330,8 +348,9 @@ Doctest summary new_opt = code[0].options.copy() new_opt.update(example.options) example.options = new_opt - self.type = 'single' # ordinary doctests + self.type = 'single' # as for ordinary doctests else: + # testcode and output separate output = code[1] and code[1].code or '' options = code[1] and code[1].options or {} # disable processing as it is not needed