Coverage for tests / test_parser.py: 100.000%

337 statements  

« prev     ^ index     » next       coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000

1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt 

3 

4"""Tests for coverage.py's code parsing.""" 

5 

6from __future__ import annotations 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

7 

8import ast 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

9import re 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

10import textwrap 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

11from unittest import mock 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

12 

13import pytest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

14 

15from coverage import env 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

16from coverage.exceptions import NoSource, NotPython 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

17from coverage.parser import PythonParser, is_constant_test_expr 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

18 

19from tests.coveragetest import CoverageTest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

20from tests.helpers import arcz_to_arcs 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

21 

22 

23class PythonParserTestBase(CoverageTest): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

24 """Tests for coverage.py's Python code parsing.""" 

25 

26 run_in_temp_dir = False 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

27 

28 def parse_text(self, text: str, exclude: str = "nocover") -> PythonParser: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

29 """Parse `text` as source, and return the `PythonParser` used.""" 

30 text = textwrap.dedent(text) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

31 parser = PythonParser(text=text, exclude=exclude) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

32 parser.parse_source() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

33 return parser 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

34 

35 

36class PythonParserTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

37 """Tests of coverage.parser.""" 

38 

39 def test_exit_counts(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

40 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

41 # check some basic branch counting 

42 class Foo: 

43 def foo(self, a): 

44 if a: 

45 return 5 

46 else: 

47 return 7 

48 

49 class Bar: 

50 pass 

51 """) 

52 assert parser.exit_counts() == { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

53 2: 1, 

54 3: 1, 

55 4: 2, 

56 5: 1, 

57 7: 1, 

58 9: 1, 

59 10: 1, 

60 } 

61 

62 def test_try_except(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

63 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

64 try: 

65 a = 2 

66 except ValueError: 

67 a = 4 

68 except ZeroDivideError: 

69 a = 6 

70 except: 

71 a = 8 

72 b = 9 

73 """) 

74 assert parser.exit_counts() == { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

75 1: 1, 

76 2: 1, 

77 3: 1, 

78 4: 1, 

79 5: 1, 

80 6: 1, 

81 7: 1, 

82 8: 1, 

83 9: 1, 

84 } 

85 

86 def test_excluded_classes(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

87 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

88 class Foo: 

89 def __init__(self): 

90 pass 

91 

92 if len([]): # nocover 

93 class Bar: 

94 pass 

95 """) 

96 assert parser.exit_counts() == {2: 1, 3: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

97 

98 def test_missing_branch_to_excluded_code(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

99 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

100 if fooey: 

101 a = 2 

102 else: # nocover 

103 a = 4 

104 b = 5 

105 """) 

106 assert parser.exit_counts() == {1: 1, 2: 1, 5: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

107 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

108 def foo(): 

109 if fooey: 

110 a = 3 

111 else: 

112 a = 5 

113 b = 6 

114 """) 

115 assert parser.exit_counts() == {1: 1, 2: 2, 3: 1, 5: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

116 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

117 def foo(): 

118 if fooey: 

119 a = 3 

120 else: # nocover 

121 a = 5 

122 b = 6 

123 """) 

124 assert parser.exit_counts() == {1: 1, 2: 1, 3: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

125 

126 @pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

127 "text", 

128 [ 

129 pytest.param("0 spaces\n 2\n 1", id="bad_indent"), 

130 pytest.param("'''", id="string_eof"), 

131 pytest.param("$hello", id="dollar"), 

132 # on 3.10 this passes ast.parse but fails on tokenize.generate_tokens 

133 pytest.param( 

134 "\r'\\\n'''", 

135 id="leading_newline_eof", 

136 marks=[ 

137 pytest.mark.skipif(env.PYVERSION >= (3, 12), reason="parses fine in 3.12"), 

138 ], 

139 ), 

140 ], 

141 ) 

142 def test_not_python(self, text: str) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

143 msg = r"Couldn't parse '<code>' as Python source: ['\"].*['\"] at line \d+" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

144 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

145 _ = self.parse_text(text) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

146 

147 def test_empty_decorated_function(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

148 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

149 def decorator(func): 

150 return func 

151 

152 @decorator 

153 def foo(self): 

154 '''Docstring''' 

155 

156 @decorator 

157 def bar(self): 

158 pass 

159 """) 

160 

161 assert parser.statements == {1, 2, 4, 5, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

162 assert parser.arcs() == set(arcz_to_arcs("14 45 58 89 9. 2. A-8")) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

163 assert parser.exit_counts() == {1: 1, 2: 1, 4: 1, 5: 1, 8: 1, 9: 1, 10: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

164 

165 def test_nested_context_managers(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

166 # https://github.com/coveragepy/coveragepy/issues/1876 

167 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

168 a = 1 

169 with suppress(ValueError): 

170 with suppress(ValueError): 

171 x = 4 

172 with suppress(ValueError): 

173 x = 6 

174 with suppress(ValueError): 

175 x = 8 

176 a = 9 

177 """) 

