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,14 +361,16 @@ 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 ),
347368 )
348369
349370 parser = doctest .DocTestParser ()
350- test = parser .get_doctest (text , globs , name , filename , 0 )
371+ # Remove ignore once this reaches mypy:
372+ # https://github.com/python/typeshed/commit/3e4a251b2b6da6bb43137acf5abf81ecfa7ba8ee
373+ test = parser .get_doctest (text , globs , name , filename , 0 ) # type: ignore
351374 if test .examples :
352375 yield DoctestItem (test .name , self , runner , test )
353376
@@ -419,7 +442,8 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
419442 return
420443 with _patch_unwrap_mock_aware ():
421444
422- doctest .DocTestFinder ._find (
445+ # Type ignored because this is a private function.
446+ doctest .DocTestFinder ._find ( # type: ignore
423447 self , tests , obj , name , module , source_lines , globs , seen
424448 )
425449
@@ -437,7 +461,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
437461 finder = MockAwareDocTestFinder ()
438462 optionflags = get_optionflags (self )
439463 runner = _get_runner (
440- verbose = 0 ,
464+ verbose = False ,
441465 optionflags = optionflags ,
442466 checker = _get_checker (),
443467 continue_on_failure = _get_continue_on_failure (self .config ),
@@ -466,24 +490,7 @@ def func():
466490 return fixture_request
467491
468492
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-
493+ def _init_checker_class () -> "Type[doctest.OutputChecker]" :
487494 import doctest
488495 import re
489496
@@ -573,11 +580,31 @@ def _remove_unwanted_precision(self, want, got):
573580 offset += w .end () - w .start () - (g .end () - g .start ())
574581 return got
575582
576- _get_checker .LiteralsOutputChecker = LiteralsOutputChecker
577- return _get_checker .LiteralsOutputChecker ()
583+ return LiteralsOutputChecker
584+
585+
586+ def _get_checker () -> "doctest.OutputChecker" :
587+ """
588+ Returns a doctest.OutputChecker subclass that supports some
589+ additional options:
590+
591+ * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b''
592+ prefixes (respectively) in string literals. Useful when the same
593+ doctest should run in Python 2 and Python 3.
594+
595+ * NUMBER to ignore floating-point differences smaller than the
596+ precision of the literal number in the doctest.
597+
598+ An inner class is used to avoid importing "doctest" at the module
599+ level.
600+ """
601+ global CHECKER_CLASS
602+ if CHECKER_CLASS is None :
603+ CHECKER_CLASS = _init_checker_class ()
604+ return CHECKER_CLASS ()
578605
579606
580- def _get_allow_unicode_flag ():
607+ def _get_allow_unicode_flag () -> int :
581608 """
582609 Registers and returns the ALLOW_UNICODE flag.
583610 """
@@ -586,7 +613,7 @@ def _get_allow_unicode_flag():
586613 return doctest .register_optionflag ("ALLOW_UNICODE" )
587614
588615
589- def _get_allow_bytes_flag ():
616+ def _get_allow_bytes_flag () -> int :
590617 """
591618 Registers and returns the ALLOW_BYTES flag.
592619 """
@@ -595,7 +622,7 @@ def _get_allow_bytes_flag():
595622 return doctest .register_optionflag ("ALLOW_BYTES" )
596623
597624
598- def _get_number_flag ():
625+ def _get_number_flag () -> int :
599626 """
600627 Registers and returns the NUMBER flag.
601628 """
@@ -604,7 +631,7 @@ def _get_number_flag():
604631 return doctest .register_optionflag ("NUMBER" )
605632
606633
607- def _get_report_choice (key ) :
634+ def _get_report_choice (key : str ) -> int :
608635 """
609636 This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
610637 importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
0 commit comments