Coverage for tests / test_oddball.py: 100.000%

171 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"""Oddball cases for testing coverage.py""" 

5 

6from __future__ import annotations 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

7 

8import os.path 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

9import re 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

10import sys 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

11import warnings 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

12 

13import pytest 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

14 

15import coverage 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

16from coverage import env 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

17from coverage.data import sorted_lines 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

18from coverage.files import abs_file 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

19from coverage.misc import import_local_file 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

20 

21from tests import osinfo, testenv 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

22from tests.coveragetest import CoverageTest 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

23from tests.helpers import swallow_warnings 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

24 

25 

26class ThreadingTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

27 """Tests of the threading support.""" 

28 

29 def test_threading(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

30 self.check_coverage( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

31 """\ 

32 import threading 

33 

34 def fromMainThread(): 

35 return "called from main thread" 

36 

37 def fromOtherThread(): 

38 return "called from other thread" 

39 

40 def neverCalled(): 

41 return "no one calls me" 

42 

43 other = threading.Thread(target=fromOtherThread) 

44 other.start() 

45 fromMainThread() 

46 other.join() 

47 """, 

48 lines=[1, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15], 

49 missing="10", 

50 ) 

51 

52 def test_thread_run(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

53 self.check_coverage( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

54 """\ 

55 import threading 

56 

57 class TestThread(threading.Thread): 

58 def run(self): 

59 self.a = 5 

60 self.do_work() 

61 self.a = 7 

62 

63 def do_work(self): 

64 self.a = 10 

65 

66 thd = TestThread() 

67 thd.start() 

68 thd.join() 

69 """, 

70 lines=[1, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14], 

71 missing="", 

72 ) 

73 

74 

75class RecursionTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

76 """Check what happens when recursive code gets near limits.""" 

77 

78 def test_short_recursion(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

79 # We can definitely get close to 500 stack frames. 

80 self.check_coverage( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

81 """\ 

82 def recur(n): 

83 if n == 0: 

84 return 0 

85 else: 

86 return recur(n-1)+1 

87 

88 recur(495) # We can get at least this many stack frames. 

89 i = 8 # and this line will be traced 

90 """, 

91 lines=[1, 2, 3, 5, 7, 8], 

92 missing="", 

93 ) 

94 

95 @pytest.mark.flaky(max_runs=3) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

96 def test_long_recursion(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

97 # We can't finish a very deep recursion, but we don't crash. 

98 with pytest.raises(RuntimeError): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

99 with swallow_warnings("Trace function changed, data is likely wrong: None"): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

100 self.check_coverage( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

101 """\ 

102 def recur(n): 

103 if n == 0: 

104 return 0 

105 else: 

106 return recur(n-1)+1 

107 

108 recur(100000) # This is definitely too many frames. 

109 """, 

110 lines=[1, 2, 3, 5, 7], 

111 missing="", 

112 ) 

113 

114 def test_long_recursion_recovery(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

115 # Test the core of bug 93: https://github.com/coveragepy/coveragepy/issues/93 

116 # When recovering from a stack overflow, the Python trace function is 

117 # disabled, but the C trace function is not. So if we're using a 

118 # Python trace function, we won't trace anything after the stack 

119 # overflow, and there should be a warning about it. If we're using 

120 # the C trace function, only line 3 will be missing, and all else 

121 # will be traced. 

122 

123 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

124 "recur.py", 

125 """\ 

126 import sys #; sys.setrecursionlimit(70) 

127 def recur(n): 

128 if n == 0: 

129 return 0 # never hit 

130 else: 

131 return recur(n-1)+1 

132 

133 try: 

134 recur(100000) # This is definitely too many frames. 

135 except RuntimeError: 

136 i = 11 

137 i = 12 

138 """, 

139 ) 

140 

141 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

142 with swallow_warnings("Trace function changed, data is likely wrong: None"): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

143 self.start_import_stop(cov, "recur") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

144 

145 assert cov._collector is not None 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

146 pytrace = (cov._collector.tracer_name() == "PyTracer") # fmt: skip 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

147 expected_missing = [4] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

148 if pytrace: # pragma: partial metacov 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

149 expected_missing += [10, 11, 12] 1BCDEFGHIJKLMNOPQRSTUVWXYZ01234

150 

151 _, statements, missing, _ = cov.analysis("recur.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

152 assert statements == [1, 2, 3, 4, 6, 8, 9, 10, 11, 12] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

153 assert expected_missing == missing 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

154 

155 # Get a warning about the stackoverflow effect on the tracing function. 

156 if pytrace and not env.METACOV: # pragma: partial metacov 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

157 assert len(cov._warnings) == 1 

158 assert re.fullmatch( 

159 r"Trace function changed, data is likely wrong: None != " 

160 + r"<bound method PyTracer._trace of " 

161 + "<PyTracer at 0x[0-9a-fA-F]+: 6 data points in 1 files>>", 

162 cov._warnings[0], 

163 ) 

164 else: 

165 assert not cov._warnings 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

166 

167 

168class MemoryLeakTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

169 """Attempt the impossible: test that memory doesn't leak. 

170 

171 Note: this test is truly unusual, and has had a colorful history. See 

172 for example: https://github.com/coveragepy/coveragepy/issues/186 

173 

174 It may still fail occasionally, especially on PyPy. 

175 

176 """ 

177 

178 @pytest.mark.flaky 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

179 @pytest.mark.skipif(not testenv.C_TRACER, reason="Only the C tracer has refcounting issues") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

180 def test_for_leaks(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

181 # Our original bad memory leak only happened on line numbers > 255, so 

182 # make a code object with more lines than that. Ugly string mumbo 

183 # jumbo to get 300 blank lines at the beginning.. 

184 code = ( 1ijklmanbopcqAyzrstudevfgwhx

185 """\ 

186 # blank line\n""" 

187 * 300 

188 + """\ 

189 def once(x): # line 301 

190 if x % 100 == 0: 

191 raise Exception("100!") 

192 elif x % 2: 

193 return 10 

194 else: # line 306 

195 return 11 

196 i = 0 # Portable loop without alloc'ing memory. 

197 while i < ITERS: 

198 try: 

199 once(i) 

200 except: 

201 pass 

202 i += 1 # line 315 

203 """ 

204 ) 

205 lines = list(range(301, 315)) 1ijklmanbopcqAyzrstudevfgwhx

206 lines.remove(306) # Line 306 is the "else". 1ijklmanbopcqAyzrstudevfgwhx

207 

208 # This is a non-deterministic test, so try it a few times, and fail it 

209 # only if it predominantly fails. 

210 fails = 0 1ijklmanbopcqAyzrstudevfgwhx

211 for _ in range(10): 1ijklmanbopcqAyzrstudevfgwhx

212 ram_0 = osinfo.process_ram() 1ijklmanbopcqAyzrstudevfgwhx

213 self.check_coverage(code.replace("ITERS", "10"), lines=lines, missing="") 1ijklmanbopcqAyzrstudevfgwhx

214 ram_10 = osinfo.process_ram() 1ijklmanbopcqAyzrstudevfgwhx

215 self.check_coverage(code.replace("ITERS", "10000"), lines=lines, missing="") 1ijklmanbopcqAyzrstudevfgwhx

216 ram_10k = osinfo.process_ram() 1ijklmanbopcqAyzrstudevfgwhx

217 # Running the code 10k times shouldn't grow the ram much more than 

218 # running it 10 times. 

219 ram_growth = (ram_10k - ram_10) - (ram_10 - ram_0) 1ijklmanbopcqAyzrstudevfgwhx

220 if ram_growth > 100000: 1ijklmanbopcqAyzrstudevfgwhx

221 fails += 1 # pragma: only failure 1abcyzdefgh

222 

223 if fails > 8: 1ijklmanbopcqAyzrstudevfgwhx

224 pytest.fail("RAM grew by %d" % (ram_growth)) # pragma: only failure 

225 

226 @pytest.mark.skipif( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

227 not testenv.C_TRACER, 

228 reason="Only the C tracer has refcounting issues", 

229 # In fact, sysmon explicitly holds onto all code objects, 

230 # so this will definitely fail with sysmon. 

231 ) 

232 @pytest.mark.skipif( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

233 env.PYVERSION[:2] == (3, 13) and not env.GIL, 

234 reason="3.13t never frees code objects: https://github.com/python/cpython/pull/131989", 

235 ) 

236 @pytest.mark.parametrize("branch", [False, True]) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

237 def test_eval_codeobject_leak(self, branch: bool) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

238 # https://github.com/coveragepy/coveragepy/issues/1924 

239 code = """\ 1ijklmanbopcqrstudevfgwhx

240 for i in range(10_000): 

241 r = eval("'a' + '1'") 

242 assert r == 'a1' 

243 """ 

244 # Looking for leaks is hard. We consider the leak fixed if at least 

245 # one of our loops only increased the footprint by a small amount. 

246 base = osinfo.process_ram() 1ijklmanbopcqrstudevfgwhx

247 deltas = [] 1ijklmanbopcqrstudevfgwhx

248 for _ in range(30): 1ijklmanbopcqrstudevfgwhx

249 self.check_coverage(code, lines=[1, 2, 3], missing="", branch=branch) 1ijklmanbopcqrstudevfgwhx

250 now = osinfo.process_ram() 1ijklmanbopcqrstudevfgwhx

251 deltas.append(now - base) 1ijklmanbopcqrstudevfgwhx

252 print(f"Mem delta: {(now - base) // 1024}") 1ijklmanbopcqrstudevfgwhx

253 base = now 1ijklmanbopcqrstudevfgwhx

254 assert any(d < 50 * 1024 for d in deltas) 1ijklmanbopcqrstudevfgwhx

255 

256 

257class MemoryFumblingTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

258 """Test that we properly manage the None refcount.""" 

259 

260 @pytest.mark.skipif(not testenv.C_TRACER, reason="Only the C tracer has refcounting issues") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

261 def test_dropping_none(self) -> None: # pragma: not covered 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

262 # TODO: Mark this so it will only be run sometimes. 

263 pytest.skip("This is too expensive for now (30s)") 1ijklmanbopcqAyzrstudevfgwhx

264 # Start and stop coverage thousands of times to flush out bad 

265 # reference counting, maybe. 

266 _ = "this is just here to put a type comment on" # type: ignore[unreachable] 

267 self.make_file( 

268 "the_code.py", 

269 """\ 

270 import random 

271 def f(): 

272 if random.random() > .5: 

273 x = 1 

274 else: 

275 x = 2 

276 """, 

277 ) 

278 self.make_file( 

279 "main.py", 

280 """\ 

281 import coverage 

282 import sys 

283 from the_code import f 

284 for i in range(10000): 

285 cov = coverage.Coverage(branch=True) 

286 cov.start() 

287 f() 

288 cov.stop() 

289 cov.erase() 

290 print("Final None refcount: %d" % (sys.getrefcount(None))) 

291 """, 

292 ) 

293 status, out = self.run_command_status("python main.py") 

294 assert status == 0 

295 assert "Final None refcount" in out 

296 assert "Fatal" not in out 

297 

298 

299class PyexpatTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

300 """Pyexpat screws up tracing. Make sure we've counter-defended properly.""" 

301 

302 def test_pyexpat(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

303 # pyexpat calls the trace function explicitly (inexplicably), and does 

304 # it wrong for exceptions. Parsing a DOCTYPE for some reason throws 

305 # an exception internally, and triggers its wrong behavior. This test 

306 # checks that our fake PyTrace_RETURN hack in tracer.c works. It will 

307 # also detect if the pyexpat bug is fixed unbeknownst to us, meaning 

308 # we'd see two RETURNs where there should only be one. 

309 

310 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

311 "trydom.py", 

312 """\ 

313 import xml.dom.minidom 

314 

315 XML = '''\\ 

316 <!DOCTYPE fooey SYSTEM "http://www.example.com/example.dtd"> 

317 <root><child/><child/></root> 

318 ''' 

319 

320 def foo(): 

321 dom = xml.dom.minidom.parseString(XML) 

322 assert len(dom.getElementsByTagName('child')) == 2 

323 a = 11 

324 

325 foo() 

326 """, 

327 ) 

328 

329 self.make_file("outer.py", "\n" * 100 + "import trydom\na = 102\n") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

330 

331 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

332 cov.erase() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

333 

334 # Import the Python file, executing it. 

335 self.start_import_stop(cov, "outer") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

336 

337 _, statements, missing, _ = cov.analysis("trydom.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

338 assert statements == [1, 3, 8, 9, 10, 11, 13] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

339 assert missing == [] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

340 

341 _, statements, missing, _ = cov.analysis("outer.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

342 assert statements == [101, 102] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

343 assert missing == [] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

344 

345 # Make sure pyexpat isn't recorded as a source file. 

346 # https://github.com/coveragepy/coveragepy/issues/419 

347 files = cov.get_data().measured_files() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

348 msg = f"Pyexpat.c is in the measured files!: {files!r}:" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

349 assert not any(f.endswith("pyexpat.c") for f in files), msg 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

350 

351 

352class ExceptionTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

353 """I suspect different versions of Python deal with exceptions differently 

354 in the trace function. 

355 """ 

356 

357 def test_exception(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

358 # Python 2.3's trace function doesn't get called with "return" if the 

359 # scope is exiting due to an exception. This confounds our trace 

360 # function which relies on scope announcements to track which files to 

361 # trace. 

362 # 

363 # This test is designed to sniff this out. Each function in the call 

364 # stack is in a different file, to try to trip up the tracer. Each 

365 # file has active lines in a different range so we'll see if the lines 

366 # get attributed to the wrong file. 

367 

368 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

369 "oops.py", 

370 """\ 

371 def oops(args): 

372 a = 2 

373 raise Exception("oops") 

374 a = 4 

375 """, 

376 ) 

377 

378 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

379 "fly.py", 

380 "\n" * 100 

381 + """\ 

382 def fly(calls): 

383 a = 2 

384 calls[0](calls[1:]) 

385 a = 4 

386 """, 

387 ) 

388 

389 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

390 "catch.py", 

391 "\n" * 200 

392 + """\ 

393 def catch(calls): 

394 try: 

395 a = 3 

396 calls[0](calls[1:]) 

397 a = 5 

398 except: 

399 a = 7 

400 """, 

401 ) 

402 

403 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

404 "doit.py", 

405 "\n" * 300 

406 + """\ 

407 def doit(calls): 

408 try: 

409 calls[0](calls[1:]) 

410 except: 

411 a = 5 

412 """, 

413 ) 

414 

415 # Import all the modules before starting coverage, so the def lines 

416 # won't be in all the results. 

417 for mod in "oops fly catch doit".split(): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

418 import_local_file(mod) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

419 

420 # Each run nests the functions differently to get different 

421 # combinations of catching exceptions and letting them fly. 

422 runs = [ 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

423 ( 

424 "doit fly oops", 

425 { 

426 "doit.py": [302, 303, 304, 305], 

427 "fly.py": [102, 103], 

428 "oops.py": [2, 3], 

429 }, 

430 ), 

431 ( 

432 "doit catch oops", 

433 { 

434 "doit.py": [302, 303], 

435 "catch.py": [202, 203, 204, 206, 207], 

436 "oops.py": [2, 3], 

437 }, 

438 ), 

439 ( 

440 "doit fly catch oops", 

441 { 

442 "doit.py": [302, 303], 

443 "fly.py": [102, 103, 104], 

444 "catch.py": [202, 203, 204, 206, 207], 

445 "oops.py": [2, 3], 

446 }, 

447 ), 

448 ( 

449 "doit catch fly oops", 

450 { 

451 "doit.py": [302, 303], 

452 "catch.py": [202, 203, 204, 206, 207], 

453 "fly.py": [102, 103], 

454 "oops.py": [2, 3], 

455 }, 

456 ), 

457 ] 

458 

459 for callnames, lines_expected in runs: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

460 # Make the list of functions we'll call for this test. 

461 callnames_list = callnames.split() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

462 calls = [getattr(sys.modules[cn], cn) for cn in callnames_list] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

463 

464 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

465 with cov.collect(): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

466 # Call our list of functions: invoke the first, with the rest as 

467 # an argument. 

468 calls[0](calls[1:]) 1BCDEFGHIJKLMNOPQRSTUVWXYZ01234

469 

470 # Clean the line data and compare to expected results. 

471 # The file names are absolute, so keep just the base. 

472 clean_lines = {} 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

473 data = cov.get_data() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

474 for callname in callnames_list: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

475 filename = callname + ".py" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

476 clean_lines[filename] = sorted_lines(data, abs_file(filename)) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

477 

478 assert clean_lines == lines_expected 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

479 

480 

481class DoctestTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

482 """Tests invoked with doctest should measure properly.""" 

483 

484 def test_doctest(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

485 # Doctests used to be traced, with their line numbers credited to the 

486 # file they were in. Below, one of the doctests has four lines (1-4), 

487 # which would incorrectly claim that lines 1-4 of the file were 

488 # executed. In this file, line 2 is not executed. 

489 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

490 "the_doctest.py", 

491 '''\ 

492 if "x" in "abc": 

493 print("hello") 

494 def return_arg_or_void(arg): 

495 """If <arg> is None, return "Void"; otherwise return <arg> 

496 

497 >>> return_arg_or_void(None) 

498 'Void' 

499 >>> return_arg_or_void("arg") 

500 'arg' 

501 >>> return_arg_or_void("None") 

502 'None' 

503 >>> if "x" in "xyz": # line 1 

504 ... if "a" in "aswed": # line 2 

505 ... if "a" in "abc": # line 3 

506 ... return_arg_or_void(12) # line 4 

507 12 

508 """ 

509 if arg is None: 

510 return "Void" 

511 else: 

512 return arg 

513 

514 import doctest, sys 

515 doctest.testmod(sys.modules[__name__]) # we're not __main__ :( 

516 ''', 

517 ) 

518 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

519 with warnings.catch_warnings(): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

520 # Doctest calls pdb which opens ~/.pdbrc without an encoding argument, 

521 # but we don't care. PYVERSIONS: this was needed for 3.10 only. 

522 warnings.filterwarnings("ignore", r".*'encoding' argument not specified.*") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

523 self.start_import_stop(cov, "the_doctest") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

524 data = cov.get_data() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

525 assert len(data.measured_files()) == 1 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

526 lines = sorted_lines(data, data.measured_files().pop()) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

527 assert lines == [1, 3, 18, 19, 21, 23, 24] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

528 

529 

530class GettraceTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

531 """Tests that we work properly with `sys.gettrace()`.""" 

532 

533 def test_round_trip_in_untraced_function(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

534 # https://github.com/coveragepy/coveragepy/issues/575 

535 self.make_file("main.py", """import sample""") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

536 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

537 "sample.py", 

538 """\ 

539 from swap import swap_it 

540 def doit(): 

541 print(3) 

542 swap_it() 

543 print(5) 

544 def doit_soon(): 

545 print(7) 

546 doit() 

547 print(9) 

548 print(10) 

549 doit_soon() 

550 print(12) 

551 """, 

552 ) 

553 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

554 "swap.py", 

555 """\ 

556 import sys 

557 def swap_it(): 

558 sys.settrace(sys.gettrace()) 

559 """, 

560 ) 

561 

562 # Use --source=sample to prevent measurement of swap.py. 

563 cov = coverage.Coverage(source=["sample"]) 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

564 self.start_import_stop(cov, "main") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

565 

566 assert self.stdout() == "10\n7\n3\n5\n9\n12\n" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

567 

568 _, statements, missing, _ = cov.analysis("sample.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

569 assert statements == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

570 assert missing == [] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

571 

572 def test_setting_new_trace_function(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

573 # https://github.com/coveragepy/coveragepy/issues/436 

574 if testenv.SETTRACE_CORE or not env.PYBEHAVIOR.branch_right_left: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

575 missing = "5-7, 13-14" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

576 else: 

577 missing = "5-7" 156789!#$%'()

578 self.check_coverage( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

579 """\ 

580 import os.path 

581 import sys 

582 

583 def tracer(frame, event, arg): 

584 filename = os.path.basename(frame.f_code.co_filename) # 5 

585 print(f"{event}: {filename} @ {frame.f_lineno}") # 6 

586 return tracer # 7 

587 

588 def begin(): 

589 sys.settrace(tracer) 

590 

591 def collect(): 

592 t = sys.gettrace() # 13 

593 assert t is tracer, t # 14 

594 

595 def test_unsets_trace() -> None: 

596 begin() 

597 collect() 

598 

599 old = sys.gettrace() 

600 test_unsets_trace() 

601 sys.settrace(old) 

602 a = 21 

603 b = 22 

604 """, 

605 lines=[1, 2, 4, 5, 6, 7, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 23, 24], 

606 missing=missing, 

607 ) 

608 

609 assert self.last_module_name is not None 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

610 out = self.stdout().replace(self.last_module_name, "coverage_test") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

611 expected = ( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

612 "call: coverage_test.py @ 12\n" 

613 + "line: coverage_test.py @ 13\n" 

614 + "line: coverage_test.py @ 14\n" 

615 + "return: coverage_test.py @ 14\n" 

616 ) 

617 assert expected == out 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

618 

619 @pytest.mark.skipif(env.METACOV, reason="Can't set trace functions during meta-coverage") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

620 def test_atexit_gettrace(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

621 # This is not a test of coverage at all, but of our understanding 

622 # of this edge-case behavior in various Pythons. 

623 

624 self.make_file( 

625 "atexit_gettrace.py", 

626 """\ 

627 import atexit, sys 

628 

629 def trace_function(frame, event, arg): 

630 return trace_function 

631 sys.settrace(trace_function) 

632 

633 def show_trace_function(): 

634 tfn = sys.gettrace() 

635 if tfn is not None: 

636 tfn = tfn.__name__ 

637 print(tfn) 

638 atexit.register(show_trace_function) 

639 

640 # This will show what the trace function is at the end of the program. 

641 """, 

642 ) 

643 status, out = self.run_command_status("python atexit_gettrace.py") 

644 assert status == 0 

645 if env.PYPY: 

646 # PyPy clears the trace function before atexit runs. 

647 assert out == "None\n" 

648 else: 

649 # Other Pythons leave the trace function in place. 

650 assert out == "trace_function\n" 

651 

652 

653class ExecTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

654 """Tests of exec.""" 

655 

656 def test_correct_filename(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

657 # https://github.com/coveragepy/coveragepy/issues/380 

658 # Bug was that exec'd files would have their lines attributed to the 

659 # calling file. Make two files, both with ~30 lines, but no lines in 

660 # common. Line 30 in to_exec.py was recorded as line 30 in main.py, 

661 # but now it's fixed. :) 

662 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

663 "to_exec.py", 

664 """\ 

665 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 

666 print("var is {}".format(var)) # line 31 

667 """, 

668 ) 

669 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

670 "main.py", 

671 """\ 

672 namespace = {'var': 17} 

673 with open("to_exec.py", encoding="utf-8") as to_exec_py: 

674 code = compile(to_exec_py.read(), 'to_exec.py', 'exec') 

675 exec(code, globals(), namespace) 

676 \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 

677 print("done") # line 35 

678 """, 

679 ) 

680 

681 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

682 self.start_import_stop(cov, "main") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

683 

684 _, statements, missing, _ = cov.analysis("main.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

685 assert statements == [1, 2, 3, 4, 35] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

686 assert missing == [] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

687 _, statements, missing, _ = cov.analysis("to_exec.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

688 assert statements == [31] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

689 assert missing == [] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

690 

691 def test_unencodable_filename(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

692 # https://github.com/coveragepy/coveragepy/issues/891 

693 self.make_file("bug891.py", r"""exec(compile("pass", "\udcff.py", "exec"))""") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

694 cov = coverage.Coverage() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

695 self.start_import_stop(cov, "bug891") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

696 # Saving would fail trying to encode \udcff.py 

697 cov.save() 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

698 files = [os.path.basename(f) for f in cov.get_data().measured_files()] 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

699 assert "bug891.py" in files 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQsRtSuTdUeVvWfXgYwZh0x1234

700 

701 

702class MockingProtectionTest(CoverageTest): 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

703 """Tests about protecting ourselves from aggressive mocking. 

704 

705 https://github.com/coveragepy/coveragepy/issues/416 

706 

707 """ 

708 

709 def test_isolate_module_os(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

710 # To see if this test still detects the problem, change isolate_module 

711 # in misc.py to simply return its argument. It should fail with a 

712 # stack trace like: 

713 # File "/tmp/pytest-of-ned/pytest-118/t0/bug416.py", line 11, in test_path_exists 

714 # import bug416a 

715 # File "/Users/ned/coverage/trunk/coverage/control.py", line 446, in _should_trace 

716 # disp = self._inorout.should_trace(filename, frame) 

717 # File "/Users/ned/coverage/trunk/coverage/inorout.py", line 343, in should_trace 

718 # orig = os.path.basename(original_filename) 

719 # File "/tmp/pytest-of-ned/pytest-118/t0/bug416.py", line 6, in __getattr__ 

720 # raise ZeroDivisionError(f"boom: {name}") 

721 # ZeroDivisionError: boom: basename 

722 

723 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

724 "bug416.py", 

725 """\ 

726 import os.path 

727 from unittest import mock 

728 

729 class BadMod: 

730 def __getattr__(self, name): 

731 raise ZeroDivisionError(f"boom: {name}") 

732 

733 @mock.patch("os.path", new=BadMod()) 

734 def test_path_exists(): 

735 print("in test") 

736 import bug416a 

737 print(bug416a.foo) 

738 try: 

739 os.path.exists(".") 

740 except ZeroDivisionError as e: 

741 print(f"Got {e}!") 

742 

743 test_path_exists() 

744 """, 

745 ) 

746 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

747 "bug416a.py", 

748 """\ 

749 print("bug416a.py") 

750 foo = 23 

751 """, 

752 ) 

753 

754 import py_compile 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

755 

756 py_compile.compile("bug416a.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

757 out = self.run_command("coverage run bug416.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

758 assert out == "in test\nbug416a.py\n23\nGot boom: exists!\n" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

759 

760 def test_defend_against_mock_open(self) -> None: 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

761 # We defend against mocking of builtins.open, which some test suites 

762 # do although they shouldn't. 

763 # See https://github.com/coveragepy/coveragepy/issues/2083 

764 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

765 "mock_open.py", 

766 """\ 

767 from unittest import mock 

768 

769 @mock.patch("builtins.open", new=lambda *a, **k: 1/0) 

770 def test_path_exists(): 

771 print("in test") 

772 try: 

773 open("somefile") 

774 except ZeroDivisionError as e: 

775 print(f"Got {e}!") 

776 

777 test_path_exists() 

778 """, 

779 ) 

780 self.make_file( 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

781 ".coveragerc", 

782 """\ 

783 [run] 

784 branch = True 

785 disable_warnings = no-sysmon,no-ctracer 

786 """, 

787 ) 

788 out = self.run_command("coverage run --branch mock_open.py") 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234

789 assert out == "in test\nGot division by zero!\n" 1iBjCkDlEmFaGnHbIoJpKcLqMANyOzPrQ5sR6tS7uT8dU9eV!vW#fX$gY%wZ'h0(x1)234