coverage 7.11.2__cp314-cp314t-musllinux_1_2_riscv64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. coverage/__init__.py +40 -0
  2. coverage/__main__.py +12 -0
  3. coverage/annotate.py +114 -0
  4. coverage/bytecode.py +196 -0
  5. coverage/cmdline.py +1184 -0
  6. coverage/collector.py +486 -0
  7. coverage/config.py +731 -0
  8. coverage/context.py +74 -0
  9. coverage/control.py +1481 -0
  10. coverage/core.py +142 -0
  11. coverage/data.py +227 -0
  12. coverage/debug.py +669 -0
  13. coverage/disposition.py +59 -0
  14. coverage/env.py +135 -0
  15. coverage/exceptions.py +91 -0
  16. coverage/execfile.py +329 -0
  17. coverage/files.py +553 -0
  18. coverage/html.py +856 -0
  19. coverage/htmlfiles/coverage_html.js +733 -0
  20. coverage/htmlfiles/favicon_32.png +0 -0
  21. coverage/htmlfiles/index.html +164 -0
  22. coverage/htmlfiles/keybd_closed.png +0 -0
  23. coverage/htmlfiles/pyfile.html +149 -0
  24. coverage/htmlfiles/style.css +377 -0
  25. coverage/htmlfiles/style.scss +824 -0
  26. coverage/inorout.py +614 -0
  27. coverage/jsonreport.py +188 -0
  28. coverage/lcovreport.py +219 -0
  29. coverage/misc.py +373 -0
  30. coverage/multiproc.py +120 -0
  31. coverage/numbits.py +146 -0
  32. coverage/parser.py +1213 -0
  33. coverage/patch.py +166 -0
  34. coverage/phystokens.py +197 -0
  35. coverage/plugin.py +617 -0
  36. coverage/plugin_support.py +299 -0
  37. coverage/py.typed +1 -0
  38. coverage/python.py +269 -0
  39. coverage/pytracer.py +369 -0
  40. coverage/regions.py +127 -0
  41. coverage/report.py +298 -0
  42. coverage/report_core.py +117 -0
  43. coverage/results.py +471 -0
  44. coverage/sqldata.py +1153 -0
  45. coverage/sqlitedb.py +239 -0
  46. coverage/sysmon.py +482 -0
  47. coverage/templite.py +306 -0
  48. coverage/tomlconfig.py +210 -0
  49. coverage/tracer.cpython-314t-riscv64-linux-musl.so +0 -0
  50. coverage/tracer.pyi +43 -0
  51. coverage/types.py +206 -0
  52. coverage/version.py +35 -0
  53. coverage/xmlreport.py +264 -0
  54. coverage-7.11.2.dist-info/METADATA +221 -0
  55. coverage-7.11.2.dist-info/RECORD +59 -0
  56. coverage-7.11.2.dist-info/WHEEL +5 -0
  57. coverage-7.11.2.dist-info/entry_points.txt +4 -0
  58. coverage-7.11.2.dist-info/licenses/LICENSE.txt +177 -0
  59. coverage-7.11.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,59 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
