3
3
4
4
"""Tests for coverage.py's code parsing."""
5
5
6
+ from __future__ import annotations
7
+
6
8
import ast
7
9
import os .path
8
10
import textwrap
9
11
import warnings
10
12
13
+ from typing import List
14
+
11
15
import pytest
12
16
13
17
from coverage import env
@@ -23,14 +27,14 @@ class PythonParserTest(CoverageTest):
23
27
24
28
run_in_temp_dir = False
25
29
26
- def parse_source (self , text ) :
30
+ def parse_source (self , text : str ) -> PythonParser :
27
31
"""Parse `text` as source, and return the `PythonParser` used."""
28
32
text = textwrap .dedent (text )
29
33
parser = PythonParser (text = text , exclude = "nocover" )
30
34
parser .parse_source ()
31
35
return parser
32
36
33
- def test_exit_counts (self ):
37
+ def test_exit_counts (self ) -> None :
34
38
parser = self .parse_source ("""\
35
39
# check some basic branch counting
36
40
class Foo:
@@ -47,7 +51,7 @@ class Bar:
47
51
2 :1 , 3 :1 , 4 :2 , 5 :1 , 7 :1 , 9 :1 , 10 :1
48
52
}
49
53
50
- def test_generator_exit_counts (self ):
54
+ def test_generator_exit_counts (self ) -> None :
51
55
# https://github.com/nedbat/coveragepy/issues/324
52
56
parser = self .parse_source ("""\
53
57
def gen(input):
@@ -63,7 +67,7 @@ def gen(input):
63
67
5 :1 , # list -> exit
64
68
}
65
69
66
- def test_try_except (self ):
70
+ def test_try_except (self ) -> None :
67
71
parser = self .parse_source ("""\
68
72
try:
69
73
a = 2
@@ -79,7 +83,7 @@ def test_try_except(self):
79
83
1 : 1 , 2 :1 , 3 :2 , 4 :1 , 5 :2 , 6 :1 , 7 :1 , 8 :1 , 9 :1
80
84
}
81
85
82
- def test_excluded_classes (self ):
86
+ def test_excluded_classes (self ) -> None :
83
87
parser = self .parse_source ("""\
84
88
class Foo:
85
89
def __init__(self):
@@ -93,7 +97,7 @@ class Bar:
93
97
1 :0 , 2 :1 , 3 :1
94
98
}
95
99
96
- def test_missing_branch_to_excluded_code (self ):
100
+ def test_missing_branch_to_excluded_code (self ) -> None :
97
101
parser = self .parse_source ("""\
98
102
if fooey:
99
103
a = 2
@@ -121,7 +125,7 @@ def foo():
121
125
""" )
122
126
assert parser .exit_counts () == { 1 :1 , 2 :1 , 3 :1 , 6 :1 }
123
127
124
- def test_indentation_error (self ):
128
+ def test_indentation_error (self ) -> None :
125
129
msg = (
126
130
"Couldn't parse '<code>' as Python source: " +
127
131
"'unindent does not match any outer indentation level' at line 3"
@@ -133,15 +137,15 @@ def test_indentation_error(self):
133
137
1
134
138
""" )
135
139
136
- def test_token_error (self ):
140
+ def test_token_error (self ) -> None :
137
141
msg = "Couldn't parse '<code>' as Python source: 'EOF in multi-line string' at line 1"
138
142
with pytest .raises (NotPython , match = msg ):
139
143
_ = self .parse_source ("""\
140
144
'''
141
145
""" )
142
146
143
147
@xfail_pypy38
144
- def test_decorator_pragmas (self ):
148
+ def test_decorator_pragmas (self ) -> None :
145
149
parser = self .parse_source ("""\
146
150
# 1
147
151
@@ -177,7 +181,7 @@ def func(x=25):
177
181
assert parser .statements == {8 }
178
182
179
183
@xfail_pypy38
180
- def test_decorator_pragmas_with_colons (self ):
184
+ def test_decorator_pragmas_with_colons (self ) -> None :
181
185
# A colon in a decorator expression would confuse the parser,
182
186
# ending the exclusion of the decorated function.
183
187
parser = self .parse_source ("""\
@@ -197,7 +201,7 @@ def g():
197
201
assert parser .raw_statements == raw_statements
198
202
assert parser .statements == set ()
199
203
200
- def test_class_decorator_pragmas (self ):
204
+ def test_class_decorator_pragmas (self ) -> None :
201
205
parser = self .parse_source ("""\
202
206
class Foo(object):
203
207
def __init__(self):
@@ -211,7 +215,7 @@ def __init__(self):
211
215
assert parser .raw_statements == {1 , 2 , 3 , 5 , 6 , 7 , 8 }
212
216
assert parser .statements == {1 , 2 , 3 }
213
217
214
- def test_empty_decorated_function (self ):
218
+ def test_empty_decorated_function (self ) -> None :
215
219
parser = self .parse_source ("""\
216
220
def decorator(func):
217
221
return func
@@ -247,7 +251,7 @@ def bar(self):
247
251
assert expected_arcs == parser .arcs ()
248
252
assert expected_exits == parser .exit_counts ()
249
253
250
- def test_fuzzed_double_parse (self ):
254
+ def test_fuzzed_double_parse (self ) -> None :
251
255
# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
252
256
# The second parse used to raise `TypeError: 'NoneType' object is not iterable`
253
257
msg = "EOF in multi-line statement"
@@ -262,13 +266,13 @@ class ParserMissingArcDescriptionTest(CoverageTest):
262
266
263
267
run_in_temp_dir = False
264
268
265
- def parse_text (self , source ) :
269
+ def parse_text (self , source : str ) -> PythonParser :
266
270
"""Parse Python source, and return the parser object."""
267
271
parser = PythonParser (text = textwrap .dedent (source ))
268
272
parser .parse_source ()
269
273
return parser
270
274
271
- def test_missing_arc_description (self ):
275
+ def test_missing_arc_description (self ) -> None :
272
276
# This code is never run, so the actual values don't matter.
273
277
parser = self .parse_text ("""\
274
278
if x:
@@ -304,7 +308,7 @@ def func10():
304
308
)
305
309
assert expected == parser .missing_arc_description (11 , 13 )
306
310
307
- def test_missing_arc_descriptions_for_small_callables (self ):
311
+ def test_missing_arc_descriptions_for_small_callables (self ) -> None :
308
312
parser = self .parse_text ("""\
309
313
callables = [
310
314
lambda: 2,
@@ -323,7 +327,7 @@ def test_missing_arc_descriptions_for_small_callables(self):
323
327
expected = "line 5 didn't finish the set comprehension on line 5"
324
328
assert expected == parser .missing_arc_description (5 , - 5 )
325
329
326
- def test_missing_arc_descriptions_for_exceptions (self ):
330
+ def test_missing_arc_descriptions_for_exceptions (self ) -> None :
327
331
parser = self .parse_text ("""\
328
332
try:
329
333
pass
@@ -343,7 +347,7 @@ def test_missing_arc_descriptions_for_exceptions(self):
343
347
)
344
348
assert expected == parser .missing_arc_description (5 , 6 )
345
349
346
- def test_missing_arc_descriptions_for_finally (self ):
350
+ def test_missing_arc_descriptions_for_finally (self ) -> None :
347
351
parser = self .parse_text ("""\
348
352
def function():
349
353
for i in range(2):
@@ -417,7 +421,7 @@ def function():
417
421
)
418
422
assert expected == parser .missing_arc_description (18 , - 1 )
419
423
420
- def test_missing_arc_descriptions_bug460 (self ):
424
+ def test_missing_arc_descriptions_bug460 (self ) -> None :
421
425
parser = self .parse_text ("""\
422
426
x = 1
423
427
d = {
@@ -429,7 +433,7 @@ def test_missing_arc_descriptions_bug460(self):
429
433
assert parser .missing_arc_description (2 , - 3 ) == "line 3 didn't finish the lambda on line 3"
430
434
431
435
@pytest .mark .skipif (not env .PYBEHAVIOR .match_case , reason = "Match-case is new in 3.10" )
432
- def test_match_case_with_default (self ):
436
+ def test_match_case_with_default (self ) -> None :
433
437
parser = self .parse_text ("""\
434
438
for command in ["huh", "go home", "go n"]:
435
439
match command.split():
@@ -450,7 +454,7 @@ def test_match_case_with_default(self):
450
454
class ParserFileTest (CoverageTest ):
451
455
"""Tests for coverage.py's code parsing from files."""
452
456
453
- def parse_file (self , filename ) :
457
+ def parse_file (self , filename : str ) -> PythonParser :
454
458
"""Parse `text` as source, and return the `PythonParser` used."""
455
459
parser = PythonParser (filename = filename , exclude = "nocover" )
456
460
parser .parse_source ()
@@ -459,7 +463,7 @@ def parse_file(self, filename):
459
463
@pytest .mark .parametrize ("slug, newline" , [
460
464
("unix" , "\n " ), ("dos" , "\r \n " ), ("mac" , "\r " ),
461
465
])
462
- def test_line_endings (self , slug , newline ) :
466
+ def test_line_endings (self , slug : str , newline : str ) -> None :
463
467
text = """\
464
468
# check some basic branch counting
465
469
class Foo:
@@ -478,14 +482,14 @@ class Bar:
478
482
parser = self .parse_file (fname )
479
483
assert parser .exit_counts () == counts , f"Wrong for { fname !r} "
480
484
481
- def test_encoding (self ):
485
+ def test_encoding (self ) -> None :
482
486
self .make_file ("encoded.py" , """\
483
487
coverage = "\xe7 \xf6 v\xea r\xe3 g\xe9 "
484
488
""" )
485
489
parser = self .parse_file ("encoded.py" )
486
490
assert parser .exit_counts () == {1 : 1 }
487
491
488
- def test_missing_line_ending (self ):
492
+ def test_missing_line_ending (self ) -> None :
489
493
# Test that the set of statements is the same even if a final
490
494
# multi-line statement has no final newline.
491
495
# https://github.com/nedbat/coveragepy/issues/293
@@ -514,7 +518,7 @@ def test_missing_line_ending(self):
514
518
assert parser .statements == {1 }
515
519
516
520
517
- def test_ast_dump ():
521
+ def test_ast_dump () -> None :
518
522
# Run the AST_DUMP code to make sure it doesn't fail, with some light
519
523
# assertions. Use parser.py as the test code since it is the longest file,
520
524
# and fitting, since it's the AST_DUMP code.
@@ -531,7 +535,7 @@ def test_ast_dump():
531
535
# stress_phystoken.tok has deprecation warnings, suppress them.
532
536
warnings .filterwarnings ("ignore" , message = r".*invalid escape sequence" ,)
533
537
ast_root = ast .parse (source )
534
- result = []
538
+ result : List [ str ] = []
535
539
ast_dump (ast_root , print = result .append )
536
540
if num_lines < 100 :
537
541
continue
0 commit comments