Coverage for coverage / env.py: 100.000%

33 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"""Determine facts about the environment.""" 

5 

6from __future__ import annotations 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

7 

8import os 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

9import platform 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

10import sys 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

11from collections.abc import Iterable 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

12from typing import Any, Final 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

13 

14# debug_info() at the bottom wants to show all the globals, but not imports. 

15# Grab the global names here to know which names to not show. Nothing defined 

16# above this line will be in the output. 

17_UNINTERESTING_GLOBALS = list(globals()) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

18# These names also shouldn't be shown. 

19_UNINTERESTING_GLOBALS += ["PYBEHAVIOR", "debug_info"] 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

20 

21# Operating systems. 

22WINDOWS = sys.platform == "win32" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

23LINUX = sys.platform.startswith("linux") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

24MACOS = sys.platform == "darwin" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

25 

26# Python implementations. 

27CPYTHON = (platform.python_implementation() == "CPython") # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

28PYPY = (platform.python_implementation() == "PyPy") # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

29 

30# Python versions. We amend version_info with one more value, a zero if an 

31# official version, or 1 if built from source beyond an official version. 

32# Only use sys.version_info directly where tools like mypy need it to understand 

33# version-specfic code, otherwise use PYVERSION. 

34PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

35 

36if PYPY: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

37 # Minimum now is 7.3.16 

38 PYPYVERSION = tuple(sys.pypy_version_info) # type: ignore[attr-defined] 1234

39else: 

40 PYPYVERSION = (0,) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01

41 

42# Do we have a GIL? 

43GIL = getattr(sys, "_is_gil_enabled", lambda: True)() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

44 

45# Do we ship compiled coveragepy wheels for this version? 

46SHIPPING_WHEELS = CPYTHON and PYVERSION[:2] <= (3, 14) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

47 

48# Should we default to sys.monitoring? 

49SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

50 

51 

52# Python behavior. 

53class PYBEHAVIOR: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

54 """Flags indicating this Python's behavior.""" 

55 

56 # When leaving a with-block, do we visit the with-line exactly, 

57 # or the context managers in inner-out order? 

58 # 

59 # mwith.py: 

60 # with ( 

61 # open("/tmp/one", "w") as f2, 

62 # open("/tmp/two", "w") as f3, 

63 # open("/tmp/three", "w") as f4, 

64 # ): 

65 # print("hello 6") 

66 # 

67 # % python3.11 -m trace -t mwith.py | grep mwith 

68 # --- modulename: mwith, funcname: <module> 

69 # mwith.py(2): open("/tmp/one", "w") as f2, 

70 # mwith.py(1): with ( 

71 # mwith.py(2): open("/tmp/one", "w") as f2, 

72 # mwith.py(3): open("/tmp/two", "w") as f3, 

73 # mwith.py(1): with ( 

74 # mwith.py(3): open("/tmp/two", "w") as f3, 

75 # mwith.py(4): open("/tmp/three", "w") as f4, 

76 # mwith.py(1): with ( 

77 # mwith.py(4): open("/tmp/three", "w") as f4, 

78 # mwith.py(6): print("hello 6") 

79 # mwith.py(1): with ( 

80 # 

81 # % python3.12 -m trace -t mwith.py | grep mwith 

82 # --- modulename: mwith, funcname: <module> 

83 # mwith.py(2): open("/tmp/one", "w") as f2, 

84 # mwith.py(3): open("/tmp/two", "w") as f3, 

85 # mwith.py(4): open("/tmp/three", "w") as f4, 

86 # mwith.py(6): print("hello 6") 

87 # mwith.py(4): open("/tmp/three", "w") as f4, 

88 # mwith.py(3): open("/tmp/two", "w") as f3, 

89 # mwith.py(2): open("/tmp/one", "w") as f2, 

90 

91 exit_with_through_ctxmgr = (PYVERSION >= (3, 12, 6)) # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

92 

93 # f-strings are parsed as code, pep 701 

94 fstring_syntax = (PYVERSION >= (3, 12)) # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

95 

96 # PEP669 Low Impact Monitoring: https://peps.python.org/pep-0669/ 

97 pep669: Final[bool] = bool(getattr(sys, "monitoring", None)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

98 

99 # Where does frame.f_lasti point when yielding from a generator? 

100 # It used to point at the YIELD, in 3.13 it points at the RESUME, 

101 # then it went back to the YIELD. 

102 # https://github.com/python/cpython/issues/113728 

103 lasti_is_yield = (PYVERSION[:2] != (3, 13)) # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

104 

105 # PEP649 and PEP749: Deferred annotations 

106 deferred_annotations = (PYVERSION >= (3, 14)) # fmt: skip 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

107 

108 # Does sys.monitoring support BRANCH_RIGHT and BRANCH_LEFT? The names 

109 # were added in early 3.14 alphas, but didn't work entirely correctly until 

110 # after 3.14.0a5. 

111 branch_right_left = pep669 and (PYVERSION > (3, 14, 0, "alpha", 5, 0)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

112 

113 

114# Coverage.py specifics, about testing scenarios. See tests/testenv.py also. 

115 

116# Are we coverage-measuring ourselves? 

117METACOV = os.getenv("COVERAGE_COVERAGE") is not None 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

118 

119# Are we running our test suite? 

120# Even when running tests, you can use COVERAGE_TESTING=0 to disable the 

121# test-specific behavior like AST checking. 

122TESTING = os.getenv("COVERAGE_TESTING") == "True" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

123 

124 

125def debug_info() -> Iterable[tuple[str, Any]]: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234

126 """Return a list of (name, value) pairs for printing debug information.""" 

127 info = [ 1abcdefghijklmnopqrstuvwxyzABCDEF5GH6IJ7KL8MN9OP!QR#ST$UV%WX'YZ(01)234

128 (name, value) 

129 for name, value in globals().items() 

130 if not name.startswith("_") and name not in _UNINTERESTING_GLOBALS 

131 ] 

132 info += [ 1abcdefghijklmnopqrstuvwxyzABCDEF5GH6IJ7KL8MN9OP!QR#ST$UV%WX'YZ(01)234

133 (name, value) for name, value in PYBEHAVIOR.__dict__.items() if not name.startswith("_") 

134 ] 

135 return sorted(info) 1abcdefghijklmnopqrstuvwxyzABCDEF5GH6IJ7KL8MN9OP!QR#ST$UV%WX'YZ(01)234