Coverage for coverage / core.py: 93.496%
89 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« 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
4"""Management of core choices."""
6from __future__ import annotations 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
8import os 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
9import sys 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
10from typing import Any 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
12from coverage import env 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
13from coverage.config import CoverageConfig 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
14from coverage.disposition import FileDisposition 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
15from coverage.exceptions import ConfigError 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
16from coverage.misc import isolate_module 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
17from coverage.pytracer import PyTracer 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
18from coverage.sysmon import SysMonitor 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
19from coverage.types import TDebugCtl, TFileDisposition, Tracer, TWarnFn 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
21os = isolate_module(os) 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
23IMPORT_ERROR: str = "" 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
25try: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
26 # Use the C extension code when we can, for speed.
27 import coverage.tracer 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
29 CTRACER_FILE: str | None = getattr(coverage.tracer, "__file__", "unknown") 1#$%'()TUVWXYZ01stuvwxyACEGI
30except ImportError as imp_err: 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
31 # Couldn't import the C extension, maybe it isn't built.
32 # We still need to check the environment variable directly here,
33 # as this code runs before configuration is loaded.
34 if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
35 # During testing, we use the COVERAGE_CORE environment variable
36 # to indicate that we've fiddled with the environment to test this
37 # fallback code. If we thought we had a C tracer, but couldn't import
38 # it, then exit quickly and clearly instead of dribbling confusing
39 # errors. I'm using sys.exit here instead of an exception because an
40 # exception here causes all sorts of other noise in unittest.
41 sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n")
42 sys.exit(1)
43 IMPORT_ERROR = str(imp_err) 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
44 CTRACER_FILE = None 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
47class Core: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
48 """Information about the central technology enabling execution measurement."""
50 tracer_class: type[Tracer] 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
51 tracer_kwargs: dict[str, Any] 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
52 file_disposition_class: type[TFileDisposition] 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
53 supports_plugins: bool 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
54 packed_arcs: bool 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
55 systrace: bool 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
57 def __init__( 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
58 self,
59 *,
60 warn: TWarnFn,
61 debug: TDebugCtl | None,
62 config: CoverageConfig,
63 dynamic_contexts: bool,
64 metacov: bool,
65 ) -> None:
66 def _debug(msg: str) -> None: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
67 if debug: 67 ↛ 68line 67 didn't jump to line 68 because the condition on line 67 was never true1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
68 debug.write(msg)
70 _debug("in core.py") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
72 # Check the conditions that preclude us from using sys.monitoring.
73 reason_no_sysmon = "" 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
74 if not env.PYBEHAVIOR.pep669: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
75 reason_no_sysmon = "sys.monitoring isn't available in this version" 1#2$3%4'5(6)789!
76 elif config.branch and not env.PYBEHAVIOR.branch_right_left: 1TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
77 reason_no_sysmon = "sys.monitoring can't measure branches in this version" 1TKULVMWNXOYPZQ0R1S
78 elif dynamic_contexts: 1TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
79 reason_no_sysmon = "it doesn't yet support dynamic contexts" 1TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
80 elif any((bad := c) in config.concurrency for c in ["greenlet", "eventlet", "gevent"]): 1TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
81 reason_no_sysmon = f"it doesn't support concurrency={bad}" 1TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
83 core_name: str | None = None 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
84 if config.timid: 84 ↛ 85line 84 didn't jump to line 85 because the condition on line 84 was never true1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
85 core_name = "pytrace"
86 _debug("core.py: Using pytrace because timid=True")
87 elif core_name is None: 87 ↛ 92line 87 didn't jump to line 92 because the condition on line 87 was always true1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
88 # This could still leave core_name as None.
89 core_name = config.core 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
90 _debug(f"core.py: core from config is {core_name!r}") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
92 if core_name == "sysmon" and reason_no_sysmon: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
93 _debug(f"core.py: defaulting because sysmon not usable: {reason_no_sysmon}") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
94 warn(f"Can't use core=sysmon: {reason_no_sysmon}, using default core", slug="no-sysmon") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
95 core_name = None 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
97 if core_name is None: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
98 if env.SYSMON_DEFAULT and not reason_no_sysmon: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
99 core_name = "sysmon" 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
100 _debug("core.py: Using sysmon because SYSMON_DEFAULT is set") 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
101 else:
102 core_name = "ctrace" 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
103 _debug("core.py: Defaulting to ctrace core") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
105 if core_name == "ctrace": 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
106 if not CTRACER_FILE: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
107 if IMPORT_ERROR and env.SHIPPING_WHEELS: 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
108 warn(f"Couldn't import C tracer: {IMPORT_ERROR}", slug="no-ctracer", once=True) 1234567KLMNOPQRSagbhcidjekfl
109 core_name = "pytrace" 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
110 _debug("core.py: Falling back to pytrace because C tracer not available") 1234567KLMNOPQRSagbhcidjekflmznBoDpFqHrJ89!
112 _debug(f"core.py: Using core={core_name}") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
114 self.tracer_kwargs = {} 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
116 if core_name == "sysmon": 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
117 self.tracer_class = SysMonitor 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
118 self.tracer_kwargs["tool_id"] = 3 if metacov else 1 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
119 self.file_disposition_class = FileDisposition 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
120 self.supports_plugins = False 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
121 self.packed_arcs = False 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
122 self.systrace = False 1sagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ
123 elif core_name == "ctrace": 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
124 self.tracer_class = coverage.tracer.CTracer 1#$%'()TUVWXYZ01stuvwxyACEGI
125 self.file_disposition_class = coverage.tracer.CFileDisposition 1#$%'()TUVWXYZ01stuvwxyACEGI
126 self.supports_plugins = True 1#$%'()TUVWXYZ01stuvwxyACEGI
127 self.packed_arcs = True 1#$%'()TUVWXYZ01stuvwxyACEGI
128 self.systrace = True 1#$%'()TUVWXYZ01stuvwxyACEGI
129 elif core_name == "pytrace": 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
130 self.tracer_class = PyTracer 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
131 self.file_disposition_class = FileDisposition 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
132 self.supports_plugins = False 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
133 self.packed_arcs = False 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
134 self.systrace = True 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
135 else:
136 raise ConfigError(f"Unknown core value: {core_name!r}") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsatbucvdwexfymAnCoEpGqIr89!
138 def __repr__(self) -> str: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!
139 return f"<Core tracer_class={self.tracer_class.__name__}>"