6
6
import traceback
7
7
import warnings
8
8
from contextlib import contextmanager
9
+ from typing import Dict
10
+ from typing import List
11
+ from typing import Optional
9
12
from typing import Sequence
10
13
from typing import Tuple
14
+ from typing import Union
11
15
12
16
import pytest
13
17
from _pytest import outcomes
20
24
from _pytest .python_api import approx
21
25
from _pytest .warning_types import PytestWarning
22
26
27
+ if False : # TYPE_CHECKING
28
+ import doctest
29
+ from typing import Type
30
+
23
31
DOCTEST_REPORT_CHOICE_NONE = "none"
24
32
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
25
33
DOCTEST_REPORT_CHOICE_NDIFF = "ndiff"
36
44
37
45
# Lazy definition of runner class
38
46
RUNNER_CLASS = None
47
+ # Lazy definition of output checker class
48
+ CHECKER_CLASS = None # type: Optional[Type[doctest.OutputChecker]]
39
49
40
50
41
51
def pytest_addoption (parser ):
@@ -139,7 +149,7 @@ def __init__(self, failures):
139
149
self .failures = failures
140
150
141
151
142
- def _init_runner_class ():
152
+ def _init_runner_class () -> "Type[doctest.DocTestRunner]" :
143
153
import doctest
144
154
145
155
class PytestDoctestRunner (doctest .DebugRunner ):
@@ -177,12 +187,19 @@ def report_unexpected_exception(self, out, test, example, exc_info):
177
187
return PytestDoctestRunner
178
188
179
189
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" :
181
196
# We need this in order to do a lazy import on doctest
182
197
global RUNNER_CLASS
183
198
if RUNNER_CLASS is None :
184
199
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
186
203
checker = checker ,
187
204
verbose = verbose ,
188
205
optionflags = optionflags ,
@@ -211,7 +228,7 @@ def setup(self):
211
228
def runtest (self ):
212
229
_check_all_skipped (self .dtest )
213
230
self ._disable_output_capturing_for_darwin ()
214
- failures = []
231
+ failures = [] # type: List[doctest.DocTestFailure]
215
232
self .runner .run (self .dtest , out = failures )
216
233
if failures :
217
234
raise MultipleDoctestFailures (failures )
@@ -232,7 +249,9 @@ def _disable_output_capturing_for_darwin(self):
232
249
def repr_failure (self , excinfo ):
233
250
import doctest
234
251
235
- failures = None
252
+ failures = (
253
+ None
254
+ ) # type: Optional[List[Union[doctest.DocTestFailure, doctest.UnexpectedException]]]
236
255
if excinfo .errisinstance ((doctest .DocTestFailure , doctest .UnexpectedException )):
237
256
failures = [excinfo .value ]
238
257
elif excinfo .errisinstance (MultipleDoctestFailures ):
@@ -255,8 +274,10 @@ def repr_failure(self, excinfo):
255
274
self .config .getoption ("doctestreport" )
256
275
)
257
276
if lineno is not None :
277
+ assert failure .test .docstring is not None
258
278
lines = failure .test .docstring .splitlines (False )
259
279
# add line numbers to the left of the error message
280
+ assert test .lineno is not None
260
281
lines = [
261
282
"%03d %s" % (i + test .lineno + 1 , x )
262
283
for (i , x ) in enumerate (lines )
@@ -288,7 +309,7 @@ def reportinfo(self):
288
309
return self .fspath , self .dtest .lineno , "[doctest] %s" % self .name
289
310
290
311
291
- def _get_flag_lookup ():
312
+ def _get_flag_lookup () -> Dict [ str , int ] :
292
313
import doctest
293
314
294
315
return dict (
@@ -340,7 +361,7 @@ def collect(self):
340
361
optionflags = get_optionflags (self )
341
362
342
363
runner = _get_runner (
343
- verbose = 0 ,
364
+ verbose = False ,
344
365
optionflags = optionflags ,
345
366
checker = _get_checker (),
346
367
continue_on_failure = _get_continue_on_failure (self .config ),
@@ -419,7 +440,8 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
419
440
return
420
441
with _patch_unwrap_mock_aware ():
421
442
422
- doctest .DocTestFinder ._find (
443
+ # Type ignored because this is a private function.
444
+ doctest .DocTestFinder ._find ( # type: ignore
423
445
self , tests , obj , name , module , source_lines , globs , seen
424
446
)
425
447
@@ -437,7 +459,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
437
459
finder = MockAwareDocTestFinder ()
438
460
optionflags = get_optionflags (self )
439
461
runner = _get_runner (
440
- verbose = 0 ,
462
+ verbose = False ,
441
463
optionflags = optionflags ,
442
464
checker = _get_checker (),
443
465
continue_on_failure = _get_continue_on_failure (self .config ),
@@ -466,24 +488,7 @@ def func():
466
488
return fixture_request
467
489
468
490
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]" :
487
492
import doctest
488
493
import re
489
494
@@ -573,11 +578,31 @@ def _remove_unwanted_precision(self, want, got):
573
578
offset += w .end () - w .start () - (g .end () - g .start ())
574
579
return got
575
580
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 ()
578
603
579
604
580
- def _get_allow_unicode_flag ():
605
+ def _get_allow_unicode_flag () -> int :
581
606
"""
582
607
Registers and returns the ALLOW_UNICODE flag.
583
608
"""
@@ -586,7 +611,7 @@ def _get_allow_unicode_flag():
586
611
return doctest .register_optionflag ("ALLOW_UNICODE" )
587
612
588
613
589
- def _get_allow_bytes_flag ():
614
+ def _get_allow_bytes_flag () -> int :
590
615
"""
591
616
Registers and returns the ALLOW_BYTES flag.
592
617
"""
@@ -595,7 +620,7 @@ def _get_allow_bytes_flag():
595
620
return doctest .register_optionflag ("ALLOW_BYTES" )
596
621
597
622
598
- def _get_number_flag ():
623
+ def _get_number_flag () -> int :
599
624
"""
600
625
Registers and returns the NUMBER flag.
601
626
"""
@@ -604,7 +629,7 @@ def _get_number_flag():
604
629
return doctest .register_optionflag ("NUMBER" )
605
630
606
631
607
- def _get_report_choice (key ) :
632
+ def _get_report_choice (key : str ) -> int :
608
633
"""
609
634
This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
610
635
importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
0 commit comments