Major fixes and new features
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
165
venv/lib/python3.12/site-packages/coverage/patch.py
Normal file
165
venv/lib/python3.12/site-packages/coverage/patch.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
|
||||
|
||||
"""Invasive patches for coverage.py."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import atexit
|
||||
import contextlib
|
||||
import os
|
||||
import site
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, NoReturn
|
||||
|
||||
from coverage import env
|
||||
from coverage.debug import NoDebugging, DevNullDebug
|
||||
from coverage.exceptions import ConfigError, CoverageException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from coverage import Coverage
|
||||
from coverage.config import CoverageConfig
|
||||
from coverage.types import TDebugCtl
|
||||
|
||||
|
||||
def apply_patches(
|
||||
cov: Coverage,
|
||||
config: CoverageConfig,
|
||||
debug: TDebugCtl,
|
||||
*,
|
||||
make_pth_file: bool = True,
|
||||
) -> None:
|
||||
"""Apply invasive patches requested by `[run] patch=`."""
|
||||
debug = debug if debug.should("patch") else DevNullDebug()
|
||||
for patch in sorted(set(config.patch)):
|
||||
if patch == "_exit":
|
||||
_patch__exit(cov, debug)
|
||||
|
||||
elif patch == "execv":
|
||||
_patch_execv(cov, config, debug)
|
||||
|
||||
elif patch == "fork":
|
||||
_patch_fork(debug)
|
||||
|
||||
elif patch == "subprocess":
|
||||
_patch_subprocess(config, debug, make_pth_file)
|
||||
|
||||
else:
|
||||
raise ConfigError(f"Unknown patch {patch!r}")
|
||||
|
||||
|
||||
def _patch__exit(cov: Coverage, debug: TDebugCtl) -> None:
|
||||
"""Patch os._exit."""
|
||||
debug.write("Patching _exit")
|
||||
|
||||
old_exit = os._exit
|
||||
|
||||
def coverage_os_exit_patch(status: int) -> NoReturn:
|
||||
with contextlib.suppress(Exception):
|
||||
debug.write(f"Using _exit patch with {cov = }")
|
||||
with contextlib.suppress(Exception):
|
||||
cov.save()
|
||||
old_exit(status)
|
||||
|
||||
os._exit = coverage_os_exit_patch
|
||||
|
||||
|
||||
def _patch_execv(cov: Coverage, config: CoverageConfig, debug: TDebugCtl) -> None:
|
||||
"""Patch the execv family of functions."""
|
||||
if env.WINDOWS:
|
||||
raise CoverageException("patch=execv isn't supported yet on Windows.")
|
||||
|
||||
debug.write("Patching execv")
|
||||
|
||||
def make_execv_patch(fname: str, old_execv: Any) -> Any:
|
||||
def coverage_execv_patch(*args: Any, **kwargs: Any) -> Any:
|
||||
with contextlib.suppress(Exception):
|
||||
debug.write(f"Using execv patch for {fname} with {cov = }")
|
||||
with contextlib.suppress(Exception):
|
||||
cov.save()
|
||||
|
||||
if fname.endswith("e"):
|
||||
# Assume the `env` argument is passed positionally.
|
||||
new_env = args[-1]
|
||||
# Pass our configuration in the new environment.
|
||||
new_env["COVERAGE_PROCESS_CONFIG"] = config.serialize()
|
||||
if env.TESTING:
|
||||
# The subprocesses need to use the same core as the main process.
|
||||
new_env["COVERAGE_CORE"] = os.getenv("COVERAGE_CORE")
|
||||
|
||||
# When testing locally, we need to honor the pyc file location
|
||||
# or they get written to the .tox directories and pollute the
|
||||
# next run with a different core.
|
||||
if (cache_prefix := os.getenv("PYTHONPYCACHEPREFIX")) is not None:
|
||||
new_env["PYTHONPYCACHEPREFIX"] = cache_prefix
|
||||
|
||||
# Without this, it fails on PyPy and Ubuntu.
|
||||
new_env["PATH"] = os.getenv("PATH")
|
||||
old_execv(*args, **kwargs)
|
||||
|
||||
return coverage_execv_patch
|
||||
|
||||
# All the exec* and spawn* functions eventually call execv or execve.
|
||||
os.execv = make_execv_patch("execv", os.execv)
|
||||
os.execve = make_execv_patch("execve", os.execve)
|
||||
|
||||
|
||||
def _patch_fork(debug: TDebugCtl) -> None:
|
||||
"""Ensure Coverage is properly reset after a fork."""
|
||||
from coverage.control import _after_fork_in_child
|
||||
|
||||
if env.WINDOWS:
|
||||
raise CoverageException("patch=fork isn't supported yet on Windows.")
|
||||
|
||||
debug.write("Patching fork")
|
||||
os.register_at_fork(after_in_child=_after_fork_in_child)
|
||||
|
||||
|
||||
def _patch_subprocess(config: CoverageConfig, debug: TDebugCtl, make_pth_file: bool) -> None:
|
||||
"""Write .pth files and set environment vars to measure subprocesses."""
|
||||
debug.write("Patching subprocess")
|
||||
|
||||
if make_pth_file:
|
||||
pth_files = create_pth_files(debug)
|
||||
|
||||
def delete_pth_files() -> None:
|
||||
for p in pth_files:
|
||||
debug.write(f"Deleting subprocess .pth file: {str(p)!r}")
|
||||
p.unlink(missing_ok=True)
|
||||
|
||||
atexit.register(delete_pth_files)
|
||||
assert config.config_file is not None
|
||||
os.environ["COVERAGE_PROCESS_CONFIG"] = config.serialize()
|
||||
|
||||
|
||||
# Writing .pth files is not obvious. On Windows, getsitepackages() returns two
|
||||
# directories. A .pth file in the first will be run, but coverage isn't
|
||||
# importable yet. We write into all the places we can, but with defensive
|
||||
# import code.
|
||||
|
||||
PTH_CODE = """\
|
||||
try:
|
||||
import coverage
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
coverage.process_startup()
|
||||
"""
|
||||
|
||||
PTH_TEXT = f"import sys; exec({PTH_CODE!r})\n"
|
||||
|
||||
|
||||
def create_pth_files(debug: TDebugCtl = NoDebugging()) -> list[Path]:
|
||||
"""Create .pth files for measuring subprocesses."""
|
||||
pth_files = []
|
||||
for pth_dir in site.getsitepackages():
|
||||
pth_file = Path(pth_dir) / f"subcover_{os.getpid()}.pth"
|
||||
try:
|
||||
if debug.should("patch"):
|
||||
debug.write(f"Writing subprocess .pth file: {str(pth_file)!r}")
|
||||
pth_file.write_text(PTH_TEXT, encoding="utf-8")
|
||||
except OSError: # pragma: cant happen
|
||||
continue
|
||||
else:
|
||||
pth_files.append(pth_file)
|
||||
return pth_files
|
||||
Reference in New Issue
Block a user