Coverage for coverage / inorout.py: 84.082%

320 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"""Determining whether files are being measured/reported or not.""" 

5 

6from __future__ import annotations 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

7 

8import importlib.util 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

9import inspect 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

10import itertools 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

11import os 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

12import os.path 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

13import platform 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

14import re 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

15import sys 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

16import sysconfig 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

17import traceback 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

18from collections.abc import Iterable 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

19from types import FrameType, ModuleType 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

20from typing import TYPE_CHECKING, Any, cast 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

21 

22from coverage import env 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

23from coverage.disposition import FileDisposition, disposition_init 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

24from coverage.exceptions import ConfigError, CoverageException, PluginError 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

25from coverage.files import ( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

26 GlobMatcher, 

27 ModuleMatcher, 

28 TreeMatcher, 

29 canonical_filename, 

30 find_python_files, 

31 prep_patterns, 

32) 

33from coverage.misc import isolate_module, sys_modules_saved 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

34from coverage.python import source_for_file, source_for_morf 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

35from coverage.types import TDebugCtl, TFileDisposition, TMorf, TWarnFn 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

36 

37if TYPE_CHECKING: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

38 from coverage.config import CoverageConfig 

39 from coverage.plugin_support import Plugins 

40 

41 

42modules_we_happen_to_have: list[ModuleType] = [ 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

43 inspect, 

44 itertools, 

45 os, 

46 platform, 

47 re, 

48 sysconfig, 

49 traceback, 

50] 

51 

52if env.PYPY: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

53 # Pypy has some unusual stuff in the "stdlib". Consider those locations 

54 # when deciding where the stdlib is. These modules are not used for anything, 

55 # they are modules importable from the pypy lib directories, so that we can 

56 # find those directories. 

57 import _pypy_irc_topic # pylint: disable=import-error 1BCD

58 import _structseq # pylint: disable=import-error 1BCD

59 

60 modules_we_happen_to_have.extend([_structseq, _pypy_irc_topic]) 1BCD

61 

62 

63os = isolate_module(os) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

64 

65 

66def canonical_path(morf: TMorf, directory: bool = False) -> str: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

67 """Return the canonical path of the module or file `morf`. 

68 

69 If the module is a package, then return its directory. If it is a 

70 module, then return its file, unless `directory` is True, in which 

71 case return its enclosing directory. 

72 

73 """ 