178 

179 one_nine = set(range(1, 10)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

180 assert parser.statements == one_nine 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

181 assert parser.exit_counts() == dict.fromkeys(one_nine, 1) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

182 

183 def test_module_docstrings(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

184 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

185 '''The docstring on line 1''' 

186 a = 2 

187 """) 

188 assert {2} == parser.statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

189 

190 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

191 # Docstring is not line 1 

192 '''The docstring on line 2''' 

193 a = 3 

194 """) 

195 assert {3} == parser.statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

196 

197 def test_fuzzed_double_parse(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

198 # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381 

199 # The second parse used to raise `TypeError: 'NoneType' object is not iterable` 

200 msg = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

201 r"(EOF in multi-line statement)" # before 3.12.0b1 

202 + r"|(unmatched ']')" # after 3.12.0b1 

203 ) 

204 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

205 self.parse_text("]") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

206 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

207 self.parse_text("]") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

208 

209 def test_bug_1891(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

210 # These examples exercise code paths I thought were impossible. 

211 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

212 res = siblings( 

213 'configure', 

214 **ca, 

215 ) 

216 """) 

217 assert parser.exit_counts() == {1: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

218 # In conftest.py, we silence the SyntaxWarning this code causes. If 

219 # we remove this code, we can probably remove that warning. 

220 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

221 def g2(): 

222 try: 

223 return 2 

224 finally: 

225 return 3 

226 """) 

227 assert parser.exit_counts() == {1: 1, 2: 1, 3: 1, 5: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

228 

229 def test_fuzzed_weirdness(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

230 # This used to cause a `min of empty sequence` error: 

231 self.parse_text("\r\\\n\n\t\t\\\n\n") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

232 

233 

234class ExclusionParserTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

235 """Tests for the exclusion code in PythonParser.""" 

236 

237 def test_simple(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

238 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

239 """\ 

240 a = 1; b = 2 

241 

242 if len([]): 

243 a = 4 # nocover 

244 """, 

245 ) 

246 assert parser.statements == {1, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

247 

248 def test_excluding_if_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

249 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

250 """\ 

251 a = 1; b = 2 

252 

253 if len([]): # nocover 

254 a = 4 

255 b = 5 

256 c = 6 

257 assert a == 1 and b == 2 

258 """, 

259 ) 

260 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

261 

262 def test_excluding_if_but_not_else_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

263 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

264 """\ 

265 a = 1; b = 2 

266 

267 if len([]): # nocover 

268 a = 4 

269 b = 5 

270 c = 6 

271 else: 

272 a = 8 

273 b = 9 

274 assert a == 8 and b == 9 

275 """, 

276 ) 

277 assert parser.statements == {1, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

278 

279 def test_excluding_else_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

280 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

281 """\ 

282 a = 1; b = 2 

283 

284 if 1==1: 

285 a = 4 

286 b = 5 

287 c = 6 

288 else: # nocover 

289 a = 8 

290 b = 9 

291 assert a == 4 and b == 5 and c == 6 

292 """, 

293 ) 

294 assert parser.statements == {1, 3, 4, 5, 6, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

295 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

296 """\ 

297 a = 1; b = 2 

298 

299 if 1==1: 

300 a = 4 

301 b = 5 

302 c = 6 

303 

304 # Lots of comments to confuse the else handler. 

305 # more. 

306 

307 else: # nocover 

308 

309 # Comments here too. 

310 

311 a = 8 

312 b = 9 

313 assert a == 4 and b == 5 and c == 6 

314 """, 

315 ) 

316 assert parser.statements == {1, 3, 4, 5, 6, 17} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

317 

318 def test_excluding_oneline_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

319 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

320 """\ 

321 def foo(): 

322 a = 2 

323 if len([]): x = 3 # nocover 

324 b = 4 

325 

326 foo() 

327 """, 

328 ) 

329 assert parser.statements == {1, 2, 4, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

330 

331 def test_excluding_a_colon_not_a_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

332 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

333 """\ 

334 def foo(): 

335 l = list(range(10)) 

336 a = l[:3] # nocover 

337 b = 4 

338 

339 foo() 

340 """, 

341 ) 

342 assert parser.statements == {1, 2, 4, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

343 

344 def test_excluding_for_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

345 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

346 """\ 

347 a = 0 

348 for i in [1,2,3,4,5]: # nocover 

349 a += i 

350 assert a == 15 

351 """, 

352 ) 

353 assert parser.statements == {1, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

354 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

355 """\ 

356 a = 0 

357 for i in [1, 

358 2,3,4, 

359 5]: # nocover 

360 a += i 

361 assert a == 15 

362 """, 

363 ) 

364 assert parser.statements == {1, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

365 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

366 """\ 

367 a = 0 

368 for i in [1,2,3,4,5 

369 ]: # nocover 

370 a += i 

371 break 

372 a = 99 

373 assert a == 1 

374 """, 

375 ) 

376 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

377 

378 def test_excluding_for_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

379 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

380 """\ 

381 a = 0 

382 for i in range(5): 

383 a += i+1 

384 break 

385 else: # nocover 

386 a = 123 

387 assert a == 1 

388 """, 

389 ) 

