Coverage for coverage / control.py: 89.645%

477 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"""Central control stuff for coverage.py.""" 

5 

6from __future__ import annotations 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

7 

8import atexit 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

9import collections 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

10import contextlib 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

11import datetime 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

12import functools 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

13import os 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

14import os.path 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

15import platform 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

16import signal 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

17import sys 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

18import threading 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

19import time 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

20import warnings 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

21from collections.abc import Iterable, Iterator 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

22from types import FrameType 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

23from typing import IO, Any, Callable, cast 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

24 

25from coverage import env 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

26from coverage.annotate import AnnotateReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

27from coverage.collector import Collector 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

28from coverage.config import CoverageConfig, read_coverage_config 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

29from coverage.context import combine_context_switchers, should_start_context_test_function 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

30from coverage.core import CTRACER_FILE, Core 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

31from coverage.data import CoverageData, combine_parallel_data 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

32from coverage.debug import ( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

33 DebugControl, 

34 NoDebugging, 

35 relevant_environment_display, 

36 short_stack, 

37 write_formatted_info, 

38) 

39from coverage.disposition import disposition_debug_msg 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

40from coverage.exceptions import ConfigError, CoverageException, CoverageWarning, PluginError 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

41from coverage.files import PathAliases, abs_file, relative_filename, set_relative_directory 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

42from coverage.html import HtmlReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

43from coverage.inorout import InOrOut 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

44from coverage.jsonreport import JsonReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

45from coverage.lcovreport import LcovReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

46from coverage.misc import ( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

47 DefaultValue, 

48 bool_or_none, 

49 ensure_dir_for_file, 

50 isolate_module, 

51 join_regex, 

52) 

53from coverage.multiproc import patch_multiprocessing 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

54from coverage.patch import apply_patches 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

55from coverage.plugin import FileReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

56from coverage.plugin_support import Plugins, TCoverageInit 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

57from coverage.python import PythonFileReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

58from coverage.report import SummaryReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

59from coverage.report_core import render_report 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

60from coverage.results import Analysis, analysis_from_file_reporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

61from coverage.types import ( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

62 FilePath, 

63 TConfigSectionIn, 

64 TConfigurable, 

65 TConfigValueIn, 

66 TConfigValueOut, 

67 TFileDisposition, 

68 TLineNo, 

69 TMorf, 

70) 

71from coverage.version import __url__ 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

72from coverage.xmlreport import XmlReporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

73 

74os = isolate_module(os) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

75 

76 

77@contextlib.contextmanager 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

78def override_config(cov: Coverage, **kwargs: TConfigValueIn) -> Iterator[None]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

79 """Temporarily tweak the configuration of `cov`. 

80 

81 The arguments are applied to `cov.config` with the `from_args` method. 

82 At the end of the with-statement, the old configuration is restored. 

83 """ 

84 original_config = cov.config 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

85 cov.config = cov.config.copy() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

86 try: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

87 cov.config.from_args(**kwargs) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

88 yield 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

89 finally: 

90 cov.config = original_config 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

91 

92 

93DEFAULT_DATAFILE = DefaultValue("MISSING") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

94_DEFAULT_DATAFILE = DEFAULT_DATAFILE # Just in case, for backwards compatibility 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

95CONFIG_DATA_PREFIX = ":data:" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

96 

97 

98class Coverage(TConfigurable): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

99 """Programmatic access to coverage.py. 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

100 

101 To use:: 

102 

103 from coverage import Coverage 

104 

105 cov = Coverage() 

106 cov.start() 

107 #.. call your code .. 

108 cov.stop() 

109 cov.html_report(directory="covhtml") 

110 

111 A context manager is available to do the same thing:: 

112 

113 cov = Coverage() 

114 with cov.collect(): 

115 #.. call your code .. 

116 cov.html_report(directory="covhtml") 

117 

118 Note: in keeping with Python custom, names starting with underscore are 

119 not part of the public API. They might stop working at any point. Please 

120 limit yourself to documented methods to avoid problems. 

121 

122 Methods can raise any of the exceptions described in :ref:`api_exceptions`. 

123 

124 """ 

125 

126 # The stack of started Coverage instances. 

127 _instances: list[Coverage] = [] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

128 

129 @classmethod 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

130 def current(cls) -> Coverage | None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

131 """Get the latest started `Coverage` instance, if any. 

132 

133 Returns: a `Coverage` instance, or None. 

134 

135 .. versionadded:: 5.0 

136 

137 """ 

138 if cls._instances: 138 ↛ 141line 138 didn't jump to line 141 because the condition on line 138 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

139 return cls._instances[-1] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

140 else: 

141 return None 

142 

143 def __init__( # pylint: disable=too-many-arguments 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

144 self, 

145 data_file: FilePath | DefaultValue | None = DEFAULT_DATAFILE, 

146 data_suffix: str | bool | None = None, 

147 cover_pylib: bool | None = None, 

148 auto_data: bool = False, 

149 timid: bool | None = None, 

150 branch: bool | None = None, 

151 config_file: FilePath | bool = True, 

152 source: Iterable[str] | None = None, 

153 source_pkgs: Iterable[str] | None = None, 

154 source_dirs: Iterable[str] | None = None, 

155 omit: str | Iterable[str] | None = None, 

156 include: str | Iterable[str] | None = None, 

157 debug: Iterable[str] | None = None, 

158 concurrency: str | Iterable[str] | None = None, 

159 check_preimported: bool = False, 

160 context: str | None = None, 

161 messages: bool = False, 

162 plugins: Iterable[Callable[..., None]] | None = None, 

163 ) -> None: 