74 morf_path = canonical_filename(source_for_morf(morf)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

75 if morf_path.endswith("__init__.py") or directory: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

76 morf_path = os.path.split(morf_path)[0] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

77 return morf_path 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

78 

79 

80def name_for_module(filename: str, frame: FrameType | None) -> str | None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

81 """Get the name of the module for a filename and frame. 

82 

83 For configurability's sake, we allow __main__ modules to be matched by 

84 their importable name. 

85 

86 If loaded via runpy (aka -m), we can usually recover the "original" 

87 full dotted module name, otherwise, we resort to interpreting the 

88 file name to get the module's name. In the case that the module name 

89 can't be determined, None is returned. 

90 

91 """ 

92 module_globals = frame.f_globals if frame is not None else {} 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

93 dunder_name: str | None = module_globals.get("__name__", None) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

94 

95 if isinstance(dunder_name, str) and dunder_name != "__main__": 95 ↛ 97line 95 didn't jump to line 97 because the condition on line 95 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

96 # This is the usual case: an imported module. 

97 return dunder_name 

98 

99 spec = module_globals.get("__spec__", None) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

100 if spec: 100 ↛ 101line 100 didn't jump to line 101 because the condition on line 100 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

101 fullname = spec.name 

102 if isinstance(fullname, str) and fullname != "__main__": 

103 # Module loaded via: runpy -m 

104 return fullname 

105 

106 # Script as first argument to Python command line. 

107 inspectedname = inspect.getmodulename(filename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

108 if inspectedname is not None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

109 return inspectedname 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

110 else: 

111 return dunder_name 1aEbFcGdHeIfJgKhLiMjNkO5lP6mQ7nR8oS9pT!qU#rV$BC

112 

113 

114def module_is_namespace(mod: ModuleType) -> bool: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

115 """Is the module object `mod` a PEP420 namespace module?""" 

116 return hasattr(mod, "__path__") and getattr(mod, "__file__", None) is None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

117 

118 

119def module_has_file(mod: ModuleType) -> bool: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

120 """Does the module object `mod` have an existing __file__ ?""" 

121 mod__file__ = getattr(mod, "__file__", None) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

122 if mod__file__ is None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

123 return False 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

124 return os.path.exists(mod__file__) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

125 

126 

127def file_and_path_for_module(modulename: str) -> tuple[str | None, list[str]]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

128 """Find the file and search path for `modulename`. 

129 

130 Returns: 

131 filename: The filename of the module, or None. 

132 path: A list (possibly empty) of directories to find submodules in. 

133 

134 """ 

135 filename = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

136 path = [] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

137 try: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

138 spec = importlib.util.find_spec(modulename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

139 except Exception: 

140 pass 

141 else: 

142 if spec is not None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

143 filename = spec.origin 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

144 path = list(spec.submodule_search_locations or ()) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

145 return filename, path 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

146 

147 

148def add_stdlib_paths(paths: set[str]) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

149 """Add paths where the stdlib can be found to the set `paths`.""" 

150 # Look at where some standard modules are located. That's the 

151 # indication for "installed with the interpreter". In some 

152 # environments (virtualenv, for example), these modules may be 

153 # spread across a few locations. Look at all the candidate modules 

154 # we've imported, and take all the different ones. 

155 for m in modules_we_happen_to_have: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

156 if hasattr(m, "__file__"): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

157 paths.add(canonical_path(m, directory=True)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

158 

159 

160def add_third_party_paths(paths: set[str]) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

161 """Add locations for third-party packages to the set `paths`.""" 

162 # Get the paths that sysconfig knows about. 

163 scheme_names = set(sysconfig.get_scheme_names()) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

164 

165 for scheme in scheme_names: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

166 # https://foss.heptapod.net/pypy/pypy/-/issues/3433 

167 better_scheme = "pypy_posix" if scheme == "pypy" else scheme 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

168 if os.name in better_scheme.split("_"): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

169 config_paths = sysconfig.get_paths(scheme) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

170 for path_name in ["platlib", "purelib", "scripts"]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

171 paths.add(config_paths[path_name]) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

172 

173 

174def add_coverage_paths(paths: set[str]) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

175 """Add paths where coverage.py code can be found to the set `paths`.""" 

176 cover_path = canonical_path(__file__, directory=True) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

177 paths.add(cover_path) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

178 if env.TESTING: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

179 # Don't include our own test code. 

180 paths.add(os.path.join(cover_path, "tests")) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

181 

182 

183class InOrOut: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

184 """Machinery for determining what files to measure.""" 

185 

186 def __init__( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

187 self, 

188 config: CoverageConfig, 

189 warn: TWarnFn, 

190 debug: TDebugCtl | None, 

191 include_namespace_packages: bool, 

192 ) -> None: 

193 self.warn = warn 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

194 self.debug = debug 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

195 self.include_namespace_packages = include_namespace_packages 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

196 

197 self.source_pkgs: list[str] = [] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

198 self.source_pkgs.extend(config.source_pkgs) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

199 self.source_dirs: list[str] = [] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

200 self.source_dirs.extend(config.source_dirs) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

201 for src in config.source or []: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

202 if os.path.isdir(src): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

203 self.source_dirs.append(src) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

204 else: 

205 self.source_pkgs.append(src) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

206 

207 # Canonicalize everything in `source_dirs`. 

208 # Also confirm that they actually are directories. 

209 for i, src in enumerate(self.source_dirs): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

210 if not os.path.isdir(src): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

211 raise ConfigError(f"Source dir is not a directory: {src!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kOlPx1mQnRy2oSpTz3qUrVA4BCD

212 self.source_dirs[i] = canonical_filename(src) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

213 

214 self.source_pkgs_unmatched = self.source_pkgs[:] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

215 

216 self.include = prep_patterns(config.run_include) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

217 self.omit = prep_patterns(config.run_omit) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

218 

219 # The directories for files considered "installed with the interpreter". 

220 self.pylib_paths: set[str] = set() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

221 if not config.cover_pylib: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

222 add_stdlib_paths(self.pylib_paths) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

223 

224 # To avoid tracing the coverage.py code itself, we skip anything 

225 # located where we are. 

226 self.cover_paths: set[str] = set() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

227 add_coverage_paths(self.cover_paths) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

228 

229 # Find where third-party packages are installed. 

230 self.third_paths: set[str] = set() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

231 add_third_party_paths(self.third_paths) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

232 

233 def _debug(msg: str) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

234 if self.debug: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

235 self.debug.write(msg) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

236 

237 # The matchers for should_trace. 

238 

239 # Generally useful information 

240 _debug("sys.path:" + "".join(f"\n {p}" for p in sys.path)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

241 

242 # Create the matchers we need for should_trace 

243 self.source_match = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

244 self.source_pkgs_match = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

245 self.pylib_match = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

246 self.include_match = self.omit_match = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

247 

248 if self.source_dirs or self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

249 against = [] 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

250 if self.source_dirs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

251 self.source_match = TreeMatcher(self.source_dirs, "source") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

252 against.append(f"trees {self.source_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

253 if self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

254 self.source_pkgs_match = ModuleMatcher(self.source_pkgs, "source_pkgs") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

255 against.append(f"modules {self.source_pkgs_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

256 _debug("Source matching against " + " and ".join(against)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

257 else: 

258 if self.pylib_paths: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

259 self.pylib_match = TreeMatcher(self.pylib_paths, "pylib") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

260 _debug(f"Python stdlib matching: {self.pylib_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

261 if self.include: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

262 self.include_match = GlobMatcher(self.include, "include") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

263 _debug(f"Include matching: {self.include_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

264 if self.omit: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

265 self.omit_match = GlobMatcher(self.omit, "omit") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

266 _debug(f"Omit matching: {self.omit_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

267 

268 self.cover_match = TreeMatcher(self.cover_paths, "coverage") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

269 _debug(f"Coverage code matching: {self.cover_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

270 

271 self.third_match = TreeMatcher(self.third_paths, "third") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

272 _debug(f"Third-party lib matching: {self.third_match!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

273 

274 # Check if the source we want to measure has been installed as a 

275 # third-party package. 

276 # Is the source inside a third-party area? 

277 self.source_in_third_paths = set() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

278 with sys_modules_saved(): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

279 for pkg in self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

280 try: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

281 modfile, path = file_and_path_for_module(pkg) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

282 _debug(f"Imported source package {pkg!r} as {modfile!r}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

283 except CoverageException as exc: 

284 _debug(f"Couldn't import source package {pkg!r}: {exc}") 

285 continue 

286 if modfile: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

287 if self.third_match.match(modfile): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

288 _debug( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

289 f"Source in third-party: source_pkg {pkg!r} at {modfile!r}", 

290 ) 

291 self.source_in_third_paths.add(canonical_path(source_for_file(modfile))) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

292 else: 

293 for pathdir in path: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

294 if self.third_match.match(pathdir): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

295 _debug( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

296 f"Source in third-party: {pkg!r} path directory at {pathdir!r}", 

297 ) 

298 self.source_in_third_paths.add(pathdir) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

299 

300 for src in self.source_dirs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

301 if self.third_match.match(src): 301 ↛ 302line 301 didn't jump to line 302 because the condition on line 301 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

302 _debug(f"Source in third-party: source directory {src!r}") 

303 self.source_in_third_paths.add(src) 

304 self.source_in_third_match = TreeMatcher(self.source_in_third_paths, "source_in_third") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

305 _debug(f"Source in third-party matching: {self.source_in_third_match}") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

306 

307 self.plugins: Plugins 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

308 self.disp_class: type[TFileDisposition] = FileDisposition 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

309 

310 def should_trace(self, filename: str, frame: FrameType | None = None) -> TFileDisposition: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

311 """Decide whether to trace execution in `filename`, with a reason. 

312 

313 This function is called from the trace function. As each new file name 

314 is encountered, this function determines whether it is traced or not. 

315 

316 Returns a FileDisposition object. 

317 

318 """ 

319 original_filename = filename 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

320 disp = disposition_init(self.disp_class, filename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

321 

322 def nope(disp: TFileDisposition, reason: str) -> TFileDisposition: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

323 """Simple helper to make it easy to return NO.""" 

324 disp.trace = False 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

325 disp.reason = reason 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

326 return disp 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

327 

328 if original_filename.startswith("<"): 328 ↛ 329line 328 didn't jump to line 329 because the condition on line 328 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

329 return nope(disp, "original file name is not real") 

330 

331 if frame is not None: 331 ↛ 338line 331 didn't jump to line 338 because the condition on line 331 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

332 # Compiled Python files have two file names: frame.f_code.co_filename is 

333 # the file name at the time the .pyc was compiled. The second name is 

334 # __file__, which is where the .pyc was actually loaded from. Since 

335 # .pyc files can be moved after compilation (for example, by being 

336 # installed), we look for __file__ in the frame and prefer it to the 

337 # co_filename value. 

338 dunder_file = frame.f_globals and frame.f_globals.get("__file__") 

339 if dunder_file: 

340 # Danger: __file__ can (rarely?) be of type Path. 

341 filename = source_for_file(str(dunder_file)) 

342 if original_filename and not original_filename.startswith("<"): 

343 orig = os.path.basename(original_filename) 

344 if orig != os.path.basename(filename): 

345 # Files shouldn't be renamed when moved. This happens when 

346 # exec'ing code. If it seems like something is wrong with 

347 # the frame's file name, then just use the original. 

348 filename = original_filename 

349 

350 if not filename: 350 ↛ 352line 350 didn't jump to line 352 because the condition on line 350 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

351 # Empty string is pretty useless. 

352 return nope(disp, "empty string isn't a file name") 

353 

354 if filename.startswith("memory:"): 354 ↛ 355line 354 didn't jump to line 355 because the condition on line 354 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

355 return nope(disp, "memory isn't traceable") 

356 

357 if filename.startswith("<"): 357 ↛ 362line 357 didn't jump to line 362 because the condition on line 357 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

358 # Lots of non-file execution is represented with artificial 

359 # file names like "<string>", "<doctest readme.txt[0]>", or 

360 # "<exec_function>". Don't ever trace these executions, since we 

361 # can't do anything with the data later anyway. 

362 return nope(disp, "file name is not real") 

363 

364 canonical = canonical_filename(filename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

365 disp.canonical_filename = canonical 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

366 

367 # Try the plugins, see if they have an opinion about the file. 

368 plugin = None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

369 for plugin in self.plugins.file_tracers: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

370 if not plugin._coverage_enabled: 370 ↛ 371line 370 didn't jump to line 371 because the condition on line 370 was never true1abscdtefughvijwklxmnyopzqrA

371 continue 

372 

373 try: 1abscdtefughvijwklxmnyopzqrA

374 file_tracer = plugin.file_tracer(canonical) 1abscdtefughvijwklxmnyopzqrA

375 if file_tracer is not None: 1abscdtefughvijwklxmnyopzqrA

376 file_tracer._coverage_plugin = plugin 1abscdtefughvijwklxmnyopzqrA

377 disp.trace = True 1abscdtefughvijwklxmnyopzqrA

378 disp.file_tracer = file_tracer 1abscdtefughvijwklxmnyopzqrA

379 if file_tracer.has_dynamic_source_filename(): 379 ↛ 382line 379 didn't jump to line 382 because the condition on line 379 was always true1abscdtefughvijwklxmnyopzqrA

380 disp.has_dynamic_filename = True 1abscdtefughvijwklxmnyopzqrA

381 else: 

382 disp.source_filename = canonical_filename( 

383 file_tracer.source_filename(), 

384 ) 

385 break 1abscdtefughvijwklxmnyopzqrA

386 except Exception: 

387 plugin_name = plugin._coverage_plugin_name 

388 tb = traceback.format_exc() 

389 self.warn(f"Disabling plug-in {plugin_name!r} due to an exception:\n{tb}") 

390 plugin._coverage_enabled = False 

391 continue 

392 else: 

393 # No plugin wanted it: it's Python. 

394 disp.trace = True 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

395 disp.source_filename = canonical 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

396 

397 if not disp.has_dynamic_filename: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

398 if not disp.source_filename: 398 ↛ 399line 398 didn't jump to line 399 because the condition on line 398 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

399 raise PluginError( 

400 f"Plugin {plugin!r} didn't set source_filename for '{disp.original_filename}'", 

401 ) 

402 reason = self.check_include_omit_etc(disp.source_filename, frame) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

403 if reason: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

404 nope(disp, reason) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

405 

406 return disp 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

407 

408 def check_include_omit_etc(self, filename: str, frame: FrameType | None) -> str | None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

409 """Check a file name against the include, omit, etc, rules. 

410 

411 Returns a string or None. String means, don't trace, and is the reason 

412 why. None means no reason found to not trace. 

413 

414 """ 

415 modulename = name_for_module(filename, frame) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

416 

417 # If the user specified source or include, then that's authoritative 

418 # about the outer bound of what to measure and we don't have to apply 

419 # any canned exclusions. If they didn't, then we have to exclude the 

420 # stdlib and coverage.py directories. 

421 if self.source_match or self.source_pkgs_match: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

422 extra = "" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

423 ok = False 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

424 if self.source_pkgs_match: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

425 if isinstance(modulename, str) and self.source_pkgs_match.match(modulename): 425 ↛ 426line 425 didn't jump to line 426 because the condition on line 425 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

426 ok = True 

427 if modulename in self.source_pkgs_unmatched: 

428 self.source_pkgs_unmatched.remove(modulename) 

429 else: 

430 extra = f"module {modulename!r} " 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

431 if not ok and self.source_match: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

432 if self.source_match.match(filename): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

433 ok = True 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

434 if not ok: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

435 return extra + "falls outside the --source spec" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

436 if self.third_match.match(filename) and not self.source_in_third_match.match(filename): 436 ↛ 457line 436 didn't jump to line 457 because the condition on line 436 was always true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

437 return "inside --source, but is third-party" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

438 elif self.include_match: 438 ↛ 444line 438 didn't jump to line 444 because the condition on line 438 was always true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

439 if not self.include_match.match(filename): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

440 return "falls outside the --include trees" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

441 else: 

442 # We exclude the coverage.py code itself, since a little of it 

443 # will be measured otherwise. 

444 if self.cover_match.match(filename): 

445 return "is part of coverage.py" 

446 

447 # If we aren't supposed to trace installed code, then check if this 

448 # is near the Python standard library and skip it if so. 

449 if self.pylib_match and self.pylib_match.match(filename): 

450 return "is in the stdlib" 

451 

452 # Exclude anything in the third-party installation areas. 

453 if self.third_match.match(filename): 

454 return "is a third-party module" 

455 

456 # Check the file against the omit pattern. 

457 if self.omit_match and self.omit_match.match(filename): 457 ↛ 458line 457 didn't jump to line 458 because the condition on line 457 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

458 return "is inside an --omit pattern" 

459 

460 # No point tracing a file we can't later write to SQLite. 

461 try: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

462 filename.encode("utf-8") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

463 except UnicodeEncodeError: 

464 return "non-encodable filename" 

465 

466 # No reason found to skip this file. 

467 return None 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

468 

469 def warn_conflicting_settings(self) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

470 """Warn if there are settings that conflict.""" 

471 if self.include: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

472 if self.source_dirs or self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

473 self.warn("--include is ignored because --source is set", slug="include-ignored") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kOlPx1mQnRy2oSpTz3qUrVA4BCD

474 

475 def warn_already_imported_files(self) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

476 """Warn if files have already been imported that we will be measuring.""" 

477 if self.include or self.source_dirs or self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

478 warned = set() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

479 for mod in list(sys.modules.values()): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

480 filename = getattr(mod, "__file__", None) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

481 if filename is None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

482 continue 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

483 if filename in warned: 483 ↛ 484line 483 didn't jump to line 484 because the condition on line 483 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

484 continue 

485 

486 if len(getattr(mod, "__path__", ())) > 1: 486 ↛ 488line 486 didn't jump to line 488 because the condition on line 486 was never true1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

487 # A namespace package, which confuses this code, so ignore it. 

488 continue 

489 

490 disp = self.should_trace(filename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

491 if disp.has_dynamic_filename: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

492 # A plugin with dynamic filenames: the Python file 

493 # shouldn't cause a warning, since it won't be the subject 

494 # of tracing anyway. 

495 continue 1abscdtefughvijwklxmnyopzqrA

496 if disp.trace: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

497 msg = f"Already imported a file that will be measured: {filename}" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

498 self.warn(msg, slug="already-imported") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

499 warned.add(filename) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

500 elif self.debug and self.debug.should("trace"): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

501 self.debug.write( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

502 "Didn't trace already imported file {!r}: {}".format( 

503 disp.original_filename, 

504 disp.reason, 

505 ), 

506 ) 

507 

508 def warn_unimported_source(self) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

509 """Warn about source packages that were of interest, but never traced.""" 

510 for pkg in self.source_pkgs_unmatched: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

511 self._warn_about_unmeasured_code(pkg) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

512 

513 def _warn_about_unmeasured_code(self, pkg: str) -> None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

514 """Warn about a package or module that we never traced. 

515 

516 `pkg` is a string, the name of the package or module. 

517 

518 """ 

519 mod = sys.modules.get(pkg) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

520 if mod is None: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

521 self.warn(f"Module {pkg} was never imported.", slug="module-not-imported") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

522 return 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

523 

524 if module_is_namespace(mod): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

525 # A namespace package. It's OK for this not to have been traced, 

526 # since there is no code directly in it. 

527 return 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

528 

529 if not module_has_file(mod): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

530 self.warn(f"Module {pkg} has no Python source.", slug="module-not-python") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

531 return 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

532 

533 # The module was in sys.modules, and seems like a module with code, but 

534 # we never measured it. I guess that means it was imported before 

535 # coverage even started. 

536 msg = f"Module {pkg} was previously imported, but not measured" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

537 self.warn(msg, slug="module-not-measured") 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

538 

539 def find_possibly_unexecuted_files(self) -> Iterable[tuple[str, str | None]]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

540 """Find files in the areas of interest that might be untraced. 

541 

542 Yields pairs: file path, and responsible plug-in name. 

543 """ 

544 for pkg in self.source_pkgs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

545 if pkg not in sys.modules or not module_has_file(sys.modules[pkg]): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

546 continue 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

547 pkg_file = source_for_file(cast(str, sys.modules[pkg].__file__)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

548 yield from self._find_executable_files(canonical_path(pkg_file)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

549 

550 for src in self.source_dirs: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

551 yield from self._find_executable_files(src) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

552 

553 def _find_plugin_files(self, src_dir: str) -> Iterable[tuple[str, str]]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

554 """Get executable files from the plugins.""" 

555 for plugin in self.plugins.file_tracers: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

556 for x_file in plugin.find_executable_files(src_dir): 1abscdtefughvijwklxmnyopzqrA

557 yield x_file, plugin._coverage_plugin_name 1abscdtefughvijwklxmnyopzqrA

558 

559 def _find_executable_files(self, src_dir: str) -> Iterable[tuple[str, str | None]]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

560 """Find executable files in `src_dir`. 

561 

562 Search for files in `src_dir` that can be executed because they 

563 are probably importable. Don't include ones that have been omitted 

564 by the configuration. 

565 

566 Yield the file path, and the plugin name that handles the file. 

567 

568 """ 

569 py_files = ( 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

570 (py_file, None) 

571 for py_file in find_python_files(src_dir, self.include_namespace_packages) 

572 ) 

573 plugin_files = self._find_plugin_files(src_dir) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

574 

575 for file_path, plugin_name in itertools.chain(py_files, plugin_files): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

576 file_path = canonical_filename(file_path) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

577 if self.omit_match and self.omit_match.match(file_path): 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

578 # Turns out this file was omitted, so don't pull it back 

579 # in as un-executed. 

580 continue 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

581 yield file_path, plugin_name 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

582 

583 def sys_info(self) -> Iterable[tuple[str, Any]]: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

584 """Our information for Coverage.sys_info. 

585 

586 Returns a list of (key, value) pairs. 

587 """ 

588 info = [ 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

589 ("coverage_paths", self.cover_paths), 

590 ("stdlib_paths", self.pylib_paths), 

591 ("third_party_paths", self.third_paths), 

592 ("source_in_third_party_paths", self.source_in_third_paths), 

593 ] 

594 

595 matcher_names = [ 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

596 "source_match", 

597 "source_pkgs_match", 

598 "include_match", 

599 "omit_match", 

600 "cover_match", 

601 "pylib_match", 

602 "third_match", 

603 "source_in_third_match", 

604 ] 

605 

606 for matcher_name in matcher_names: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

607 matcher = getattr(self, matcher_name) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

608 if matcher: 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

609 matcher_info = matcher.info() 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

610 else: 

611 matcher_info = "-none-" 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

612 info.append((matcher_name, matcher_info)) 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD

613 

614 return info 1aEbFsWcGdHtXeIfJuYgKhLvZiMjNw0kO5lP6x1%mQ7nR8y2'oS9pT!z3(qU#rV$A4)BCD