66import traceback
77import warnings
88from contextlib import contextmanager
9+ from typing import Dict
10+ from typing import List
11+ from typing import Optional
912from typing import Sequence
1013from typing import Tuple
14+ from typing import Union
1115
1216import pytest
1317from _pytest import outcomes
2024from _pytest .python_api import approx
2125from _pytest .warning_types import PytestWarning
2226
27+ if False : # TYPE_CHECKING
28+ import doctest
29+ from typing import Type
30+
2331DOCTEST_REPORT_CHOICE_NONE = "none"
2432DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
2533DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
3644
3745# Lazy definition of runner class
3846RUNNER_CLASS = None
47+ # Lazy definition of output checker class
48+ CHECKER_CLASS = None # type: Optional[Type[doctest.OutputChecker]]
3949
4050
4151def pytest_addoption (parser ):
@@ -139,7 +149,7 @@ def __init__(self, failures):
139149 self .failures = failures
140150
141151
142- def _init_runner_class ():
152+ def _init_runner_class () -> "Type[doctest.DocTestRunner]" :
143153 import doctest
144154
145155 class PytestDoctestRunner (doctest .DebugRunner ):
@@ -177,12 +187,19 @@ def report_unexpected_exception(self, out, test, example, exc_info):
177187 return PytestDoctestRunner
178188
179189
180- def _get_runner (checker = None , verbose = None , optionflags = 0 , continue_on_failure = True ):
190+ def _get_runner (
191+ checker : Optional ["doctest.OutputChecker" ] = None ,
192+ verbose : Optional [bool ] = None ,
193+ optionflags : int = 0 ,
194+ continue_on_failure : bool = True ,
195+ ) -> "doctest.DocTestRunner" :
181196 # We need this in order to do a lazy import on doctest
182197 global RUNNER_CLASS
183198 if RUNNER_CLASS is None :
184199 RUNNER_CLASS = _init_runner_class ()
185- return RUNNER_CLASS (
200+ # Type ignored because the continue_on_failure argument is only defined on
201+ # PytestDoctestRunner, which is lazily defined so can't be used as a type.
202+ return RUNNER_CLASS ( # type: ignore
186203 checker = checker ,
187204 verbose = verbose ,
188205 optionflags = optionflags ,
@@ -211,7 +228,7 @@ def setup(self):
211228 def runtest (self ):
212229 _check_all_skipped (self .dtest )
213230 self ._disable_output_capturing_for_darwin ()
214- failures = []
231+ failures = [] # type: List[doctest.DocTestFailure]
215232 self .runner .run (self .dtest , out = failures )
216233 if failures :
217234 raise MultipleDoctestFailures (failures )
@@ -232,7 +249,9 @@ def _disable_output_capturing_for_darwin(self):
232249 def repr_failure (self , excinfo ):
233250 import doctest
234251
235- failures = None
252+ failures = (
253+ None
254+ ) # type: Optional[List[Union[doctest.DocTestFailure, doctest.UnexpectedException]]]
236255 if excinfo .errisinstance ((doctest .DocTestFailure , doctest .UnexpectedException )):
237256 failures = [excinfo .value ]
238257 elif excinfo .errisinstance (MultipleDoctestFailures ):
@@ -255,8 +274,10 @@ def repr_failure(self, excinfo):
255274 self .config .getoption ("doctestreport" )
256275 )
257276 if lineno is not None :
277+ assert failure .test .docstring is not None
258278 lines = failure .test .docstring .splitlines (False )
259279 # add line numbers to the left of the error message
280+ assert test .lineno is not None
260281 lines = [
261282 "%03d %s" % (i + test .lineno + 1 , x )
262283 for (i , x ) in enumerate (lines )
@@ -288,7 +309,7 @@ def reportinfo(self):
288309 return self .fspath , self .dtest .lineno , "[doctest] %s" % self .name
289310
290311
291- def _get_flag_lookup ():
312+ def _get_flag_lookup () -> Dict [ str , int ] :
292313 import doctest
293314
294315 return dict (
@@ -340,7 +361,7 @@ def collect(self):
340361 optionflags = get_optionflags (self )
341362
342363 runner = _get_runner (
343- verbose = 0 ,
364+ verbose = False ,
344365 optionflags = optionflags ,
345366 checker = _get_checker (),
346367 continue_on_failure = _get_continue_on_failure (self .config ),
@@ -419,7 +440,8 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
419440 return
420441 with _patch_unwrap_mock_aware ():
421442
422- doctest .DocTestFinder ._find (
443+ # Type ignored because this is a private function.
444+ doctest .DocTestFinder ._find ( # type: ignore
423445 self , tests , obj , name , module , source_lines , globs , seen
424446 )
425447
@@ -437,7 +459,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
437459 finder = MockAwareDocTestFinder ()
438460 optionflags = get_optionflags (self )
439461 runner = _get_runner (
440- verbose = 0 ,
462+ verbose = False ,
441463 optionflags = optionflags ,
442464 checker = _get_checker (),
443465 continue_on_failure = _get_continue_on_failure (self .config ),
@@ -466,24 +488,7 @@ def func():
466488 return fixture_request
467489
468490
469- def _get_checker ():
470- """
471- Returns a doctest.OutputChecker subclass that supports some
472- additional options:
473-
474- * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
475- prefixes (respectively) in string literals. Useful when the same
476- doctest should run in Python 2 and Python 3.
477-
478- * NUMBER to ignore floating-point differences smaller than the
479- precision of the literal number in the doctest.
480-
481- An inner class is used to avoid importing "doctest" at the module
482- level.
483- """
484- if hasattr (_get_checker , "LiteralsOutputChecker" ):
485- return _get_checker .LiteralsOutputChecker ()
486-
491+ def _init_checker_class () -> "Type[doctest.OutputChecker]" :
487492 import doctest
488493 import re
489494
@@ -573,11 +578,31 @@ def _remove_unwanted_precision(self, want, got):
573578 offset += w .end () - w .start () - (g .end () - g .start ())
574579 return got
575580
576- _get_checker .LiteralsOutputChecker = LiteralsOutputChecker
577- return _get_checker .LiteralsOutputChecker ()
581+ return LiteralsOutputChecker
582+
583+
584+ def _get_checker () -> "doctest.OutputChecker" :
585+ """
586+ Returns a doctest.OutputChecker subclass that supports some
587+ additional options:
588+
589+ * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
590+ prefixes (respectively) in string literals. Useful when the same
591+ doctest should run in Python 2 and Python 3.
592+
593+ * NUMBER to ignore floating-point differences smaller than the
594+ precision of the literal number in the doctest.
595+
596+ An inner class is used to avoid importing "doctest" at the module
597+ level.
598+ """
599+ global CHECKER_CLASS
600+ if CHECKER_CLASS is None :
601+ CHECKER_CLASS = _init_checker_class ()
602+ return CHECKER_CLASS ()
578603
579604
580- def _get_allow_unicode_flag ():
605+ def _get_allow_unicode_flag () -> int :
581606 """
582607 Registers and returns the ALLOW_UNICODE flag.
583608 """
@@ -586,7 +611,7 @@ def _get_allow_unicode_flag():
586611 return doctest .register_optionflag ("ALLOW_UNICODE" )
587612
588613
589- def _get_allow_bytes_flag ():
614+ def _get_allow_bytes_flag () -> int :
590615 """
591616 Registers and returns the ALLOW_BYTES flag.
592617 """
@@ -595,7 +620,7 @@ def _get_allow_bytes_flag():
595620 return doctest .register_optionflag ("ALLOW_BYTES" )
596621
597622
598- def _get_number_flag ():
623+ def _get_number_flag () -> int :
599624 """
600625 Registers and returns the NUMBER flag.
601626 """
@@ -604,7 +629,7 @@ def _get_number_flag():
604629 return doctest .register_optionflag ("NUMBER" )
605630
606631
607- def _get_report_choice (key ) :
632+ def _get_report_choice (key : str ) -> int :
608633 """
609634 This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
610635 importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
0 commit comments