164 """ 

165 Many of these arguments duplicate and override values that can be 

166 provided in a configuration file. Parameters that are missing here 

167 will use values from the config file. 

168 

169 `data_file` is the base name of the data file to use. The config value 

170 defaults to ".coverage". None can be provided to prevent writing a data 

171 file. `data_suffix` is appended (with a dot) to `data_file` to create 

172 the final file name. If `data_suffix` is simply True, then a suffix is 

173 created with the machine and process identity included. 

174 

175 `cover_pylib` is a boolean determining whether Python code installed 

176 with the Python interpreter is measured. This includes the Python 

177 standard library and any packages installed with the interpreter. 

178 

179 If `auto_data` is true, then any existing data file will be read when 

180 coverage measurement starts, and data will be saved automatically when 

181 measurement stops. 

182 

183 If `timid` is true, then a slower and simpler trace function will be 

184 used. This is important for some environments where manipulation of 

185 tracing functions breaks the faster trace function. 

186 

187 If `branch` is true, then branch coverage will be measured in addition 

188 to the usual statement coverage. 

189 

190 `config_file` determines what configuration file to read: 

191 

192 * If it is ".coveragerc", it is interpreted as if it were True, 

193 for backward compatibility. 

194 

195 * If it is a string, it is the name of the file to read. If the 

196 file can't be read, it is an error. 

197 

198 * If it is True, then a few standard files names are tried 

199 (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for 

200 these files to not be found. 

201 

202 * If it is False, then no configuration file is read. 

203 

204 `source` is a list of file paths or package names. Only code located 

205 in the trees indicated by the file paths or package names will be 

206 measured. 

207 

208 `source_pkgs` is a list of package names. It works the same as 

209 `source`, but can be used to name packages where the name can also be 

210 interpreted as a file path. 

211 

212 `source_dirs` is a list of file paths. It works the same as 

213 `source`, but raises an error if the path doesn't exist, rather 

214 than being treated as a package name. 

215 

216 `include` and `omit` are lists of file name patterns. Files that match 

217 `include` will be measured, files that match `omit` will not. Each 

218 will also accept a single string argument. 

219 

220 `debug` is a list of strings indicating what debugging information is 

221 desired. 

222 

223 `concurrency` is a string indicating the concurrency library being used 

224 in the measured code. Without this, coverage.py will get incorrect 

225 results if these libraries are in use. Valid strings are "greenlet", 

226 "eventlet", "gevent", "multiprocessing", or "thread" (the default). 

227 This can also be a list of these strings. 

228 

229 If `check_preimported` is true, then when coverage is started, the 

230 already-imported files will be checked to see if they should be 

231 measured by coverage. Importing measured files before coverage is 

232 started can mean that code is missed. 

233 

234 `context` is a string to use as the :ref:`static context 

235 <static_contexts>` label for collected data. 

236 

237 If `messages` is true, some messages will be printed to stdout 

238 indicating what is happening. 

239 

240 If `plugins` are passed, they are an iterable of function objects 

241 accepting a `reg` object to register plugins, as described in 

242 :ref:`api_plugin`. When they are provided, they will override the 

243 plugins found in the coverage configuration file. 

244 

245 .. versionadded:: 4.0 

246 The `concurrency` parameter. 

247 

248 .. versionadded:: 4.2 

249 The `concurrency` parameter can now be a list of strings. 

250 

251 .. versionadded:: 5.0 

252 The `check_preimported` and `context` parameters. 

253 

254 .. versionadded:: 5.3 

255 The `source_pkgs` parameter. 

256 

257 .. versionadded:: 6.0 

258 The `messages` parameter. 

259 

260 .. versionadded:: 7.7 

261 The `plugins` parameter. 

262 

263 .. versionadded:: 7.8 

264 The `source_dirs` parameter. 

265 """ 

266 # Start self.config as a usable default configuration. It will soon be 

267 # replaced with the real configuration. 

268 self.config = CoverageConfig() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

269 

270 # data_file=None means no disk file at all. data_file missing means 

271 # use the value from the config file. 

272 self._no_disk = data_file is None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

273 if isinstance(data_file, DefaultValue): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

274 data_file = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

275 if data_file is not None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