+
4
+ """Simple value objects for tracking what to do with files."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING
9
+
10
+ from coverage.types import TFileDisposition
11
+
12
+ if TYPE_CHECKING:
13
+ from coverage.plugin import FileTracer
14
+
15
+
16
+ class FileDisposition:
17
+ """A simple value type for recording what to do with a file."""
18
+
19
+ original_filename: str
20
+ canonical_filename: str
21
+ source_filename: str | None
22
+ trace: bool
23
+ reason: str
24
+ file_tracer: FileTracer | None
25
+ has_dynamic_filename: bool
26
+
27
+ def __repr__(self) -> str:
28
+ return f"<FileDisposition {self.canonical_filename!r}: trace={self.trace}>"
29
+
30
+
31
+ # FileDisposition "methods": FileDisposition is a pure value object, so it can
32
+ # be implemented in either C or Python. Acting on them is done with these
33
+ # functions.
34
+
35
+
36
+ def disposition_init(cls: type[TFileDisposition], original_filename: str) -> TFileDisposition:
37
+ """Construct and initialize a new FileDisposition object."""
38
+ disp = cls()
39
+ disp.original_filename = original_filename
40
+ disp.canonical_filename = original_filename
41
+ disp.source_filename = None
42
+ disp.trace = False
43
+ disp.reason = ""
44
+ disp.file_tracer = None
45
+ disp.has_dynamic_filename = False
46
+ return disp
47
+
48
+
49
+ def disposition_debug_msg(disp: TFileDisposition) -> str:
50
+ """Make a nice debug message of what the FileDisposition is doing."""
51
+ if disp.trace:
52
+ msg = f"Tracing {disp.original_filename!r}"
53
+ if disp.original_filename != disp.source_filename:
54
+ msg += f" as {disp.source_filename!r}"
55
+ if disp.file_tracer:
56
+ msg += f": will be traced by {disp.file_tracer!r}"
57
+ else:
58
+ msg = f"Not tracing {disp.original_filename!r}: {disp.reason}"
59
+ return msg
coverage/env.py ADDED
@@ -0,0 +1,135 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
+
4
+ """Determine facts about the environment."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import platform
10
+ import sys
11
+ from collections.abc import Iterable
12
+ from typing import Any, Final
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())
18
+ # These names also shouldn't be shown.
19
+ _UNINTERESTING_GLOBALS += ["PYBEHAVIOR", "debug_info"]
20
+
21
+ # Operating systems.
22
+ WINDOWS = sys.platform == "win32"
23
+ LINUX = sys.platform.startswith("linux")
24
+ MACOS = sys.platform == "darwin"
25
+
26
+ # Python implementations.
27
+ CPYTHON = (platform.python_implementation() == "CPython") # fmt: skip
28
+ PYPY = (platform.python_implementation() == "PyPy") # fmt: skip
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.
34
+ PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
35
+
36
+ if PYPY:
37
+ # Minimum now is 7.3.16
38
+ PYPYVERSION = tuple(sys.pypy_version_info) # type: ignore[attr-defined]
39
+ else:
40
+ PYPYVERSION = (0,)
41
+
42
+ # Do we have a GIL?
43
+ GIL = getattr(sys, "_is_gil_enabled", lambda: True)()
44
+
45
+ # Do we ship compiled coveragepy wheels for this version?
46
+ SHIPPING_WHEELS = CPYTHON and PYVERSION[:2] <= (3, 14)
47
+
48
+ # Should we default to sys.monitoring?
49
+ SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14)
50
+
51
+
52
+ # Python behavior.
53
+ class PYBEHAVIOR:
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
92
+
93
+ # f-strings are parsed as code, pep 701
94
+ fstring_syntax = (PYVERSION >= (3, 12)) # fmt: skip
95
+
96
+ # PEP669 Low Impact Monitoring: https://peps.python.org/pep-0669/
97
+ pep669: Final[bool] = bool(getattr(sys, "monitoring", None))
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
104
+
105
+ # PEP649 and PEP749: Deferred annotations
106
+ deferred_annotations = (PYVERSION >= (3, 14)) # fmt: skip
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))
112
+
113
+
114
+ # Coverage.py specifics, about testing scenarios. See tests/testenv.py also.
115
+
116
+ # Are we coverage-measuring ourselves?
117
+ METACOV = os.getenv("COVERAGE_COVERAGE") is not None
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.
122
+ TESTING = os.getenv("COVERAGE_TESTING") == "True"
123
+
124
+
125
+ def debug_info() -> Iterable[tuple[str, Any]]:
126
+ """Return a list of (name, value) pairs for printing debug information."""
127
+ info = [
128
+ (name, value)
129
+ for name, value in globals().items()
130
+ if not name.startswith("_") and name not in _UNINTERESTING_GLOBALS
131
+ ]
132
+ info += [
133
+ (name, value) for name, value in PYBEHAVIOR.__dict__.items() if not name.startswith("_")
134
+ ]
135
+ return sorted(info)
coverage/exceptions.py ADDED
@@ -0,0 +1,91 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
+
4
+ """Exceptions coverage.py can raise."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any
9
+
10
+
11
+ class CoverageException(Exception):
12
+ """The base class of all exceptions raised by Coverage.py."""
13
+
14
+ def __init__(
15
+ self,
16
+ *args: Any,
17
+ slug: str | None = None,
18
+ skip_tests: bool = False,
19
+ ) -> None:
20
+ """Create an exception.
21
+
22
+ Args:
23
+ slug: A short string identifying the exception, will be used for
24
+ linking to documentation.
25
+ skip_tests: If True, raising this exception will skip the test it
26
+ is raised in. This is used for shutting off large numbers of
27
+ tests that we know will not succeed because of a configuration
28
+ mismatch.
29
+ """
30
+
31
+ super().__init__(*args)
32
+ self.slug = slug
33
+ self.skip_tests = skip_tests
34
+
35
+
36
+ class ConfigError(CoverageException):
37
+ """A problem with a config file, or a value in one."""
38
+
39
+ pass
40
+
41
+
42
+ class DataError(CoverageException):
43
+ """An error in using a data file."""
44
+
45
+ pass
46
+
47
+
48
+ class NoDataError(CoverageException):
49
+ """We didn't have data to work with."""
50
+
51
+ pass
52
+
53
+
54
+ class NoSource(CoverageException):
55
+ """We couldn't find the source for a module."""
56
+
57
+ pass
58
+
59
+
60
+ class NoCode(NoSource):
61
+ """We couldn't find any code at all."""
62
+
63
+ pass
64
+
65
+
66
+ class NotPython(CoverageException):
67
+ """A source file turned out not to be parsable Python."""
68
+
69
+ pass
70
+
71
+
72
+ class PluginError(CoverageException):
73
+ """A plugin misbehaved."""
74
+
75
+ pass
76
+
77
+
78
+ class _ExceptionDuringRun(CoverageException):
79
+ """An exception happened while running customer code.
80
+
81
+ Construct it with three arguments, the values from `sys.exc_info`.
82
+
83
+ """
84
+
85
+ pass
86
+
87
+
88
+ class CoverageWarning(Warning):
89
+ """A warning from Coverage.py."""
90
+
91
+ pass
coverage/execfile.py ADDED
@@ -0,0 +1,329 @@
1
+ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
+
4
+ """Execute files of Python code."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import importlib.machinery
9
+ import importlib.util
10
+ import inspect
11
+ import marshal
12
+ import os
13
+ import struct
14
+ import sys
15
+ from importlib.machinery import ModuleSpec
16
+ from types import CodeType, ModuleType
17
+ from typing import Any
18
+
19
+ from coverage.exceptions import CoverageException, NoCode, NoSource, _ExceptionDuringRun
20
+ from coverage.files import canonical_filename, python_reported_file
21
+ from coverage.misc import isolate_module
22
+ from coverage.python import get_python_source
23
+
24
+ os = isolate_module(os)
25
+
26
+
27
+ PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
28
+
29
+
30
+ class DummyLoader:
31
+ """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
32
+
33
+ Currently only implements the .fullname attribute
34
+ """
35
+
36
+ def __init__(self, fullname: str, *_args: Any) -> None:
37
+ self.fullname = fullname
38
+
39
+
40
+ def find_module(
41
+ modulename: str,
42
+ ) -> tuple[str | None, str, ModuleSpec]:
43
+ """Find the module named `modulename`.
44
+
45
+ Returns the file path of the module, the name of the enclosing
46
+ package, and the spec.
47
+ """
48
+ try:
49
+ spec = importlib.util.find_spec(modulename)
50
+ except ImportError as err:
51
+ raise NoSource(str(err)) from err
52
+ if not spec:
53
+ raise NoSource(f"No module named {modulename!r}")
54
+ pathname = spec.origin
55
+ packagename = spec.name
56
+ if spec.submodule_search_locations:
57
+ mod_main = modulename + ".__main__"
58
+ spec = importlib.util.find_spec(mod_main)
59
+ if not spec:
60
+ raise NoSource(
61
+ f"No module named {mod_main}; "
62
+ + f"{modulename!r} is a package and cannot be directly executed",
63
+ )
64
+ pathname = spec.origin
65
+ packagename = spec.name
66
+ packagename = packagename.rpartition(".")[0]
67
+ return pathname, packagename, spec
68
+
69
+
70
+ class PyRunner:
71
+ """Multi-stage execution of Python code.
72
+
73
+ This is meant to emulate real Python execution as closely as possible.
74
+
75
+ """
76
+
77
+ def __init__(self, args: list[str], as_module: bool = False) -> None:
78
+ self.args = args
79
+ self.as_module = as_module
80
+
81
+ self.arg0 = args[0]
82
+ self.package: str | None = None
83
+ self.modulename: str | None = None
84
+ self.pathname: str | None = None
85
+ self.loader: DummyLoader | None = None
86
+ self.spec: ModuleSpec | None = None
87
+
88
+ def prepare(self) -> None:
89
+ """Set sys.path properly.
90
+
91
+ This needs to happen before any importing, and without importing anything.
92
+ """
93
+ path0: str | None
94
+ if getattr(sys.flags, "safe_path", False):
95
+ # See https://docs.python.org/3/using/cmdline.html#cmdoption-P
96
+ path0 = None
97
+ elif self.as_module:
98
+ path0 = os.getcwd()
99
+ elif os.path.isdir(self.arg0):
100
+ # Running a directory means running the __main__.py file in that
101
+ # directory.
102
+ path0 = self.arg0
103
+ else:
104
+ path0 = os.path.abspath(os.path.dirname(self.arg0))
105
+
106
+ if os.path.isdir(sys.path[0]):
107
+ # sys.path fakery. If we are being run as a command, then sys.path[0]
108
+ # is the directory of the "coverage" script. If this is so, replace
109
+ # sys.path[0] with the directory of the file we're running, or the
110
+ # current directory when running modules. If it isn't so, then we
111
+ # don't know what's going on, and just leave it alone.
112
+ top_file = inspect.stack()[-1][0].f_code.co_filename
113
+ sys_path_0_abs = os.path.abspath(sys.path[0])
114
+ top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
115
+ sys_path_0_abs = canonical_filename(sys_path_0_abs)
116
+ top_file_dir_abs = canonical_filename(top_file_dir_abs)
117
+ if sys_path_0_abs != top_file_dir_abs:
118
+ path0 = None
119
+
120
+ else:
121
+ # sys.path[0] is a file. Is the next entry the directory containing
122
+ # that file?
123
+ if sys.path[1] == os.path.dirname(sys.path[0]):
124
+ # Can it be right to always remove that?
125
+ del sys.path[1]
126
+
127
+ if path0 is not None:
128
+ sys.path[0] = python_reported_file(path0)
129
+
130
+ def _prepare2(self) -> None:
131
+ """Do more preparation to run Python code.
132
+
133
+ Includes finding the module to run and adjusting sys.argv[0].
134
+ This method is allowed to import code.
135
+
136
+ """
137
+ if self.as_module:
138
+ self.modulename = self.arg0
139
+ pathname, self.package, self.spec = find_module(self.modulename)
140
+ if self.spec is not None:
141
+ self.modulename = self.spec.name
142
+ self.loader = DummyLoader(self.modulename)
143
+ assert pathname is not None
144
+ self.pathname = os.path.abspath(pathname)
145
+ self.args[0] = self.arg0 = self.pathname
146
+ elif os.path.isdir(self.arg0):
147
+ # Running a directory means running the __main__.py file in that
148
+ # directory.
149
+ for ext in [".py", ".pyc", ".pyo"]:
150
+ try_filename = os.path.join(self.arg0, f"__main__{ext}")
151
+ # 3.8.10 changed how files are reported when running a
152
+ # directory.
153
+ try_filename = os.path.abspath(try_filename)
154
+ if os.path.exists(try_filename):
155
+ self.arg0 = try_filename
156
+ break
157
+ else:
158
+ raise NoSource(f"Can't find '__main__' module in '{self.arg0}'")
159
+
160
+ # Make a spec. I don't know if this is the right way to do it.
161
+ try_filename = python_reported_file(try_filename)
162
+ self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
163
+ self.spec.has_location = True
164
+ self.package = ""
165
+ self.loader = DummyLoader("__main__")
166
+ else:
167
+ self.loader = DummyLoader("__main__")
168
+
169
+ self.arg0 = python_reported_file(self.arg0)
170
+
171
+ def run(self) -> None:
172
+ """Run the Python code!"""
173
+
174
+ self._prepare2()
175
+
176
+ # Create a module to serve as __main__
177
+ main_mod = ModuleType("__main__")
178
+
179
+ from_pyc = self.arg0.endswith((".pyc", ".pyo"))
180
+ main_mod.__file__ = self.arg0
181
+ if from_pyc:
182
+ main_mod.__file__ = main_mod.__file__[:-1]
183
+ if self.package is not None:
184
+ main_mod.__package__ = self.package
185
+ main_mod.__loader__ = self.loader # type: ignore[assignment]
186
+ if self.spec is not None:
187
+ main_mod.__spec__ = self.spec
188
+
189
+ main_mod.__builtins__ = sys.modules["builtins"] # type: ignore[attr-defined]
190
+
191
+ sys.modules["__main__"] = main_mod
192
+
193
+ # Set sys.argv properly.
194
+ sys.argv = self.args
195
+
196
+ try:
197
+ # Make a code object somehow.
198
+ if from_pyc:
199
+ code = make_code_from_pyc(self.arg0)
200
+ else:
201
+ code = make_code_from_py(self.arg0)
202
+ except CoverageException:
203
+ raise
204
+ except Exception as exc:
205
+ msg = f"Couldn't run '{self.arg0}' as Python code: {exc.__class__.__name__}: {exc}"
206
+ raise CoverageException(msg) from exc
207
+
208
+ # Execute the code object.
209
+ # Return to the original directory in case the test code exits in
210
+ # a non-existent directory.
211
+ cwd = os.getcwd()
212
+ try:
213
+ exec(code, main_mod.__dict__)
214
+ except SystemExit: # pylint: disable=try-except-raise
215
+ # The user called sys.exit(). Just pass it along to the upper
216
+ # layers, where it will be handled.
217
+ raise
218
+ except Exception:
219
+ # Something went wrong while executing the user code.
220
+ # Get the exc_info, and pack them into an exception that we can
221
+ # throw up to the outer loop. We peel one layer off the traceback
222
+ # so that the coverage.py code doesn't appear in the final printed
223
+ # traceback.
224
+ typ, err, tb = sys.exc_info()
225
+ assert typ is not None
226
+ assert err is not None
227
+ assert tb is not None
228
+
229
+ # PyPy3 weirdness. If I don't access __context__, then somehow it
230
+ # is non-None when the exception is reported at the upper layer,
231
+ # and a nested exception is shown to the user. This getattr fixes
232
+ # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
233
+ getattr(err, "__context__", None)
234
+
235
+ # Call the excepthook.
236
+ try:
237
+ assert err.__traceback__ is not None
238
+ err.__traceback__ = err.__traceback__.tb_next
239
+ sys.excepthook(typ, err, tb.tb_next)
240
+ except SystemExit: # pylint: disable=try-except-raise
241
+ raise
242
+ except Exception as exc:
243
+ # Getting the output right in the case of excepthook
244
+ # shenanigans is kind of involved.
245
+ sys.stderr.write("Error in sys.excepthook:\n")
246
+ typ2, err2, tb2 = sys.exc_info()
247
+ assert typ2 is not None
248
+ assert err2 is not None
249
+ assert tb2 is not None
250
+ err2.__suppress_context__ = True
251
+ assert err2.__traceback__ is not None
252
+ err2.__traceback__ = err2.__traceback__.tb_next
253
+ sys.__excepthook__(typ2, err2, tb2.tb_next)
254
+ sys.stderr.write("\nOriginal exception was:\n")
255
+ raise _ExceptionDuringRun(typ, err, tb.tb_next) from exc
256
+ else:
257
+ sys.exit(1)
258
+ finally:
259
+ os.chdir(cwd)
260
+
261
+
262
+ def run_python_module(args: list[str]) -> None:
263
+ """Run a Python module, as though with ``python -m name args...``.
264
+
265
+ `args` is the argument array to present as sys.argv, including the first
266
+ element naming the module being executed.
267
+
268
+ This is a helper for tests, to encapsulate how to use PyRunner.
269
+
270
+ """
271
+ runner = PyRunner(args, as_module=True)
272
+ runner.prepare()
273
+ runner.run()
274
+
275
+
276
+ def run_python_file(args: list[str]) -> None:
277
+ """Run a Python file as if it were the main program on the command line.
278
+
279
+ `args` is the argument array to present as sys.argv, including the first
280
+ element naming the file being executed. `package` is the name of the
281
+ enclosing package, if any.
282
+
283
+ This is a helper for tests, to encapsulate how to use PyRunner.
284
+
285
+ """
286
+ runner = PyRunner(args, as_module=False)
287
+ runner.prepare()
288
+ runner.run()
289
+
290
+
291
+ def make_code_from_py(filename: str) -> CodeType:
292
+ """Get source from `filename` and make a code object of it."""
293
+ try:
294
+ source = get_python_source(filename)
295
+ except (OSError, NoSource) as exc:
296
+ raise NoSource(f"No file to run: '{filename}'") from exc
297
+
298
+ code = compile(source, filename, mode="exec", dont_inherit=True)
299
+ return code
300
+
301
+
302
+ def make_code_from_pyc(filename: str) -> CodeType:
303
+ """Get a code object from a .pyc file."""
304
+ try:
305
+ fpyc = open(filename, "rb")
306
+ except OSError as exc:
307
+ raise NoCode(f"No file to run: '{filename}'") from exc
308
+
309
+ with fpyc:
310
+ # First four bytes are a version-specific magic number. It has to
311
+ # match or we won't run the file.
312
+ magic = fpyc.read(4)
313
+ if magic != PYC_MAGIC_NUMBER:
314
+ raise NoCode(f"Bad magic number in .pyc file: {magic!r} != {PYC_MAGIC_NUMBER!r}")
315
+
316
+ flags = struct.unpack("<L", fpyc.read(4))[0]
317
+ hash_based = flags & 0x01
318
+ if hash_based:
319
+ fpyc.read(8) # Skip the hash.
320
+ else:
321
+ # Skip the junk in the header that we don't need.
322
+ fpyc.read(4) # Skip the moddate.
323
+ fpyc.read(4) # Skip the size.
324
+
325
+ # The rest of the file is the code object we want.
326
+ code = marshal.load(fpyc)
327
+ assert isinstance(code, CodeType)
328
+
329
+ return code