Coverage for coverage / inorout.py: 77.551%

320 statements  

« prev     ^ index     » next       coverage.py v7.12.1a0.dev1, created at 2025-11-29 20:34 +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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

7 

8import importlib.util 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

9import inspect 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

10import itertools 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

11import os 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

12import os.path 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

13import platform 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

14import re 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

15import sys 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

16import sysconfig 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

17import traceback 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

18from collections.abc import Iterable 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

19from types import FrameType, ModuleType 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

20from typing import TYPE_CHECKING, Any, cast 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

21 

22from coverage import env 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

23from coverage.disposition import FileDisposition, disposition_init 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

24from coverage.exceptions import ConfigError, CoverageException, PluginError 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

25from coverage.files import ( 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

34from coverage.python import source_for_file, source_for_morf 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

35from coverage.types import TDebugCtl, TFileDisposition, TMorf, TWarnFn 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

36 

37if TYPE_CHECKING: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

38 from coverage.config import CoverageConfig 

39 from coverage.plugin_support import Plugins 

40 

41 

42modules_we_happen_to_have: list[ModuleType] = [ 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

43 inspect, 

44 itertools, 

45 os, 

46 platform, 

47 re, 

48 sysconfig, 

49 traceback, 

50] 

51 

52if env.PYPY: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 1abu

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

59 

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

61 

62 

63os = isolate_module(os) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

64 

65 

66def canonical_path(morf: TMorf, directory: bool = False) -> str: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

75 if morf_path.endswith("__init__.py") or directory: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

76 morf_path = os.path.split(morf_path)[0] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

77 return morf_path 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

78 

79 

80def name_for_module(filename: str, frame: FrameType | None) -> str | None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 {} 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

93 dunder_name: str | None = module_globals.get("__name__", None) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

97 return dunder_name 

98 

99 spec = module_globals.get("__spec__", None) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

100 if spec: 100 ↛ 101line 100 didn't jump to line 101 because the condition on line 100 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

108 if inspectedname is not None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

109 return inspectedname 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

110 else: 

111 return dunder_name 1cEdFeGfHgIhJiKjLkMlNmO5nP6oQ7pR8qS9rT!sU#tV$ab

112 

113 

114def module_is_namespace(mod: ModuleType) -> bool: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

116 return hasattr(mod, "__path__") and getattr(mod, "__file__", None) is None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

117 

118 

119def module_has_file(mod: ModuleType) -> bool: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

121 mod__file__ = getattr(mod, "__file__", None) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

122 if mod__file__ is None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

123 return False 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

124 return os.path.exists(mod__file__) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

125 

126 

127def file_and_path_for_module(modulename: str) -> tuple[str | None, list[str]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

136 path = [] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

137 try: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

138 spec = importlib.util.find_spec(modulename) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

139 except Exception: 

140 pass 

141 else: 

142 if spec is not None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

143 filename = spec.origin 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

144 path = list(spec.submodule_search_locations or ()) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

145 return filename, path 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

146 

147 

148def add_stdlib_paths(paths: set[str]) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

156 if hasattr(m, "__file__"): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

157 paths.add(canonical_path(m, directory=True)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

158 

159 

160def add_third_party_paths(paths: set[str]) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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()) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

164 

165 for scheme in scheme_names: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

167 better_scheme = "pypy_posix" if scheme == "pypy" else scheme 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

168 if os.name in better_scheme.split("_"): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

169 config_paths = sysconfig.get_paths(scheme) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

170 for path_name in ["platlib", "purelib", "scripts"]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

171 paths.add(config_paths[path_name]) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

172 

173 

174def add_coverage_paths(paths: set[str]) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

176 cover_path = canonical_path(__file__, directory=True) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

177 paths.add(cover_path) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

178 if env.TESTING: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

179 # Don't include our own test code. 

180 paths.add(os.path.join(cover_path, "tests")) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

181 

182 

183class InOrOut: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

185 

186 def __init__( 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

187 self, 

188 config: CoverageConfig, 

189 warn: TWarnFn, 

190 debug: TDebugCtl | None, 

191 include_namespace_packages: bool, 

192 ) -> None: 

193 self.warn = warn 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

194 self.debug = debug 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

195 self.include_namespace_packages = include_namespace_packages 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

196 

197 self.source_pkgs: list[str] = [] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

198 self.source_pkgs.extend(config.source_pkgs) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

199 self.source_dirs: list[str] = [] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

200 self.source_dirs.extend(config.source_dirs) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

201 for src in config.source or []: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

202 if os.path.isdir(src): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

203 self.source_dirs.append(src) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

204 else: 

205 self.source_pkgs.append(src) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

206 

207 # Canonicalize everything in `source_dirs`. 

208 # Also confirm that they actually are directories. 

209 for i, src in enumerate(self.source_dirs): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

210 if not os.path.isdir(src): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

212 self.source_dirs[i] = canonical_filename(src) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

213 

214 self.source_pkgs_unmatched = self.source_pkgs[:] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

215 

216 self.include = prep_patterns(config.run_include) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

217 self.omit = prep_patterns(config.run_omit) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

218 

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

220 self.pylib_paths: set[str] = set() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

221 if not config.cover_pylib: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

222 add_stdlib_paths(self.pylib_paths) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

227 add_coverage_paths(self.cover_paths) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

228 

229 # Find where third-party packages are installed. 

230 self.third_paths: set[str] = set() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

231 add_third_party_paths(self.third_paths) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

232 

233 def _debug(msg: str) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

234 if self.debug: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

235 self.debug.write(msg) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1oQ7pR8B2qS9rT!C3sU#tV$D4abu

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)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

241 

242 # Create the matchers we need for should_trace 

243 self.source_match = None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

244 self.source_pkgs_match = None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

245 self.pylib_match = None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

246 self.include_match = self.omit_match = None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

247 

248 if self.source_dirs or self.source_pkgs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

249 against = [] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

250 if self.source_dirs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

251 self.source_match = TreeMatcher(self.source_dirs, "source") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

252 against.append(f"trees {self.source_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

253 if self.source_pkgs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

254 self.source_pkgs_match = ModuleMatcher(self.source_pkgs, "source_pkgs") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

255 against.append(f"modules {self.source_pkgs_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

256 _debug("Source matching against " + " and ".join(against)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

257 else: 

258 if self.pylib_paths: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

259 self.pylib_match = TreeMatcher(self.pylib_paths, "pylib") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

260 _debug(f"Python stdlib matching: {self.pylib_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

261 if self.include: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

262 self.include_match = GlobMatcher(self.include, "include") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

263 _debug(f"Include matching: {self.include_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

264 if self.omit: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

265 self.omit_match = GlobMatcher(self.omit, "omit") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

266 _debug(f"Omit matching: {self.omit_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

267 

268 self.cover_match = TreeMatcher(self.cover_paths, "coverage") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

269 _debug(f"Coverage code matching: {self.cover_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

270 

271 self.third_match = TreeMatcher(self.third_paths, "third") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

272 _debug(f"Third-party lib matching: {self.third_match!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

278 with sys_modules_saved(): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

279 for pkg in self.source_pkgs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

280 try: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

281 modfile, path = file_and_path_for_module(pkg) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

282 _debug(f"Imported source package {pkg!r} as {modfile!r}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

283 except CoverageException as exc: 

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

285 continue 

286 if modfile: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

287 if self.third_match.match(modfile): 287 ↛ 288line 287 didn't jump to line 288 because the condition on line 287 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

288 _debug( 

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))) 

292 else: 

293 for pathdir in path: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

294 if self.third_match.match(pathdir): 294 ↛ 295line 294 didn't jump to line 295 because the condition on line 294 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mOnPA1oQpRB2qSrTC3sUtVD4abu

295 _debug( 

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

297 ) 

298 self.source_in_third_paths.add(pathdir) 

299 

300 for src in self.source_dirs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

301 if self.third_match.match(src): 301 ↛ 302line 301 didn't jump to line 302 because the condition on line 301 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

305 _debug(f"Source in third-party matching: {self.source_in_third_match}") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

306 

307 self.plugins: Plugins 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

308 self.disp_class: type[TFileDisposition] = FileDisposition 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

309 

310 def should_trace(self, filename: str, frame: FrameType | None = None) -> TFileDisposition: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

320 disp = disposition_init(self.disp_class, filename) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

321 

322 def nope(disp: TFileDisposition, reason: str) -> TFileDisposition: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

324 disp.trace = False 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

325 disp.reason = reason 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

326 return disp 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

327 

328 if original_filename.startswith("<"): 328 ↛ 329line 328 didn't jump to line 329 because the condition on line 328 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

365 disp.canonical_filename = canonical 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

366 

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

368 plugin = None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

369 for plugin in self.plugins.file_tracers: 369 ↛ 370line 369 didn't jump to line 370 because the loop on line 369 never started1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

370 if not plugin._coverage_enabled: 

371 continue 

372 

373 try: 

374 file_tracer = plugin.file_tracer(canonical) 

375 if file_tracer is not None: 

376 file_tracer._coverage_plugin = plugin 

377 disp.trace = True 

378 disp.file_tracer = file_tracer 

379 if file_tracer.has_dynamic_source_filename(): 

380 disp.has_dynamic_filename = True 

381 else: 

382 disp.source_filename = canonical_filename( 

383 file_tracer.source_filename(), 

384 ) 

385 break 

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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

395 disp.source_filename = canonical 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

396 

397 if not disp.has_dynamic_filename: 397 ↛ 406line 397 didn't jump to line 406 because the condition on line 397 was always true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

398 if not disp.source_filename: 398 ↛ 399line 398 didn't jump to line 399 because the condition on line 398 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

403 if reason: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

404 nope(disp, reason) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

405 

406 return disp 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

407 

408 def check_include_omit_etc(self, filename: str, frame: FrameType | None) -> str | None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

422 extra = "" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

423 ok = False 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

424 if self.source_pkgs_match: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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} " 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

431 if not ok and self.source_match: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

432 if self.source_match.match(filename): 432 ↛ 433line 432 didn't jump to line 433 because the condition on line 432 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

433 ok = True 

434 if not ok: 434 ↛ 436line 434 didn't jump to line 436 because the condition on line 434 was always true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

435 return extra + "falls outside the --source spec" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

436 if self.third_match.match(filename) and not self.source_in_third_match.match(filename): 

437 return "inside --source, but is third-party" 

438 elif self.include_match: 438 ↛ 444line 438 didn't jump to line 444 because the condition on line 438 was always true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

439 if not self.include_match.match(filename): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

440 return "falls outside the --include trees" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

458 return "is inside an --omit pattern" 

459 

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

461 try: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

462 filename.encode("utf-8") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

463 except UnicodeEncodeError: 

464 return "non-encodable filename" 

465 

466 # No reason found to skip this file. 

467 return None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

468 

469 def warn_conflicting_settings(self) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

471 if self.include: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

472 if self.source_dirs or self.source_pkgs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

474 

475 def warn_already_imported_files(self) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

478 warned = set() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

479 for mod in list(sys.modules.values()): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

480 filename = getattr(mod, "__file__", None) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

481 if filename is None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

482 continue 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

483 if filename in warned: 483 ↛ 484line 483 didn't jump to line 484 because the condition on line 483 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

488 continue 

489 

490 disp = self.should_trace(filename) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

491 if disp.has_dynamic_filename: 491 ↛ 495line 491 didn't jump to line 495 because the condition on line 491 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 

496 if disp.trace: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

497 msg = f"Already imported a file that will be measured: {filename}" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

498 self.warn(msg, slug="already-imported") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

499 warned.add(filename) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

500 elif self.debug and self.debug.should("trace"): 500 ↛ 501line 500 didn't jump to line 501 because the condition on line 500 was never true1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

501 self.debug.write( 

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

510 for pkg in self.source_pkgs_unmatched: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

511 self._warn_about_unmeasured_code(pkg) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

512 

513 def _warn_about_unmeasured_code(self, pkg: str) -> None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

520 if mod is None: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

521 self.warn(f"Module {pkg} was never imported.", slug="module-not-imported") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

522 return 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

523 

524 if module_is_namespace(mod): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mOnPA1oQpRB2qSrTC3sUtVD4%abu

528 

529 if not module_has_file(mod): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

530 self.warn(f"Module {pkg} has no Python source.", slug="module-not-python") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

531 return 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

537 self.warn(msg, slug="module-not-measured") 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

538 

539 def find_possibly_unexecuted_files(self) -> Iterable[tuple[str, str | None]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

545 if pkg not in sys.modules or not module_has_file(sys.modules[pkg]): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

546 continue 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

547 pkg_file = source_for_file(cast(str, sys.modules[pkg].__file__)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

548 yield from self._find_executable_files(canonical_path(pkg_file)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

549 

550 for src in self.source_dirs: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

551 yield from self._find_executable_files(src) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

552 

553 def _find_plugin_files(self, src_dir: str) -> Iterable[tuple[str, str]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

555 for plugin in self.plugins.file_tracers: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

557 yield x_file, plugin._coverage_plugin_name 1cdvefwghxijyklzmnAopBqrCstD

558 

559 def _find_executable_files(self, src_dir: str) -> Iterable[tuple[str, str | None]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 = ( 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

574 

575 for file_path, plugin_name in itertools.chain(py_files, plugin_files): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

576 file_path = canonical_filename(file_path) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

577 if self.omit_match and self.omit_match.match(file_path): 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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

579 # in as un-executed. 

580 continue 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

581 yield file_path, plugin_name 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

582 

583 def sys_info(self) -> Iterable[tuple[str, Any]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

584 """Our information for Coverage.sys_info. 

585 

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

587 """ 

588 info = [ 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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 = [ 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

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: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

607 matcher = getattr(self, matcher_name) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

608 if matcher: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

609 matcher_info = matcher.info() 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

610 else: 

611 matcher_info = "-none-" 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

612 info.append((matcher_name, matcher_info)) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu

613 

614 return info 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu