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

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"""Management of core choices.""" 

5 

6from __future__ import annotations 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

7 

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!

11 

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!

20 

21os = isolate_module(os) 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

22 

23IMPORT_ERROR: str = "" 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

24 

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!

28 

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!

45 

46 

47class Core: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

48 """Information about the central technology enabling execution measurement.""" 

49 

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!

56 

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) 

69 

70 _debug("in core.py") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

71 

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

82 

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!

91 

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!

96 

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!

104 

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!

111 

112 _debug(f"core.py: Using core={core_name}") 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

113 

114 self.tracer_kwargs = {} 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

115 

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!

137 

138 def __repr__(self) -> str: 1#2$3%4'5(6)7TKULVMWNXOYPZQ0R1SsagtbhucivdjwekxflymzAnBCoDEpFGqHIrJ89!

139 return f"<Core tracer_class={self.tracer_class.__name__}>"