390 assert parser.statements == {1, 2, 3, 4, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

391 

392 def test_excluding_while(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

393 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

394 """\ 

395 a = 3; b = 0 

396 while a*b: # nocover 

397 b += 1 

398 break 

399 assert a == 3 and b == 0 

400 """, 

401 ) 

402 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

403 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

404 """\ 

405 a = 3; b = 0 

406 while ( 

407 a*b 

408 ): # nocover 

409 b += 1 

410 break 

411 assert a == 3 and b == 0 

412 """, 

413 ) 

414 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

415 

416 def test_excluding_while_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

417 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

418 """\ 

419 a = 3; b = 0 

420 while a: 

421 b += 1 

422 break 

423 else: # nocover 

424 b = 123 

425 assert a == 3 and b == 1 

426 """, 

427 ) 

428 assert parser.statements == {1, 2, 3, 4, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

429 

430 def test_excluding_try_except(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

431 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

432 """\ 

433 a = 0 

434 try: 

435 a = 1 

436 except: # nocover 

437 a = 99 

438 assert a == 1 

439 """, 

440 ) 

441 assert parser.statements == {1, 2, 3, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

442 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

443 """\ 

444 a = 0 

445 try: 

446 a = 1 

447 raise Exception("foo") 

448 except: 

449 a = 99 

450 assert a == 99 

451 """, 

452 ) 

453 assert parser.statements == {1, 2, 3, 4, 5, 6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

454 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

455 """\ 

456 a = 0 

457 try: 

458 a = 1 

459 raise Exception("foo") 

460 except ImportError: # nocover 

461 a = 99 

462 except: 

463 a = 123 

464 assert a == 123 

465 """, 

466 ) 

467 assert parser.statements == {1, 2, 3, 4, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

468 

469 def test_excluding_if_pass(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

470 # From a comment on the coverage.py page by Michael McNeil Forbes: 

471 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

472 """\ 

473 def f(): 

474 if False: # pragma: nocover 

475 pass # This line still reported as missing 

476 if False: # pragma: nocover 

477 x = 1 # Now it is skipped. 

478 

479 f() 

480 """, 

481 ) 

482 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

483 

484 def test_multiline_if_no_branch(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

485 # From https://github.com/coveragepy/coveragepy/issues/754 

486 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

487 """\ 

488 if (this_is_a_verylong_boolean_expression == True # pragma: no branch 

489 and another_long_expression and here_another_expression): 

490 do_something() 

491 """, 

492 ) 

493 parser2 = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

494 """\ 

495 if this_is_a_verylong_boolean_expression == True and another_long_expression \\ 

496 and here_another_expression: # pragma: no branch 

497 do_something() 

498 """, 

499 ) 

500 assert parser.statements == parser2.statements == {1, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

501 pragma_re = ".*pragma: no branch.*" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

502 assert parser.lines_matching(pragma_re) == parser2.lines_matching(pragma_re) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

503 

504 def test_excluding_function(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

505 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

506 """\ 

507 def fn(foo): # nocover 

508 a = 1 

509 b = 2 

510 c = 3 

511 

512 x = 1 

513 assert x == 1 

514 """, 

515 ) 

516 assert parser.statements == {6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

517 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

518 """\ 

519 a = 0 

520 def very_long_function_to_exclude_name(very_long_argument1, 

521 very_long_argument2): 

522 pass 

523 assert a == 0 

524 """, 

525 exclude="function_to_exclude", 

526 ) 

527 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

528 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

529 """\ 

530 a = 0 

531 def very_long_function_to_exclude_name( 

532 very_long_argument1, 

533 very_long_argument2 

534 ): 

535 pass 

536 assert a == 0 

537 """, 

538 exclude="function_to_exclude", 

539 ) 

540 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

541 # linters and formatters can't agree about whether strings are too 

542 # long, so format in the long variable names that make them too long. 

543 long_arg = "super_long_input_argument" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

544 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

545 f"""\ 

546 def my_func( 

547 {long_arg}_0=0, 

548 {long_arg}_1=1, 

549 {long_arg}_2=2): 

550 pass 

551 

552 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

553 pass 

554 """, 

555 exclude="my_func", 

556 ) 