276 data_file = os.fspath(data_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YPEm4Fn9ZQGo$Hp!0RIq6Jr#1S235

277 

278 # This is injectable by tests. 

279 self._debug_file: IO[str] | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

280 

281 self._auto_load = self._auto_save = auto_data 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

282 self._data_suffix_specified = data_suffix 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

283 

284 # Is it ok for no data to be collected? 

285 self._warn_no_data = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

286 self._warn_unimported_source = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

287 self._warn_preimported_source = check_preimported 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

288 self._no_warn_slugs: set[str] = set() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

289 self._messages = messages 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

290 

291 # A record of all the warnings that have been issued. 

292 self._warnings: list[str] = [] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

293 

294 # Other instance attributes, set with placebos or placeholders. 

295 # More useful objects will be created later. 

296 self._debug: DebugControl = NoDebugging() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

297 self._inorout: InOrOut | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

298 self._plugins: Plugins = Plugins() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

299 self._plugin_override = cast(Iterable[TCoverageInit] | None, plugins) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

300 self._data: CoverageData | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

301 self._data_to_close: list[CoverageData] = [] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

302 self._core: Core | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

303 self._collector: Collector | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

304 self._metacov = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

305 

306 self._file_mapper: Callable[[str], str] = abs_file 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

307 self._data_suffix = self._run_suffix = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

308 self._exclude_re: dict[str, str] = {} 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

309 self._old_sigterm: Callable[[int, FrameType | None], Any] | None = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

310 

311 # State machine variables: 

312 # Have we initialized everything? 

313 self._inited = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

314 self._inited_for_start = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

315 # Have we started collecting and not stopped it? 

316 self._started = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

317 # Should we write the debug output? 

318 self._should_write_debug = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

319 

320 # Build our configuration from a number of sources. 

321 if isinstance(config_file, str) and config_file.startswith(CONFIG_DATA_PREFIX): 321 ↛ 322line 321 didn't jump to line 322 because the condition on line 321 was never true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

322 self.config = CoverageConfig.deserialize(config_file[len(CONFIG_DATA_PREFIX) :]) 

323 else: 

324 if not isinstance(config_file, bool): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

325 config_file = os.fspath(config_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

326 self.config = read_coverage_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

327 config_file=config_file, 

328 warn=self._warn, 

329 data_file=data_file, 

330 cover_pylib=cover_pylib, 

331 timid=timid, 

332 branch=branch, 

333 parallel=bool_or_none(data_suffix), 

334 source=source, 

335 source_pkgs=source_pkgs, 

336 source_dirs=source_dirs, 

337 run_omit=omit, 

338 run_include=include, 

339 debug=debug, 

340 report_omit=omit, 

341 report_include=include, 

342 concurrency=concurrency, 

343 context=context, 

344 ) 

345 

346 # If we have subprocess measurement happening automatically, then we 

347 # want any explicit creation of a Coverage object to mean, this process 

348 # is already coverage-aware, so don't auto-measure it. By now, the 

349 # auto-creation of a Coverage object has already happened. But we can 

350 # find it and tell it not to save its data. 

351 if not env.METACOV: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

352 _prevent_sub_process_measurement() 

353 

354 def __repr__(self) -> str: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

355 core_name = self._core.tracer_class.__name__ if self._core is not None else "-none-" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

356 data_file = repr(self._data._filename) if self._data is not None else "-none-" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

357 return ( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

358 "<Coverage" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

359 + f" @0x{id(self):x}" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

360 + f" core={core_name}" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

361 + f" data_file={data_file}" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

362 + ">" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

363 ) 

364 

365 def _init(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

366 """Set all the initial state. 

367 

368 This is called by the public methods to initialize state. This lets us 

369 construct a :class:`Coverage` object, then tweak its state before this 

370 function is called. 

371 

372 """ 

373 if self._inited: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

374 return 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

375 

376 self._inited = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

377 

378 # Create and configure the debugging controller. 

379 self._debug = DebugControl(self.config.debug, self._debug_file, self.config.debug_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

380 if self._debug.should("process"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

381 self._debug.write("Coverage._init") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YPEm4Fn9ZQGo$Hp!0RIq6Jr#1S235

382 

383 if "multiprocessing" in (self.config.concurrency or ()): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

384 # Multi-processing uses parallel for the subprocesses, so also use 

385 # it for the main process. 

386 self.config.parallel = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

387 

388 # _exclude_re is a dict that maps exclusion list names to compiled regexes. 

389 self._exclude_re = {} 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

390 

391 set_relative_directory() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

392 if self.config.relative_files: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

393 self._file_mapper = relative_filename 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp!0RIqJr1S235

394 

395 # Load plugins 

396 self._plugins = Plugins(self._debug) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

397 if self._plugin_override: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

398 self._plugins.load_from_callables(self._plugin_override) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

399 else: 

400 self._plugins.load_from_config(self.config.plugins, self.config) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

401 

402 # Run configuring plugins. 

403 for plugin in self._plugins.configurers: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

404 # We need an object with set_option and get_option. Either self or 

405 # self.config will do. Choosing randomly stops people from doing 

406 # other things with those objects, against the public API. Yes, 

407 # this is a bit childish. :) 

408 plugin.configure([self, self.config][int(time.time()) % 2]) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

409 

410 def _post_init(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

411 """Stuff to do after everything is initialized.""" 

412 if self._should_write_debug: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

413 self._should_write_debug = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

414 self._write_startup_debug() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

415 

416 # "[run] _crash" will raise an exception if the value is close by in 

417 # the call stack, for testing error handling. 

418 if self.config._crash and self.config._crash in short_stack(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

419 raise RuntimeError(f"Crashing because called by {self.config._crash}") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7DlYP%Em4FnZQ'Go$Hp0R(Iq6Jr1S)235

420 

421 def _write_startup_debug(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

422 """Write out debug info at startup if needed.""" 

423 wrote_any = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

424 with self._debug.without_callers(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

425 if self._debug.should("config"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

426 write_formatted_info(self._debug.write, "config", self.config.debug_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

427 wrote_any = True 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

428 

429 if self._debug.should("sys"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

430 write_formatted_info(self._debug.write, "sys", self.sys_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

431 for plugin in self._plugins: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

432 header = "sys: " + plugin._coverage_plugin_name 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

433 write_formatted_info(self._debug.write, header, plugin.sys_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

434 wrote_any = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

435 

436 if self._debug.should("pybehave"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

437 write_formatted_info(self._debug.write, "pybehave", env.debug_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

438 wrote_any = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

439 

440 if self._debug.should("sqlite"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

441 write_formatted_info(self._debug.write, "sqlite", CoverageData.sys_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

442 wrote_any = True 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

443 

444 if wrote_any: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

445 write_formatted_info(self._debug.write, "end", ()) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

446 

447 def _should_trace(self, filename: str, frame: FrameType) -> TFileDisposition: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

448 """Decide whether to trace execution in `filename`. 

449 

450 Calls `_should_trace_internal`, and returns the FileDisposition. 

451 

452 """ 

453 assert self._inorout is not None 

454 disp = self._inorout.should_trace(filename, frame) 

455 if self._debug.should("trace"): 

456 self._debug.write(disposition_debug_msg(disp)) 

457 return disp 

458 

459 def _check_include_omit_etc(self, filename: str, frame: FrameType) -> bool: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

460 """Check a file name against the include/omit/etc, rules, verbosely. 

461 

462 Returns a boolean: True if the file should be traced, False if not. 

463 

464 """ 

465 assert self._inorout is not None 

466 reason = self._inorout.check_include_omit_etc(filename, frame) 

467 if self._debug.should("trace"): 

468 if not reason: 

469 msg = f"Including {filename!r}" 

470 else: 

471 msg = f"Not including {filename!r}: {reason}" 

472 self._debug.write(msg) 

473 

474 return not reason 

475 

476 def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

477 """Use `msg` as a warning. 

478 

479 For warning suppression, use `slug` as the shorthand. 

480 

481 If `once` is true, only show this warning once (determined by the 

482 slug.) 

483 

484 """ 

485 if not self._no_warn_slugs: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

486 self._no_warn_slugs = set(self.config.disable_warnings) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

487 

488 if slug in self._no_warn_slugs: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

489 # Don't issue the warning 

490 return 1satbTKucvdULwexfVMygzhWNAiBjXOCkDl8YP%EmFn9ZQ'GoHp!0R(IqJr#1S)235

491 

492 self._warnings.append(msg) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

493 if slug: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

494 msg = f"{msg} ({slug}); see {__url__}/messages.html#warning-{slug}" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

495 if self._debug.should("pid"): 495 ↛ 496line 495 didn't jump to line 496 because the condition on line 495 was never true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

496 msg = f"[{os.getpid()}] {msg}" 

497 warnings.warn(msg, category=CoverageWarning, stacklevel=2) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

498 

499 if once: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

500 assert slug is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'GoHp!0R(IqJr#1S235

501 self._no_warn_slugs.add(slug) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'GoHp!0R(IqJr#1S235

502 

503 def _message(self, msg: str) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

504 """Write a message to the user, if configured to do so.""" 

505 if self._messages: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

506 print(msg) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

507 

508 def get_option(self, option_name: str) -> TConfigValueOut | None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

509 """Get an option from the configuration. 

510 

511 `option_name` is a colon-separated string indicating the section and 

512 option name. For example, the ``branch`` option in the ``[run]`` 

513 section of the config file would be indicated with `"run:branch"`. 

514 

515 Returns the value of the option. The type depends on the option 

516 selected. 

517 

518 As a special case, an `option_name` of ``"paths"`` will return an 

519 dictionary with the entire ``[paths]`` section value. 

520 

521 .. versionadded:: 4.0 

522 

523 """ 

524 return self.config.get_option(option_name) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

525 

526 def set_option(self, option_name: str, value: TConfigValueIn | TConfigSectionIn) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

527 """Set an option in the configuration. 

528 

529 `option_name` is a colon-separated string indicating the section and 

530 option name. For example, the ``branch`` option in the ``[run]`` 

531 section of the config file would be indicated with ``"run:branch"``. 

532 

533 `value` is the new value for the option. This should be an 

534 appropriate Python value. For example, use True for booleans, not the 

535 string ``"True"``. 

536 

537 As an example, calling: 

538 

539 .. code-block:: python 

540 

541 cov.set_option("run:branch", True) 

542 

543 has the same effect as this configuration file: 

544 

545 .. code-block:: ini 

546 

547 [run] 

548 branch = True 

549 

550 As a special case, an `option_name` of ``"paths"`` will replace the 

551 entire ``[paths]`` section. The value should be a dictionary. 

552 

553 .. versionadded:: 4.0 

554 

555 """ 

556 self.config.set_option(option_name, value) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

557 

558 def load(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

559 """Load previously-collected coverage data from the data file.""" 

560 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

561 if self._collector is not None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

562 self._collector.reset() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

563 should_skip = self.config.parallel and not os.path.exists(self.config.data_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

564 if not should_skip: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

565 self._init_data(suffix=None) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

566 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

567 if not should_skip: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

568 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

569 self._data.read() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

570 

571 def _init_for_start(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

572 """Initialization for start()""" 

573 # Construct the collector. 

574 concurrency: list[str] = self.config.concurrency 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

575 if "multiprocessing" in concurrency: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

576 if self.config.config_file is None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

577 raise ConfigError("multiprocessing requires a configuration file") 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

578 patch_multiprocessing(rcfile=self.config.config_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

579 

580 dycon = self.config.dynamic_context 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

581 if not dycon or dycon == "none": 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

582 context_switchers = [] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

583 elif dycon == "test_function": 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

584 context_switchers = [should_start_context_test_function] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

585 else: 

586 raise ConfigError(f"Don't understand dynamic_context setting: {dycon!r}") 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

587 

588 context_switchers.extend( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

589 plugin.dynamic_context for plugin in self._plugins.context_switchers 

590 ) 

591 

592 should_start_context = combine_context_switchers(context_switchers) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

593 

594 self._core = Core( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

595 warn=self._warn, 

596 debug=(self._debug if self._debug.should("core") else None), 

597 config=self.config, 

598 dynamic_contexts=(should_start_context is not None), 

599 metacov=self._metacov, 

600 ) 

601 self._collector = Collector( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

602 core=self._core, 

603 should_trace=self._should_trace, 

604 check_include=self._check_include_omit_etc, 

605 should_start_context=should_start_context, 

606 file_mapper=self._file_mapper, 

607 branch=self.config.branch, 

608 warn=self._warn, 

609 concurrency=concurrency, 

610 ) 

611 

612 suffix = self._data_suffix_specified 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

613 if suffix: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

614 if not isinstance(suffix, str): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

615 # if data_suffix=True, use .machinename.pid.random 

616 suffix = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

617 elif self.config.parallel: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

618 if suffix is None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

619 suffix = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

620 elif not isinstance(suffix, str): 620 ↛ 625line 620 didn't jump to line 625 because the condition on line 620 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

621 suffix = bool(suffix) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

622 else: 

623 suffix = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

624 

625 self._init_data(suffix) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

626 

627 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

628 self._collector.use_data(self._data, self.config.context) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

629 

630 # Early warning if we aren't going to be able to support plugins. 

631 if self._plugins.file_tracers and not self._core.supports_plugins: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

632 self._warn( 1abKcdLefMghNijOk7l8P%m4n9Q'o$p!R(q6r#S)235

633 "Plugin file tracers ({}) aren't supported with {}".format( 

634 ", ".join( 

635 plugin._coverage_plugin_name for plugin in self._plugins.file_tracers 

636 ), 

637 self._collector.tracer_name(), 

638 ), 

639 ) 

640 for plugin in self._plugins.file_tracers: 1abKcdLefMghNijOklPmnQopRqrS235

641 plugin._coverage_enabled = False 1abKcdLefMghNijOklPmnQopRqrS235

642 

643 # Create the file classifying substructure. 

644 self._inorout = InOrOut( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

645 config=self.config, 

646 warn=self._warn, 

647 debug=(self._debug if self._debug.should("trace") else None), 

648 include_namespace_packages=self.config.include_namespace_packages, 

649 ) 

650 self._inorout.plugins = self._plugins 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

651 self._inorout.disp_class = self._core.file_disposition_class 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

652 

653 # It's useful to write debug info after initing for start. 

654 self._should_write_debug = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

655 

656 # Register our clean-up handlers. 

657 atexit.register(self._atexit) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

658 if self.config.sigterm: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

659 is_main = (threading.current_thread() == threading.main_thread()) # fmt: skip 1satbucvdwexfygzhAiBjCk7Dl8Em4Fn9Go$Hp!Iq6Jr#23

660 if is_main and not env.WINDOWS: 660 ↛ exitline 660 didn't return from function '_init_for_start' because the condition on line 660 was always true1satbucvdwexfygzhAiBjCk7Dl8Em4Fn9Go$Hp!Iq6Jr#23

661 # The Python docs seem to imply that SIGTERM works uniformly even 

662 # on Windows, but that's not my experience, and this agrees: 

663 # https://stackoverflow.com/questions/35772001/x/35792192#35792192 

664 self._old_sigterm = signal.signal( # type: ignore[assignment] 1satbucvdwexfygzhAiBjCk7Dl8Em4Fn9Go$Hp!Iq6Jr#23

665 signal.SIGTERM, 

666 self._on_sigterm, 

667 ) 

668 

669 def _init_data(self, suffix: str | bool | None) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

670 """Create a data file if we don't have one yet.""" 

671 if self._data is None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

672 # Create the data file. We do this at construction time so that the 

673 # data file will be written into the directory where the process 

674 # started rather than wherever the process eventually chdir'd to. 

675 ensure_dir_for_file(self.config.data_file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

676 self._data = CoverageData( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

677 basename=self.config.data_file, 

678 suffix=suffix, 

679 warn=self._warn, 

680 debug=self._debug, 

681 no_disk=self._no_disk, 

682 ) 

683 self._data_to_close.append(self._data) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

684 

685 def start(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

686 """Start measuring code coverage. 

687 

688 Coverage measurement is only collected in functions called after 

689 :meth:`start` is invoked. Statements in the same scope as 

690 :meth:`start` won't be measured. 

691 

692 Once you invoke :meth:`start`, you must also call :meth:`stop` 

693 eventually, or your process might not shut down cleanly. 

694 

695 The :meth:`collect` method is a context manager to handle both 

696 starting and stopping collection. 

697 

698 """ 

699 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

700 if not self._inited_for_start: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

701 self._inited_for_start = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

702 self._init_for_start() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

703 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

704 

705 assert self._collector is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

706 assert self._inorout is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

707 

708 # Issue warnings for possible problems. 

709 self._inorout.warn_conflicting_settings() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

710 

711 # See if we think some code that would eventually be measured has 

712 # already been imported. 

713 if self._warn_preimported_source: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

714 self._inorout.warn_already_imported_files() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

715 

716 if self._auto_load: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

717 self.load() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

718 

719 apply_patches(self, self.config, self._debug) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

720 

721 self._collector.start() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

722 self._started = True 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

723 self._instances.append(self) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

724 

725 def stop(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

726 """Stop measuring code coverage.""" 

727 if self._instances: 727 ↛ 730line 727 didn't jump to line 730 because the condition on line 727 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

728 if self._instances[-1] is self: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

729 self._instances.pop() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

730 if self._started: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

731 assert self._collector is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

732 self._collector.stop() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

733 self._started = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

734 

735 @contextlib.contextmanager 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

736 def collect(self) -> Iterator[None]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

737 """A context manager to start/stop coverage measurement collection. 

738 

739 .. versionadded:: 7.3 

740 

741 """ 

742 self.start() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

743 try: 1abKcdLefMghNijOklPmnQopRqrS235

744 yield 1abKcdLefMghNijOklPmnQopRqrS235

745 finally: 

746 self.stop() # pragma: nested 

747 

748 def _atexit(self, event: str = "atexit") -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

749 """Clean up on process shutdown.""" 

750 if self._debug.should("process"): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

751 self._debug.write(f"{event}: pid: {os.getpid()}, instance: {self!r}") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

752 if self._started: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

753 self.stop() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

754 if self._auto_save or event == "sigterm": 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

755 self.save() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

756 for d in self._data_to_close: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

757 d.close(force=True) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

758 

759 def _on_sigterm(self, signum_unused: int, frame_unused: FrameType | None) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

760 """A handler for signal.SIGTERM.""" 

761 self._atexit("sigterm") 

762 # Statements after here won't be seen by metacov because we just wrote 

763 # the data, and are about to kill the process. 

764 signal.signal(signal.SIGTERM, self._old_sigterm) # pragma: not covered 

765 os.kill(os.getpid(), signal.SIGTERM) # pragma: not covered 

766 

767 def erase(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

768 """Erase previously collected coverage data. 

769 

770 This removes the in-memory data collected in this session as well as 

771 discarding the data file. 

772 

773 """ 

774 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

775 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

776 if self._collector is not None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

777 self._collector.reset() 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

778 self._init_data(suffix=None) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

779 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

780 self._data.erase(parallel=self.config.parallel) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

781 self._data = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

782 self._inited_for_start = False 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

783 

784 def switch_context(self, new_context: str) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

785 """Switch to a new dynamic context. 

786 

787 `new_context` is a string to use as the :ref:`dynamic context 

788 <dynamic_contexts>` label for collected data. If a :ref:`static 

789 context <static_contexts>` is in use, the static and dynamic context 

790 labels will be joined together with a pipe character. 

791 

792 Coverage collection must be started already. 

793 

794 .. versionadded:: 5.0 

795 

796 """ 

797 if not self._started: # pragma: part started 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

798 raise CoverageException("Cannot switch context, coverage is not started") 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

799 

800 assert self._collector is not None 

801 if self._collector.should_start_context: 

802 self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True) 

803 

804 self._collector.switch_context(new_context) 

805 

806 def clear_exclude(self, which: str = "exclude") -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

807 """Clear the exclude list.""" 

808 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

809 setattr(self.config, f"{which}_list", []) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

810 self._exclude_regex_stale() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

811 

812 def exclude(self, regex: str, which: str = "exclude") -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

813 """Exclude source lines from execution consideration. 

814 

815 A number of lists of regular expressions are maintained. Each list 

816 selects lines that are treated differently during reporting. 

817 

818 `which` determines which list is modified. The "exclude" list selects 

819 lines that are not considered executable at all. The "partial" list 

820 indicates lines with branches that are not taken. 

821 

822 `regex` is a regular expression. The regex is added to the specified 

823 list. If any of the regexes in the list is found in a line, the line 

824 is marked for special treatment during reporting. 

825 

826 """ 

827 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

828 excl_list = getattr(self.config, f"{which}_list") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

829 excl_list.append(regex) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

830 self._exclude_regex_stale() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

831 

832 def _exclude_regex_stale(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

833 """Drop all the compiled exclusion regexes, a list was modified.""" 

834 self._exclude_re.clear() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

835 

836 def _exclude_regex(self, which: str) -> str: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

837 """Return a regex string for the given exclusion list.""" 

838 if which not in self._exclude_re: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

839 excl_list = getattr(self.config, f"{which}_list") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

840 self._exclude_re[which] = join_regex(excl_list) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

841 return self._exclude_re[which] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

842 

843 def get_exclude_list(self, which: str = "exclude") -> list[str]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

844 """Return a list of excluded regex strings. 

845 

846 `which` indicates which list is desired. See :meth:`exclude` for the 

847 lists that are available, and their meaning. 

848 

849 """ 

850 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

851 return cast(list[str], getattr(self.config, f"{which}_list")) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

852 

853 def save(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

854 """Save the collected coverage data to the data file.""" 

855 data = self.get_data() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

856 data.write() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

857 

858 def _make_aliases(self) -> PathAliases: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

859 """Create a PathAliases from our configuration.""" 

860 aliases = PathAliases( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

861 debugfn=(self._debug.write if self._debug.should("pathmap") else None), 

862 relative=self.config.relative_files, 

863 ) 

864 for paths in self.config.paths.values(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

865 result = paths[0] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

866 for pattern in paths[1:]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

867 aliases.add(pattern, result) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

868 return aliases 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

869 

870 def combine( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

871 self, 

872 data_paths: Iterable[str] | None = None, 

873 strict: bool = False, 

874 keep: bool = False, 

875 ) -> None: 

876 """Combine together a number of similarly-named coverage data files. 

877 

878 All coverage data files whose name starts with `data_file` (from the 

879 coverage() constructor) will be read, and combined together into the 

880 current measurements. 

881 

882 `data_paths` is a list of files or directories from which data should 

883 be combined. If no list is passed, then the data files from the 

884 directory indicated by the current data file (probably the current 

885 directory) will be combined. 

886 

887 If `strict` is true, then it is an error to attempt to combine when 

888 there are no data files to combine. 

889 

890 If `keep` is true, then original input data files won't be deleted. 

891 

892 .. versionadded:: 4.0 

893 The `data_paths` parameter. 

894 

895 .. versionadded:: 4.3 

896 The `strict` parameter. 

897 

898 .. versionadded: 5.5 

899 The `keep` parameter. 

900 """ 

901 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

902 self._init_data(suffix=None) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

903 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

904 self.get_data() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

905 

906 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

907 combine_parallel_data( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

908 self._data, 

909 aliases=self._make_aliases(), 

910 data_paths=data_paths, 

911 strict=strict, 

912 keep=keep, 

913 message=self._message, 

914 ) 

915 

916 def get_data(self) -> CoverageData: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

917 """Get the collected data. 

918 

919 Also warn about various problems collecting data. 

920 

921 Returns a :class:`coverage.CoverageData`, the collected coverage data. 

922 

923 .. versionadded:: 4.0 

924 

925 """ 

926 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

927 self._init_data(suffix=None) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

928 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

929 

930 if self._collector is not None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

931 for plugin in self._plugins: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

932 if not plugin._coverage_enabled: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

933 self._collector.plugin_was_disabled(plugin) 1stTuvUwxVyzWABXCDYEFZGH0IJ1

934 

935 if self._collector.flush_data(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

936 self._post_save_work() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

937 

938 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

939 return self._data 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

940 

941 def _post_save_work(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

942 """After saving data, look for warnings, post-work, etc. 

943 

944 Warn about things that should have happened but didn't. 

945 Look for un-executed files. 

946 

947 """ 

948 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

949 assert self._inorout is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

950 

951 # If there are still entries in the source_pkgs_unmatched list, 

952 # then we never encountered those packages. 

953 if self._warn_unimported_source: 953 ↛ 957line 953 didn't jump to line 957 because the condition on line 953 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

954 self._inorout.warn_unimported_source() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

955 

956 # Find out if we got any data. 

957 if not self._data and self._warn_no_data: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

958 self._warn("No data was collected.", slug="no-data-collected") 1satbTKucvdULwexfVMygzhWNAiBjXOCk7DlYP%Em4FnZQ'Go$Hp0R(Iq6Jr1S)235

959 

960 # Touch all the files that could have executed, so that we can 

961 # mark completely un-executed files as 0% covered. 

962 file_paths = collections.defaultdict(list) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

963 for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

964 file_path = self._file_mapper(file_path) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

965 file_paths[plugin_name].append(file_path) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

966 for plugin_name, paths in file_paths.items(): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

967 self._data.touch_files(paths, plugin_name) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

968 

969 # Backward compatibility with version 1. 

970 def analysis(self, morf: TMorf) -> tuple[str, list[TLineNo], list[TLineNo], str]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

971 """Like `analysis2` but doesn't return excluded line numbers.""" 

972 f, s, _, m, mf = self.analysis2(morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

973 return f, s, m, mf 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

974 

975 def analysis2( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

976 self, 

977 morf: TMorf, 

978 ) -> tuple[str, list[TLineNo], list[TLineNo], list[TLineNo], str]: 

979 """Analyze a module. 

980 

981 `morf` is a module or a file name. It will be analyzed to determine 

982 its coverage statistics. The return value is a 5-tuple: 

983 

984 * The file name for the module. 

985 * A list of line numbers of executable statements. 

986 * A list of line numbers of excluded statements. 

987 * A list of line numbers of statements not run (missing from 

988 execution). 

989 * A readable formatted string of the missing line numbers. 

990 

991 The analysis uses the source file itself and the current measured 

992 coverage data. 

993 

994 """ 

995 analysis = self._analyze(morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

996 return ( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

997 analysis.filename, 

998 sorted(analysis.statements), 

999 sorted(analysis.excluded), 

1000 sorted(analysis.missing), 

1001 analysis.missing_formatted(), 

1002 ) 

1003 

1004 @functools.lru_cache(maxsize=1) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1005 def _analyze(self, morf: TMorf) -> Analysis: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1006 """Analyze a module or file. Private for now.""" 

1007 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1008 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1009 

1010 data = self.get_data() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1011 file_reporter = self._get_file_reporter(morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1012 filename = self._file_mapper(file_reporter.filename) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1013 return analysis_from_file_reporter(data, self.config.precision, file_reporter, filename) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1014 

1015 def branch_stats(self, morf: TMorf) -> dict[TLineNo, tuple[int, int]]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1016 """Get branch statistics about a module. 

1017 

1018 `morf` is a module or a file name. 

1019 

1020 Returns a dict mapping line numbers to a tuple: 

1021 (total_exits, taken_exits). 

1022 

1023 .. versionadded:: 7.7 

1024 

1025 """ 

1026 analysis = self._analyze(morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1027 return analysis.branch_stats() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1028 

1029 @functools.lru_cache(maxsize=1) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1030 def _get_file_reporter(self, morf: TMorf) -> FileReporter: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1031 """Get a FileReporter for a module or file name.""" 

1032 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1033 plugin = None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1034 file_reporter: str | FileReporter = "python" 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1035 

1036 if isinstance(morf, str): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1037 mapped_morf = self._file_mapper(morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1038 plugin_name = self._data.file_tracer(mapped_morf) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1039 if plugin_name: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1040 plugin = self._plugins.get(plugin_name) 1stTuvUwxVyzWABXCDYEFZGH0IJ1

1041 

1042 if plugin: 1042 ↛ 1052line 1042 didn't jump to line 1052 because the condition on line 1042 was always true1stTuvUwxVyzWABXCDYEFZGH0IJ1

1043 file_reporter = plugin.file_reporter(mapped_morf) 1stTuvUwxVyzWABXCDYEFZGH0IJ1

1044 if file_reporter is None: 1044 ↛ 1045line 1044 didn't jump to line 1045 because the condition on line 1044 was never true1stTuvUwxVyzWABXCDYEFZGH0IJ1

1045 raise PluginError( 

1046 "Plugin {!r} did not provide a file reporter for {!r}.".format( 

1047 plugin._coverage_plugin_name, 

1048 morf, 

1049 ), 

1050 ) 

1051 

1052 if file_reporter == "python": 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1053 file_reporter = PythonFileReporter(morf, self) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1054 

1055 assert isinstance(file_reporter, FileReporter) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1056 return file_reporter 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1057 

1058 def _get_file_reporters( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1059 self, 

1060 morfs: Iterable[TMorf] | None = None, 

1061 ) -> list[tuple[FileReporter, TMorf]]: 

1062 """Get FileReporters for a list of modules or file names. 

1063 

1064 For each module or file name in `morfs`, find a FileReporter. Return 

1065 a list pairing FileReporters with the morfs. 

1066 

1067 If `morfs` is a single module or file name, this returns a list of one 

1068 FileReporter. If `morfs` is empty or None, then the list of all files 

1069 measured is used to find the FileReporters. 

1070 

1071 """ 

1072 assert self._data is not None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1073 if not morfs: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1074 morfs = self._data.measured_files() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1075 

1076 # Be sure we have a collection. 

1077 if not isinstance(morfs, (list, tuple, set)): 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1078 morfs = [morfs] # type: ignore[list-item] 1satbTKucvdULwexfVMygzhWNAiBjXOCkDl8YPEm4Fn9ZQGoHp0RIqJr1S235

1079 

1080 morfs = sorted(morfs, key=lambda m: m if isinstance(m, str) else m.__name__) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1081 return [(self._get_file_reporter(morf), morf) for morf in morfs] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1082 

1083 def _prepare_data_for_reporting(self) -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1084 """Re-map data before reporting, to get implicit "combine" behavior.""" 

1085 if self.config.paths: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1086 mapped_data = CoverageData(warn=self._warn, debug=self._debug, no_disk=True) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

1087 if self._data is not None: 1087 ↛ 1089line 1087 didn't jump to line 1089 because the condition on line 1087 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

1088 mapped_data.update(self._data, map_path=self._make_aliases().map) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

1089 self._data = mapped_data 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

1090 self._data_to_close.append(mapped_data) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEmFnZQGoHp0RIqJr1S235

1091 

1092 def report( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1093 self, 

1094 morfs: Iterable[TMorf] | None = None, 

1095 show_missing: bool | None = None, 

1096 ignore_errors: bool | None = None, 

1097 file: IO[str] | None = None, 

1098 omit: str | list[str] | None = None, 

1099 include: str | list[str] | None = None, 

1100 skip_covered: bool | None = None, 

1101 contexts: list[str] | None = None, 

1102 skip_empty: bool | None = None, 

1103 precision: int | None = None, 

1104 sort: str | None = None, 

1105 output_format: str | None = None, 

1106 ) -> float: 

1107 """Write a textual summary report to `file`. 

1108 

1109 Each module in `morfs` is listed, with counts of statements, executed 

1110 statements, missing statements, and a list of lines missed. 

1111 

1112 If `show_missing` is true, then details of which lines or branches are 

1113 missing will be included in the report. If `ignore_errors` is true, 

1114 then a failure while reporting a single file will not stop the entire 

1115 report. 

1116 

1117 `file` is a file-like object, suitable for writing. 

1118 

1119 `output_format` determines the format, either "text" (the default), 

1120 "markdown", or "total". 

1121 

1122 `include` is a list of file name patterns. Files that match will be 

1123 included in the report. Files matching `omit` will not be included in 

1124 the report. 

1125 

1126 If `skip_covered` is true, don't report on files with 100% coverage. 

1127 

1128 If `skip_empty` is true, don't report on empty files (those that have 

1129 no statements). 

1130 

1131 `contexts` is a list of regular expression strings. Only data from 

1132 :ref:`dynamic contexts <dynamic_contexts>` that match one of those 

1133 expressions (using :func:`re.search <python:re.search>`) will be 

1134 included in the report. 

1135 

1136 `precision` is the number of digits to display after the decimal 

1137 point for percentages. 

1138 

1139 All of the arguments default to the settings read from the 

1140 :ref:`configuration file <config>`. 

1141 

1142 Returns a float, the total percentage covered. 

1143 

1144 .. versionadded:: 4.0 

1145 The `skip_covered` parameter. 

1146 

1147 .. versionadded:: 5.0 

1148 The `contexts` and `skip_empty` parameters. 

1149 

1150 .. versionadded:: 5.2 

1151 The `precision` parameter. 

1152 

1153 .. versionadded:: 7.0 

1154 The `format` parameter. 

1155 

1156 """ 

1157 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1158 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1159 self, 

1160 ignore_errors=ignore_errors, 

1161 report_omit=omit, 

1162 report_include=include, 

1163 show_missing=show_missing, 

1164 skip_covered=skip_covered, 

1165 report_contexts=contexts, 

1166 skip_empty=skip_empty, 

1167 precision=precision, 

1168 sort=sort, 

1169 format=output_format, 

1170 ): 

1171 reporter = SummaryReporter(self) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1172 return reporter.report(morfs, outfile=file) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1173 

1174 def annotate( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1175 self, 

1176 morfs: Iterable[TMorf] | None = None, 

1177 directory: str | None = None, 

1178 ignore_errors: bool | None = None, 

1179 omit: str | list[str] | None = None, 

1180 include: str | list[str] | None = None, 

1181 contexts: list[str] | None = None, 

1182 ) -> None: 

1183 """Annotate a list of modules. 

1184 

1185 Each module in `morfs` is annotated. The source is written to a new 

1186 file, named with a ",cover" suffix, with each line prefixed with a 

1187 marker to indicate the coverage of the line. Covered lines have ">", 

1188 excluded lines have "-", and missing lines have "!". 

1189 

1190 See :meth:`report` for other arguments. 

1191 

1192 """ 

1193 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1194 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1195 self, 

1196 ignore_errors=ignore_errors, 

1197 report_omit=omit, 

1198 report_include=include, 

1199 report_contexts=contexts, 

1200 ): 

1201 reporter = AnnotateReporter(self) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1202 reporter.report(morfs, directory=directory) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1203 

1204 def html_report( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1205 self, 

1206 morfs: Iterable[TMorf] | None = None, 

1207 directory: str | None = None, 

1208 ignore_errors: bool | None = None, 

1209 omit: str | list[str] | None = None, 

1210 include: str | list[str] | None = None, 

1211 extra_css: str | None = None, 

1212 title: str | None = None, 

1213 skip_covered: bool | None = None, 

1214 show_contexts: bool | None = None, 

1215 contexts: list[str] | None = None, 

1216 skip_empty: bool | None = None, 

1217 precision: int | None = None, 

1218 ) -> float: 

1219 """Generate an HTML report. 

1220 

1221 The HTML is written to `directory`. The file "index.html" is the 

1222 overview starting point, with links to more detailed pages for 

1223 individual modules. 

1224 

1225 `extra_css` is a path to a file of other CSS to apply on the page. 

1226 It will be copied into the HTML directory. 

1227 

1228 `title` is a text string (not HTML) to use as the title of the HTML 

1229 report. 

1230 

1231 See :meth:`report` for other arguments. 

1232 

1233 Returns a float, the total percentage covered. 

1234 

1235 .. note:: 

1236 

1237 The HTML report files are generated incrementally based on the 

1238 source files and coverage results. If you modify the report files, 

1239 the changes will not be considered. You should be careful about 

1240 changing the files in the report folder. 

1241 

1242 """ 

1243 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1244 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1245 self, 

1246 ignore_errors=ignore_errors, 

1247 report_omit=omit, 

1248 report_include=include, 

1249 html_dir=directory, 

1250 extra_css=extra_css, 

1251 html_title=title, 

1252 html_skip_covered=skip_covered, 

1253 show_contexts=show_contexts, 

1254 report_contexts=contexts, 

1255 html_skip_empty=skip_empty, 

1256 precision=precision, 

1257 ): 

1258 reporter = HtmlReporter(self) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1259 return reporter.report(morfs) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1260 

1261 def xml_report( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1262 self, 

1263 morfs: Iterable[TMorf] | None = None, 

1264 outfile: str | None = None, 

1265 ignore_errors: bool | None = None, 

1266 omit: str | list[str] | None = None, 

1267 include: str | list[str] | None = None, 

1268 contexts: list[str] | None = None, 

1269 skip_empty: bool | None = None, 

1270 ) -> float: 

1271 """Generate an XML report of coverage results. 

1272 

1273 The report is compatible with Cobertura reports. 

1274 

1275 Each module in `morfs` is included in the report. `outfile` is the 

1276 path to write the file to, "-" will write to stdout. 

1277 

1278 See :meth:`report` for other arguments. 

1279 

1280 Returns a float, the total percentage covered. 

1281 

1282 """ 

1283 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1284 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1285 self, 

1286 ignore_errors=ignore_errors, 

1287 report_omit=omit, 

1288 report_include=include, 

1289 xml_output=outfile, 

1290 report_contexts=contexts, 

1291 skip_empty=skip_empty, 

1292 ): 

1293 return render_report(self.config.xml_output, XmlReporter(self), morfs, self._message) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1294 

1295 def json_report( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1296 self, 

1297 morfs: Iterable[TMorf] | None = None, 

1298 outfile: str | None = None, 

1299 ignore_errors: bool | None = None, 

1300 omit: str | list[str] | None = None, 

1301 include: str | list[str] | None = None, 

1302 contexts: list[str] | None = None, 

1303 pretty_print: bool | None = None, 

1304 show_contexts: bool | None = None, 

1305 ) -> float: 

1306 """Generate a JSON report of coverage results. 

1307 

1308 Each module in `morfs` is included in the report. `outfile` is the 

1309 path to write the file to, "-" will write to stdout. 

1310 

1311 `pretty_print` is a boolean, whether to pretty-print the JSON output or not. 

1312 

1313 See :meth:`report` for other arguments. 

1314 

1315 Returns a float, the total percentage covered. 

1316 

1317 .. versionadded:: 5.0 

1318 

1319 """ 

1320 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1321 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1322 self, 

1323 ignore_errors=ignore_errors, 

1324 report_omit=omit, 

1325 report_include=include, 

1326 json_output=outfile, 

1327 report_contexts=contexts, 

1328 json_pretty_print=pretty_print, 

1329 json_show_contexts=show_contexts, 

1330 ): 

1331 return render_report(self.config.json_output, JsonReporter(self), morfs, self._message) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1332 

1333 def lcov_report( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)

1334 self, 

1335 morfs: Iterable[TMorf] | None = None, 

1336 outfile: str | None = None, 

1337 ignore_errors: bool | None = None, 

1338 omit: str | list[str] | None = None, 

1339 include: str | list[str] | None = None, 

1340 contexts: list[str] | None = None, 

1341 ) -> float: 

1342 """Generate an LCOV report of coverage results. 

1343 

1344 Each module in `morfs` is included in the report. `outfile` is the 

1345 path to write the file to, "-" will write to stdout. 

1346 

1347 See :meth:`report` for other arguments. 

1348 

1349 .. versionadded:: 6.3 

1350 """ 

1351 self._prepare_data_for_reporting() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1352 with override_config( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1353 self, 

1354 ignore_errors=ignore_errors, 

1355 report_omit=omit, 

1356 report_include=include, 

1357 lcov_output=outfile, 

1358 report_contexts=contexts, 

1359 ): 

1360 return render_report(self.config.lcov_output, LcovReporter(self), morfs, self._message) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1361 

1362 def sys_info(self) -> Iterable[tuple[str, Any]]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1363 """Return a list of (key, value) pairs showing internal information.""" 

1364 

1365 import coverage as covmod 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1366 

1367 self._init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1368 self._post_init() 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1369 

1370 def plugin_info(plugins: list[Any]) -> list[str]: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1371 """Make an entry for the sys_info from a list of plug-ins.""" 

1372 entries = [] 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1373 for plugin in plugins: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1374 entry = plugin._coverage_plugin_name 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEm4FnZQGoHp0RIq6Jr1S235

1375 if not plugin._coverage_enabled: 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEm4FnZQGoHp0RIq6Jr1S235

1376 entry += " (disabled)" 1abKcdLefMghNijOklPm4nQopRq6rS235

1377 entries.append(entry) 1satbTKucvdULwexfVMygzhWNAiBjXOCkDlYPEm4FnZQGoHp0RIq6Jr1S235

1378 return entries 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1379 

1380 info = [ 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1381 ("coverage_version", covmod.__version__), 

1382 ("coverage_module", covmod.__file__), 

1383 ("core", self._collector.tracer_name() if self._collector is not None else "-none-"), 

1384 ("CTracer", f"available from {CTRACER_FILE}" if CTRACER_FILE else "unavailable"), 

1385 ("plugins.file_tracers", plugin_info(self._plugins.file_tracers)), 

1386 ("plugins.configurers", plugin_info(self._plugins.configurers)), 

1387 ("plugins.context_switchers", plugin_info(self._plugins.context_switchers)), 

1388 ("configs_attempted", self.config.config_files_attempted), 

1389 ("configs_read", self.config.config_files_read), 

1390 ("config_file", self.config.config_file), 

1391 ( 

1392 "config_contents", 

1393 repr(self.config._config_contents) if self.config._config_contents else "-none-", 

1394 ), 

1395 ("data_file", self._data.data_filename() if self._data is not None else "-none-"), 

1396 ("python", sys.version.replace("\n", "")), 

1397 ("platform", platform.platform()), 

1398 ("implementation", platform.python_implementation()), 

1399 ("build", platform.python_build()), 

1400 ("gil_enabled", getattr(sys, "_is_gil_enabled", lambda: True)()), 

1401 ("executable", sys.executable), 

1402 ("def_encoding", sys.getdefaultencoding()), 

1403 ("fs_encoding", sys.getfilesystemencoding()), 

1404 ("pid", os.getpid()), 

1405 ("cwd", os.getcwd()), 

1406 ("path", sys.path), 

1407 ("environment", [f"{k} = {v}" for k, v in relevant_environment_display(os.environ)]), 

1408 ("command_line", " ".join(getattr(sys, "argv", ["-none-"]))), 

1409 ("time", f"{datetime.datetime.now():%Y-%m-%d %H:%M:%S}"), 

1410 ] 

1411 

1412 if self._inorout is not None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1413 info.extend(self._inorout.sys_info()) 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1414 

1415 return info 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1416 

1417 

1418# Mega debugging... 

1419# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage. 

1420if int(os.getenv("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1421 from coverage.debug import decorate_methods, show_calls 

1422 

1423 Coverage = decorate_methods( # type: ignore[misc] 

1424 show_calls(show_args=True), 

1425 butnot=["get_data"], 

1426 )(Coverage) 

1427 

1428 

1429def process_startup( 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1430 *, 

1431 force: bool = False, 

1432 slug: str = "default", # pylint: disable=unused-argument 

1433) -> Coverage | None: 

1434 """Call this at Python start-up to perhaps measure coverage. 

1435 

1436 Coverage is started if one of these environment variables is defined: 

1437 

1438 - COVERAGE_PROCESS_START: the config file to use. 

1439 - COVERAGE_PROCESS_CONFIG: the config data to use, a string produced by 

1440 CoverageConfig.serialize, prefixed by ":data:". 

1441 

1442 If one of these is defined, it's used to get the coverage configuration, 

1443 and coverage is started. 

1444 

1445 For details, see https://coverage.readthedocs.io/en/latest/subprocess.html. 

1446 

1447 Returns the :class:`Coverage` instance that was started, or None if it was 

1448 not started by this call. 

1449 

1450 """ 

1451 # This function can be called more than once in a process, for a few 

1452 # reasons. 

1453 # 

1454 # 1) We install a .pth file in multiple places reported by the site module, 

1455 # so this function can be called more than once even in simple 

1456 # situations. 

1457 # 

1458 # 2) In some virtualenv configurations the same directory is visible twice 

1459 # in sys.path. This means that the .pth file will be found twice and 

1460 # executed twice, executing this function twice. 

1461 # https://github.com/coveragepy/coveragepy/issues/340 has more details. 

1462 # 

1463 # We set a global flag (an attribute on this function) to indicate that 

1464 # coverage.py has already been started, so we can avoid starting it twice. 

1465 

1466 if not force and hasattr(process_startup, "coverage"): 1466 ↛ 1474line 1466 didn't jump to line 1474 because the condition on line 1466 was always true1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1467 # We've annotated this function before, so we must have already 

1468 # auto-started coverage.py in this process. Nothing to do. 

1469 return None 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1470 

1471 # Now check for the environment variables that request coverage. If they 

1472 # aren't set, do nothing. 

1473 

1474 config_data = os.getenv("COVERAGE_PROCESS_CONFIG") 

1475 cps = os.getenv("COVERAGE_PROCESS_START") 

1476 if config_data is not None: 

1477 config_file = CONFIG_DATA_PREFIX + config_data 

1478 elif cps is not None: 

1479 config_file = cps 

1480 else: 

1481 # No request for coverage, nothing to do. 

1482 return None 

1483 

1484 cov = Coverage(config_file=config_file) 

1485 process_startup.coverage = cov # type: ignore[attr-defined] 

1486 cov._warn_no_data = False 

1487 cov._warn_unimported_source = False 

1488 cov._warn_preimported_source = False 

1489 cov._auto_save = True 

1490 cov.start() 

1491 

1492 return cov 

1493 

1494 

1495def _after_fork_in_child() -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1496 """Used by patch=fork in the child process to restart coverage.""" 

1497 if cov := Coverage.current(): 

1498 cov.stop() 

1499 process_startup(force=True, slug="fork") 

1500 

1501 

1502def _prevent_sub_process_measurement() -> None: 1satbTKucvdULwexfVMygzhWNAiBjXOCk7Dl8YP%Em4Fn9ZQ'Go$Hp!0R(Iq6Jr#1S)235

1503 """Stop any subprocess auto-measurement from writing data.""" 

1504 auto_created_coverage = getattr(process_startup, "coverage", None) 

1505 if auto_created_coverage is not None: 

1506 auto_created_coverage._auto_save = False