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
« 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
4"""Determining whether files are being measured/reported or not."""
6from __future__ import annotations 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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
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
37if TYPE_CHECKING: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
38 from coverage.config import CoverageConfig
39 from coverage.plugin_support import Plugins
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]
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
60 modules_we_happen_to_have.extend([_structseq, _pypy_irc_topic]) 1abu
63os = isolate_module(os) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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`.
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.
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
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.
83 For configurability's sake, we allow __main__ modules to be matched by
84 their importable name.
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.
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
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
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
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
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
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
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`.
130 Returns:
131 filename: The filename of the module, or None.
132 path: A list (possibly empty) of directories to find submodules in.
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
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
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
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
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
183class InOrOut: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
184 """Machinery for determining what files to measure."""
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
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
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
214 self.source_pkgs_unmatched = self.source_pkgs[:] 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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
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
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
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
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
237 # The matchers for should_trace.
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
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
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
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
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
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)
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
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
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.
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.
316 Returns a FileDisposition object.
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
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
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")
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
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")
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")
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")
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
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
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
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
406 return disp 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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.
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.
414 """
415 modulename = name_for_module(filename, frame) 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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"
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"
452 # Exclude anything in the third-party installation areas.
453 if self.third_match.match(filename):
454 return "is a third-party module"
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"
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"
466 # No reason found to skip this file.
467 return None 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
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
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
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
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 )
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
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.
516 `pkg` is a string, the name of the package or module.
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
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
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
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
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.
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
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
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
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`.
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.
566 Yield the file path, and the plugin name that handles the file.
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
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
583 def sys_info(self) -> Iterable[tuple[str, Any]]: 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu
584 """Our information for Coverage.sys_info.
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 ]
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 ]
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
614 return info 1cEdFvWeGfHwXgIhJxYiKjLyZkMlNz0mO5nP6A1'oQ7pR8B2(qS9rT!C3)sU#tV$D4%abu