557 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

558 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

559 f"""\ 

560 def my_func( 

561 {long_arg}_0=0, 

562 {long_arg}_1=1, 

563 {long_arg}_2=2): 

564 pass 

565 

566 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

567 pass 

568 """, 

569 exclude="my_func_2", 

570 ) 

571 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

572 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

573 f"""\ 

574 def my_func ( 

575 {long_arg}_0=0, 

576 {long_arg}_1=1, 

577 {long_arg}_2=2): 

578 pass 

579 

580 def my_func_2 ({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

581 pass 

582 """, 

583 exclude="my_func_2", 

584 ) 

585 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

586 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

587 f"""\ 

588 def my_func ( 

589 {long_arg}_0=0, 

590 {long_arg}_1=1, 

591 {long_arg}_2=2): 

592 pass 

593 

594 def my_func_2 ({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

595 pass 

596 """, 

597 exclude="my_func", 

598 ) 

599 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

600 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

601 f"""\ 

602 def my_func \ 

603 ( 

604 {long_arg}_0=0, 

605 {long_arg}_1=1 

606 ): 

607 pass 

608 

609 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

610 pass 

611 """, 

612 exclude="my_func_2", 

613 ) 

614 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

615 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

616 f"""\ 

617 def my_func \ 

618 ( 

619 {long_arg}_0=0, 

620 {long_arg}_1=1 

621 ): 

622 pass 

623 

624 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2): 

625 pass 

626 """, 

627 exclude="my_func", 

628 ) 

629 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

630 

631 def test_excluding_bug1713(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

632 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

633 """\ 

634 print("1") 

635 

636 def hello_3(a): # pragma: nocover 

637 match a: 

638 case ("5" 

639 | "6"): 

640 print("7") 

641 case "8": 

642 print("9") 

643 

644 print("11") 

645 """, 

646 ) 

647 assert parser.statements == {1, 11} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

648 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

649 """\ 

650 print("1") 

651 

652 def hello_3(a): # nocover 

653 if ("4" or 

654 "5"): 

655 print("6") 

656 else: 

657 print("8") 

658 

659 print("10") 

660 """, 

661 ) 

662 assert parser.statements == {1, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

663 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

664 """\ 

665 print(1) 

666 

667 def func(a, b): 

668 if a == 4: # nocover 

669 func5() 

670 if b: 

671 print(7) 

672 func8() 

673 

674 print(10) 

675 """, 

676 ) 

677 assert parser.statements == {1, 3, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

678 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

679 """\ 

680 class Foo: # pragma: nocover 

681 def greet(self): 

682 print("hello world") 

683 """, 

684 ) 

685 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

686 

687 def test_excluding_method(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

688 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

689 """\ 

690 class Fooey: 

691 def __init__(self): 

692 self.a = 1 

693 

694 def foo(self): # nocover 

695 return self.a 

696 

697 x = Fooey() 

698 assert x.a == 1 

699 """, 

700 ) 

701 assert parser.statements == {1, 2, 3, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

702 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

703 """\ 

704 class Fooey: 

705 def __init__(self): 

706 self.a = 1 

707 

708 def very_long_method_to_exclude_name( 

709 very_long_argument1, 

710 very_long_argument2 

711 ): 

712 pass 

713 

714 x = Fooey() 

715 assert x.a == 1 

716 """, 

717 exclude="method_to_exclude", 

718 ) 

719 assert parser.statements == {1, 2, 3, 11, 12} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

720 

721 def test_excluding_class(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

722 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

723 """\ 

724 class Fooey: # nocover 

725 def __init__(self): 

726 self.a = 1 

727 

728 def foo(self): 

729 return self.a 

730 

731 x = 1 

732 assert x == 1 

733 """, 

734 ) 

735 assert parser.statements == {8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

736 

737 def test_excludes_non_ascii(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

738 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

739 """\ 

740 # coding: utf-8 

741 a = 1; b = 2 

742 

743 if len([]): 

744 a = 5 # ✘cover 

745 """, 

746 exclude="✘cover", 

747 ) 

748 assert parser.statements == {2, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

749 

750 def test_no_exclude_at_all(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

751 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

752 """\ 

753 def foo(): 

754 if fooey: 

755 a = 3 

756 else: 

757 a = 5 

758 b = 6 

759 """, 

760 exclude="", 

761 ) 

762 assert parser.exit_counts() == {1: 1, 2: 2, 3: 1, 5: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

763 

764 def test_formfeed(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

765 # https://github.com/coveragepy/coveragepy/issues/461 

766 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

767 """\ 

768 x = 1 

769 assert len([]) == 0, ( 

770 "This won't happen %s" % ("hello",) 

771 ) 

772 \f 

773 x = 6 

774 assert len([]) == 0, ( 

775 "This won't happen %s" % ("hello",) 

776 ) 

777 """, 

778 exclude="assert", 

779 ) 

780 assert parser.statements == {1, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

781 

782 def test_decorator_pragmas(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

783 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

784 # 1 

785 

786 @foo(3) # nocover 

787 @bar 

788 def func(x, y=5): 

789 return 6 

790 

791 class Foo: # this is the only statement. 

792 '''9''' 

793 @foo # nocover 

794 def __init__(self): 

795 '''12''' 

796 return 13 

797 

798 @foo( # nocover 

799 16, 

800 17, 

801 ) 

802 def meth(self): 

803 return 20 

804 

805 @foo( # nocover 

806 23 

807 ) 

808 def func(x=25): 

809 return 26 

810 """) 

811 raw_statements = {3, 4, 5, 6, 8, 9, 10, 11, 13, 15, 16, 17, 19, 20, 22, 23, 25, 26} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

812 assert parser.raw_statements == raw_statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

813 assert parser.statements == {8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

814 

815 def test_decorator_pragmas_with_colons(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

816 # A colon in a decorator expression would confuse the parser, 

817 # ending the exclusion of the decorated function. 

818 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

819 @decorate(X) # nocover 

820 @decorate("Hello"[2]) 

821 def f(): 

822 x = 4 

823 

824 @decorate(X) # nocover 

825 @decorate("Hello"[:7]) 

826 def g(): 

827 x = 9 

828 """) 

829 raw_statements = {1, 2, 3, 4, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

830 assert parser.raw_statements == raw_statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

831 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

832 

833 def test_class_decorator_pragmas(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

834 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

835 class Foo(object): 

836 def __init__(self): 

837 self.x = 3 

838 

839 @foo # nocover 

840 class Bar(object): 

841 def __init__(self): 

842 self.x = 8 

843 """) 

844 assert parser.raw_statements == {1, 2, 3, 5, 6, 7, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

845 assert parser.statements == {1, 2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

846 

847 def test_over_exclusion_bug1779(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

848 # https://github.com/coveragepy/coveragepy/issues/1779 

849 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

850 import abc 

851 

852 class MyProtocol: # nocover 3 

853 @abc.abstractmethod # nocover 4 

854 def my_method(self) -> int: 

855 ... # 6 

856 

857 def function() -> int: 

858 return 9 

859 """) 

860 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

861 assert parser.statements == {1, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

862 

863 def test_multiline_exclusion_single_line(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

864 regex = r"print\('.*'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

865 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

866 """\ 

867 def foo(): 

868 print('Hello, world!') 

869 """, 

870 regex, 

871 ) 

872 assert parser.lines_matching(regex) == {2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

873 assert parser.raw_statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

874 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

875 

876 def test_multiline_exclusion_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

877 # A multi-line exclusion that matches a colon line still excludes the entire block. 

878 regex = r"if T:\n\s+print\('Hello, world!'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

879 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

880 """\ 

881 def foo(): 

882 if T: 

883 print('Hello, world!') 

884 print('This is a multiline regex test.') 

885 a = 5 

886 """, 

887 regex, 

888 ) 

889 assert parser.lines_matching(regex) == {2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

890 assert parser.raw_statements == {1, 2, 3, 4, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

891 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

892 

893 def test_multiline_exclusion_no_match(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

894 regex = r"nonexistent" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

895 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

896 """\ 

897 def foo(): 

898 print('Hello, world!') 

899 """, 

900 regex, 

901 ) 

902 assert parser.lines_matching(regex) == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

903 assert parser.raw_statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

904 assert parser.statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

905 

906 def test_multiline_exclusion_no_source(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

907 regex = r"anything" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

908 parser = PythonParser(text="", filename="dummy.py", exclude=regex) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

909 assert parser.lines_matching(regex) == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

910 assert parser.raw_statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

911 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

912 

913 def test_multiline_exclusion_all_lines_must_match(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

914 # https://github.com/coveragepy/coveragepy/issues/996 

915 regex = r"except ValueError:\n\s*print\('false'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

916 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

917 """\ 

918 try: 

919 a = 2 

920 print('false') 

921 except ValueError: 

922 print('false') 

923 except ValueError: 

924 print('something else') 

925 except IndexError: 

926 print('false') 

927 """, 

928 regex, 

929 ) 

930 assert parser.lines_matching(regex) == {4, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

931 assert parser.raw_statements == {1, 2, 3, 4, 5, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

932 assert parser.statements == {1, 2, 3, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

933 

934 def test_multiline_exclusion_multiple_matches(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

935 regex = r"print\('.*'\)\n\s+. = \d" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

936 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

937 """\ 

938 def foo(): 

939 print('Hello, world!') 

940 a = 5 

941 def bar(): 

942 print('Hello again!') 

943 b = 6 

944 """, 

945 regex, 

946 ) 

947 assert parser.lines_matching(regex) == {2, 3, 5, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

948 assert parser.raw_statements == {1, 2, 3, 4, 5, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

949 assert parser.statements == {1, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

950 

951 def test_multiline_exclusion_suite2(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

952 regex = r"print\('Hello, world!'\)\n\s+if T:" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

953 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

954 """\ 

955 def foo(): 

956 print('Hello, world!') 

957 if T: 

958 print('This is a test.') 

959 """, 

960 regex, 

961 ) 

962 assert parser.lines_matching(regex) == {2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

963 assert parser.raw_statements == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

964 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

965 

966 def test_multiline_exclusion_match_all(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

967 regex = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

968 r"def foo\(\):\n\s+print\('Hello, world!'\)\n" 

969 + r"\s+if T:\n\s+print\('This is a test\.'\)" 

970 ) 

971 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

972 """\ 

973 def foo(): 

974 print('Hello, world!') 

975 if T: 

976 print('This is a test.') 

977 """, 

978 regex, 

979 ) 

980 assert parser.lines_matching(regex) == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

981 assert parser.raw_statements == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

982 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

983 

984 def test_multiline_exclusion_block(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

985 # https://github.com/coveragepy/coveragepy/issues/1803 

986 regex = "# no cover: start(?s:.)*?# no cover: stop" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

987 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

988 """\ 

989 a = my_function1() 

990 if debug: 

991 msg = "blah blah" 

992 # no cover: start 

993 log_message(msg, a) 

994 b = my_function2() 

995 # no cover: stop 

996 count_this() 

997 # no cover: start 

998 but_not_this() 

999 # no cover: stop 

1000 """, 

1001 regex, 

1002 ) 

1003 assert parser.lines_matching(regex) == {4, 5, 6, 7, 9, 10, 11} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1004 assert parser.raw_statements == {1, 2, 3, 5, 6, 8, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1005 assert parser.statements == {1, 2, 3, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1006 

1007 def test_multiline_exclusion_block2(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1008 # https://github.com/coveragepy/coveragepy/issues/1797 

1009 regex = r"case _:\n\s+assert_never\(" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1010 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1011 """\ 

1012 match something: 

1013 case type_1(): 

1014 logic_1() 

1015 case type_2(): 

1016 logic_2() 

1017 case _: 

1018 assert_never(something) 

1019 match something: 

1020 case type_1(): 

1021 logic_1() 

1022 case type_2(): 

1023 logic_2() 

1024 case _: 

1025 print("Default case") 

1026 """, 

1027 regex, 

1028 ) 

1029 assert parser.lines_matching(regex) == {6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1030 assert parser.raw_statements == {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1031 assert parser.statements == {1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1032 

1033 def test_multiline_exclusion_block3(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1034 # https://github.com/coveragepy/coveragepy/issues/1741 

1035 # This will only work if there's exactly one return statement in the rest of the function 

1036 regex = r"# no cover: to return(?s:.)*?return" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1037 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1038 """\ 

1039 def my_function(args, j): 

1040 if args.command == Commands.CMD.value: 

1041 return cmd_handler(j, args) 

1042 # no cover: to return 

1043 print(f"Command '{args.command}' was not handled.", file=sys.stderr) 

1044 parser.print_help(file=sys.stderr) 

1045 

1046 return os.EX_USAGE 

1047 print("not excluded") 

1048 """, 

1049 regex, 

1050 ) 

1051 assert parser.lines_matching(regex) == {4, 5, 6, 7, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1052 assert parser.raw_statements == {1, 2, 3, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1053 assert parser.statements == {1, 2, 3, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1054 

1055 def test_multiline_exclusion_whole_source(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1056 # https://github.com/coveragepy/coveragepy/issues/118 

1057 regex = r"\A(?s:.*# pragma: exclude file.*)\Z" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1058 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1059 """\ 

1060 import coverage 

1061 # pragma: exclude file 

1062 def the_void() -> None: 

1063 if "py" not in __file__: 

1064 print("Not a Python file.") 

1065 print("Everything here is excluded.") 

1066 

1067 return 

1068 print("Excluded too") 

1069 """, 

1070 regex, 

1071 ) 

1072 assert parser.lines_matching(regex) == {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1073 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1074 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1075 

1076 def test_multiline_exclusion_from_marker(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1077 # https://github.com/coveragepy/coveragepy/issues/118 

1078 regex = r"# pragma: rest of file(?s:.)*\Z" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1079 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1080 """\ 

1081 import coverage 

1082 # pragma: rest of file 

1083 def the_void() -> None: 

1084 if "py" not in __file__: 

1085 print("Not a Python file.") 

1086 print("Everything here is excluded.") 

1087 

1088 return 

1089 print("Excluded too") 

1090 """, 

1091 regex, 

1092 ) 

1093 assert parser.lines_matching(regex) == {2, 3, 4, 5, 6, 7, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1094 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1095 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1096 

1097 

1098class ParserMissingArcDescriptionTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1099 """Tests for PythonParser.missing_arc_description.""" 

1100 

1101 def test_missing_arc_description(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1102 # This code is never run, so the actual values don't matter. 

1103 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1104 if x: 

1105 print(2) 

1106 print(3) 

1107 

1108 def func5(): 

1109 for x in range(6): 

1110 if x == 7: 

1111 break 

1112 

1113 def func10(): 

1114 while something(11): 

1115 thing(12) 

1116 more_stuff(13) 

1117 """) 

1118 expected = "line 1 didn't jump to line 2 because the condition on line 1 was never true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1119 assert expected == parser.missing_arc_description(1, 2) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1120 expected = "line 1 didn't jump to line 3 because the condition on line 1 was always true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1121 assert expected == parser.missing_arc_description(1, 3) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1122 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1123 "line 6 didn't return from function 'func5' " 

1124 + "because the loop on line 6 didn't complete" 

1125 ) 

1126 assert expected == parser.missing_arc_description(6, -5) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1127 expected = "line 6 didn't jump to line 7 because the loop on line 6 never started" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1128 assert expected == parser.missing_arc_description(6, 7) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1129 expected = "line 11 didn't jump to line 12 because the condition on line 11 was never true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1130 assert expected == parser.missing_arc_description(11, 12) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1131 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1132 "line 11 didn't jump to line 13 " + "because the condition on line 11 was always true" 

1133 ) 

1134 assert expected == parser.missing_arc_description(11, 13) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1135 

1136 def test_missing_arc_descriptions_for_exceptions(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1137 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1138 try: 

1139 pass 

1140 except ZeroDivideError: 

1141 print("whoops") 

1142 except ValueError: 

1143 print("yikes") 

1144 """) 

1145 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1146 "line 3 didn't jump to line 4 " + "because the exception caught by line 3 didn't happen" 

1147 ) 

1148 assert expected == parser.missing_arc_description(3, 4) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1149 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1150 "line 5 didn't jump to line 6 " + "because the exception caught by line 5 didn't happen" 

1151 ) 

1152 assert expected == parser.missing_arc_description(5, 6) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1153 

1154 

1155class MatchCaseMissingArcDescriptionTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1156 """Missing arc descriptions for match/case.""" 

1157 

1158 def test_match_case(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1159 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1160 match command.split(): 

1161 case ["go", direction] if direction in "nesw": # 2 

1162 match = f"go: {direction}" 

1163 case ["go", _]: # 4 

1164 match = "no go" 

1165 print(match) # 6 

1166 """) 

1167 assert parser.missing_arc_description(2, 3) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1168 "line 2 didn't jump to line 3 because the pattern on line 2 never matched" 

1169 ) 

1170 assert parser.missing_arc_description(2, 4) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1171 "line 2 didn't jump to line 4 because the pattern on line 2 always matched" 

1172 ) 

1173 assert parser.missing_arc_description(4, 6) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1174 "line 4 didn't jump to line 6 because the pattern on line 4 always matched" 

1175 ) 

1176 

1177 def test_final_wildcard(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1178 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1179 match command.split(): 

1180 case ["go", direction] if direction in "nesw": # 2 

1181 match = f"go: {direction}" 

1182 case _: # 4 

1183 match = "no go" 

1184 print(match) # 6 

1185 """) 

1186 assert parser.missing_arc_description(2, 3) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1187 "line 2 didn't jump to line 3 because the pattern on line 2 never matched" 

1188 ) 

1189 assert parser.missing_arc_description(2, 4) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1190 "line 2 didn't jump to line 4 because the pattern on line 2 always matched" 

1191 ) 

1192 # 4-6 isn't a possible arc, so the description is generic. 

1193 assert parser.missing_arc_description(4, 6) == "line 4 didn't jump to line 6" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1194 

1195 def test_missing_arc_descriptions_bug1775(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1196 # Bug: the `if x == 2:` line was marked partial with a message of: 

1197 # line 6 didn't return from function 'func', because the return on line 4 wasn't executed 

1198 # At some point it changed to "didn't jump to the function exit," which 

1199 # is close enough. These situations are hard to describe precisely. 

1200 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1201 def func(): 

1202 x = 2 

1203 try: 

1204 return 4 

1205 finally: 

1206 if x == 2: # line 6 

1207 print("x is 2") 

1208 

1209 func() 

1210 """) 

1211 assert parser.missing_arc_description(6, -1) == "line 6 didn't jump to the function exit" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1212 

1213 

1214class ParserFileTest(CoverageTest): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1215 """Tests for coverage.py's code parsing from files.""" 

1216 

1217 def parse_file(self, filename: str) -> PythonParser: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1218 """Parse `text` as source, and return the `PythonParser` used.""" 

1219 parser = PythonParser(filename=filename, exclude="nocover") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1220 parser.parse_source() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1221 return parser 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1222 

1223 @pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1224 "slug, newline", 

1225 [ 

1226 ("unix", "\n"), 

1227 ("dos", "\r\n"), 

1228 ("mac", "\r"), 

1229 ], 

1230 ) 

1231 def test_line_endings(self, slug: str, newline: str) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1232 text = """\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1233 # check some basic branch counting 

1234 class Foo: 

1235 def foo(self, a): 

1236 if a: 

1237 return 5 

1238 else: 

1239 return 7 

1240 

1241 class Bar: 

1242 pass 

1243 """ 

1244 counts = {2: 1, 3: 1, 4: 2, 5: 1, 7: 1, 9: 1, 10: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1245 fname = slug + ".py" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1246 self.make_file(fname, text, newline=newline) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1247 parser = self.parse_file(fname) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1248 assert parser.exit_counts() == counts, f"Wrong for {fname!r}" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1249 

1250 def test_encoding(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1251 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1252 "encoded.py", 

1253 """\ 

1254 coverage = "\xe7\xf6v\xear\xe3g\xe9" 

1255 """, 

1256 ) 

1257 parser = self.parse_file("encoded.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1258 assert parser.exit_counts() == {1: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1259 

1260 def test_missing_line_ending(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1261 # Test that the set of statements is the same even if a final 

1262 # multi-line statement has no final newline. 

1263 # https://github.com/coveragepy/coveragepy/issues/293 

1264 

1265 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1266 "normal.py", 

1267 """\ 

1268 out, err = some_module.some_function( 

1269 ["my data", "-c", "pass"], 

1270 arg1=some_module.NAME, 

1271 arg2=some_module.OTHER_NAME).function() 

1272 """, 

1273 ) 

1274 

1275 parser = self.parse_file("normal.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1276 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1277 

1278 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1279 "abrupt.py", 

1280 """\ 

1281 out, err = some_module.some_function( 

1282 ["my data", "-c", "pass"], 

1283 arg1=some_module.NAME, 

1284 arg2=some_module.OTHER_NAME).function()""", 

1285 ) # no final newline. 

1286 

1287 # Double-check that some test helper wasn't being helpful. 

1288 with open("abrupt.py", encoding="utf-8") as f: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1289 assert f.read()[-1] == ")" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1290 

1291 parser = self.parse_file("abrupt.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1292 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1293 

1294 def test_os_error(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1295 self.make_file("cant-read.py", "BOOM!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1296 msg = "No source for code: 'cant-read.py': Fake!" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1297 with pytest.raises(NoSource, match=re.escape(msg)): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1298 with mock.patch("coverage.python.read_python_source", side_effect=OSError("Fake!")): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1299 PythonParser(filename="cant-read.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1300 

1301 

1302@pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1303 ["expr", "ret"], 

1304 [ 

1305 ("True", (True, True)), 

1306 ("False", (True, False)), 

1307 ("1", (True, True)), 

1308 ("0", (True, False)), 

1309 ("__debug__", (True, True)), 

1310 ("not __debug__", (True, False)), 

1311 ("not(__debug__)", (True, False)), 

1312 ("-__debug__", (False, False)), 

1313 ("__debug__ or True", (True, True)), 

1314 ("__debug__ + True", (False, False)), 

1315 ("x", (False, False)), 

1316 ("__debug__ or debug", (False, False)), 

1317 ], 

1318) 

1319def test_is_constant_test_expr(expr: str, ret: tuple[bool, bool]) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1320 node = ast.parse(expr, mode="eval").body 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1321 print(ast.dump(node, indent=4)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()

1322 assert is_constant_test_expr(node) == ret 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()