from __future__ import annotations
import atexit
import contextvars
import _thread
import itertools
import mmap
import os
import sys
import time
import zlib
PLAN_VERSION = 1
EVIDENCE_VERSION = 1
CONTEXT_ENV = "SUPERCOV_CONTEXT"
PLAN_ENV = "SUPERCOV_PYTHON_PLAN"
EVIDENCE_DIR_ENV = "SUPERCOV_PYTHON_EVIDENCE_DIR"
RUN_ID_ENV = "SUPERCOV_RUN_ID"
SOURCE_ROOT_ENV = "SUPERCOV_PYTHON_SOURCE_ROOT"
WORKER_ENV = "SUPERCOV_PYTHON_WORKER"
TIMING = bool(os.environ.get("SUPERCOV_PYTHON_TIMING"))
STUB = os.environ.get("SUPERCOV_PYTHON_STUB", "")
_timing = {"switch": [0, 0.0], "harvest": [0, 0.0]}
TRANSPORT_MAGIC = b"SCVPYTH1"
TRANSPORT_VERSION = 2
TRANSPORT_HEADER_SIZE = 64
TRANSPORT_RECORD_HEADER_SIZE = 16
TRANSPORT_INITIAL_CAPACITY = 1024 * 1024
TRANSPORT_MAP_AFTER_BYTES = 64 * 1024
TRANSPORT_MAX_CAPACITY = 512 * 1024 * 1024
TRANSPORT_MAX_RECORD_SIZE = 4 * 1024 * 1024
MAX_OPEN_EVALUATIONS = 64
LAYOUT_NAME = "layout.json"
SLOT_SUFFIX = ".slot"
SHARED_SLOT_SUFFIX = ".shm"
_posixshmem = None if sys.platform == "darwin" else False
HARVEST_CHUNK = 40000
_monitoring = getattr(sys, "monitoring", None)
def _now_ms() -> int:
return int(time.time() * 1000)
_JSON_ESCAPES = {'"': '\\"', "\\": "\\\\", "\n": "\\n", "\r": "\\r", "\t": "\\t", "\b": "\\b", "\f": "\\f"}
def _json_string(text: str) -> str:
if text.isascii() and text.isprintable() and '"' not in text and "\\" not in text:
return '"' + text + '"'
out = ['"']
for character in text:
escaped = _JSON_ESCAPES.get(character)
if escaped is not None:
out.append(escaped)
elif " " <= character <= "~":
out.append(character)
else:
code = ord(character)
if code > 0xFFFF:
code -= 0x10000
out.append("\\u%04x\\u%04x" % (0xD800 | (code >> 10), 0xDC00 | (code & 0x3FF)))
else:
out.append("\\u%04x" % code)
out.append('"')
return "".join(out)
def _json_text(value) -> str:
kind = type(value)
if kind is str:
return _json_string(value)
if kind is int:
return str(value)
if kind is bool:
return "true" if value else "false"
if value is None:
return "null"
if kind is dict:
return "{" + ",".join(_json_string(str(key)) + ":" + _json_text(item) for key, item in value.items()) + "}"
if kind is list or kind is tuple:
return "[" + ",".join(map(_json_text, value)) + "]"
if isinstance(value, bool):
return "true" if value else "false"
if isinstance(value, str):
return _json_string(str(value))
if isinstance(value, int):
return str(int(value))
raise TypeError(f"a Supercov record cannot hold {kind.__name__}")
class _ParseContext:
strict = True
object_hook = None
object_pairs_hook = None
parse_float = float
parse_int = int
@staticmethod
def parse_constant(name):
raise ValueError(f"not JSON: {name}")
def _flat_object(text: str):
if "\\" in text or not text.startswith("{") or not text.endswith("}"):
return None
result: dict = {}
if text == "{}":
return result
index, end = 1, len(text)
while True:
if text[index : index + 1] != '"':
return None
close = text.find('"', index + 1)
if close < 0:
return None
key, index = text[index + 1 : close], close + 1
if text[index : index + 1] != ":":
return None
index += 1
if text[index : index + 1] == '"':
close = text.find('"', index + 1)
if close < 0:
return None
value, index = text[index + 1 : close], close + 1
else:
start = index
if text[index : index + 1] == "-":
index += 1
while index < end and "0" <= text[index] <= "9":
index += 1
if index == start or text[start:index] == "-":
return None
value = int(text[start:index])
result[key] = value
separator = text[index : index + 1]
index += 1
if separator == "}" and index == end:
return result
if separator != ",":
return None
def _json_load(text: str):
flat = _flat_object(text)
if flat is not None:
return flat
try:
from _json import make_scanner
except ImportError: import json
return json.loads(text)
try:
value, end = make_scanner(_ParseContext())(text, 0)
except StopIteration:
raise ValueError("not JSON") from None
if end != len(text):
raise ValueError("trailing data after JSON")
return value
def _digits(mask: int, width: int) -> str:
out = []
for _ in range(width):
out.append("012"[mask % 3])
mask //= 3
return "".join(out)
class _Decision:
__slots__ = ("id", "width", "outcome_true", "outcome_false", "logical")
def __init__(self, plan: dict, logical: list) -> None:
self.id = plan["id"]
self.width = len(plan["conditions"])
self.outcome_true = plan["outcomeTrue"]
self.outcome_false = plan["outcomeFalse"]
self.logical = logical
def implied(self, digits: str, outcome: bool, into: list) -> None:
into.append(self.outcome_true if outcome else self.outcome_false)
for logical in self.logical:
if any(digits[index] != "0" for index in logical["operandLeaves"]):
into.append(logical["evaluated"])
elif any(digits[index] != "0" for index in logical["previousLeaves"]):
into.append(logical["shortCircuit"])
class _FileOutput:
def __init__(self, descriptor):
self.descriptor = descriptor
self.map_failed = False
def write_at(self, offset, data):
while data:
written = os.pwrite(self.descriptor, data, offset)
if written <= 0:
raise OSError("Python evidence transport write made no progress")
offset += written
data = data[written:]
def flush(self):
pass
def close(self):
pass
def _open_transport(descriptor, capacity):
if sys.platform == "darwin":
return _FileOutput(descriptor)
return mmap.mmap(descriptor, capacity, access=mmap.ACCESS_WRITE)
def _transport_write(output, offset, data):
if isinstance(output, _FileOutput):
output.write_at(offset, data)
else:
output[offset:offset + len(data)] = data
class _Hits(mmap.mmap):
__slots__ = ("context", "states", "path", "descriptor", "shared")
class _LazyHits:
__slots__ = ("runtime", "context", "resolved")
def __init__(self, runtime, context):
self.runtime, self.context, self.resolved = runtime, context, None
def _get(self):
if self.resolved is None:
with self.runtime.lock:
if self.resolved is None:
self.resolved = self.runtime._allocate_hits(self.context)
if self.runtime.hits_var.get() is self:
self.runtime.hits_var.set(self.resolved)
return self.resolved
def __getitem__(self, key):
return self._get()[key]
def __setitem__(self, key, value):
self._get()[key] = value
def __getattr__(self, name):
return getattr(self._get(), name)
class _Sink(bytearray):
__slots__ = ("context", "states")
class _DecisionState:
__slots__ = ("mask", "last", "saved")
def __init__(self) -> None:
self.mask = 0
self.last = -1
self.saved: list = []
class Runtime:
def __init__(self, plan_path: str, evidence_dir: str, run_id: str, worker: str) -> None:
import supercov_probes
root, index = supercov_probes.load_plan(plan_path, PLAN_VERSION)
self.root = os.path.realpath(root)
source_root = os.environ.get(SOURCE_ROOT_ENV)
self.roots = (self.root,) + ((os.path.realpath(source_root),) if source_root else ())
self.evidence_dir = evidence_dir
self.run_id = run_id
self.worker = worker
self.tool_id = None
self.lock = _thread.RLock()
self.path_cache: dict[str, str | None] = {}
self.real_directories: dict[str, str] = {}
self.under_root = False
self.context = contextvars.ContextVar("supercov_python_context", default=0)
self.identities: dict[int, dict] = {}
self.inherited_identity = None
self.next_context = 1
self.asserted: set[int] = set()
self.probed_sites: set[tuple[int, str, int]] = set()
self.seen_hits: set = set()
self.seen_vectors: set = set()
self._id_json: dict = {}
self.pytest_active = False
self.reported_limitations: set = set()
self.output_path = None
self.output_descriptor = None
self.output = None
self.output_capacity = 0
self.output_cursor = TRANSPORT_HEADER_SIZE
self.output_pid = None
self.output_token = f"{time.time_ns():x}-{id(self) & 0xFFFF:x}"
self.shared_token = f"{time.time_ns() & 0xFFFFFF:x}"
self.dropped_records = 0
self.closed = False
self.registered_events: tuple = ()
self.index = index
self.probe_files = index.files
self.probe_sites = index.sites
self.probe_ids = index.ids
self.probe_decisions = index.decisions
self.loaded_decisions: dict[int, _Decision] = {}
self.probe_boolops = index.boolop_groups
self.regions = index.regions
self.slot_header = supercov_probes.SLOT_HEADER
self.slot_bytes = index.slot_bytes
self.slot_zeros = memoryview(bytes(index.slot_bytes))
self.slot_tag = bytes.fromhex(index.digest[:16])
self.pow3 = [3**i for i in range(index.max_width + 1)]
self.live_slots: list = []
self.free_slots: list = []
self.next_slot = 0
self.free_refs = self._holders([object()], 0)
self.probe_open_boolops: dict = {}
self.probing = None
self.hits_var = None
def _open_output(self) -> None:
os.makedirs(self.evidence_dir, exist_ok=True)
pid = os.getpid()
safe_worker = "".join(
character if character.isalnum() or character in "-_." else "_"
for character in self.worker
)
self.output_path = os.path.join(
self.evidence_dir,
f"{safe_worker}.{pid}.{self.output_token}.mmap",
)
descriptor = os.open(self.output_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.ftruncate(descriptor, TRANSPORT_INITIAL_CAPACITY)
output = _open_transport(descriptor, TRANSPORT_INITIAL_CAPACITY)
except Exception:
os.close(descriptor)
raise
self.output_descriptor = descriptor
self.output = output
self.output_capacity = TRANSPORT_INITIAL_CAPACITY
self.output_cursor = TRANSPORT_HEADER_SIZE
self.output_pid = pid
self.dropped_records = 0
_transport_write(
output,
0,
TRANSPORT_MAGIC
+ TRANSPORT_VERSION.to_bytes(4, "little")
+ TRANSPORT_HEADER_SIZE.to_bytes(4, "little")
+ self.output_capacity.to_bytes(8, "little")
+ bytes(8)
+ pid.to_bytes(8, "little")
+ bytes(24),
)
self._write_record(
{
"t": "process",
"v": EVIDENCE_VERSION,
"run": self.run_id,
"pid": pid,
"worker": self.worker,
"frontend": "probes",
"python": sys.version.split()[0],
"executable": sys.executable,
"argv": sys.argv,
}
)
context = self.context.get()
identity = self.identities.get(context)
if context and identity is not None:
self._write_record({"t": "phase", "ctx": context, "at": _now_ms(), **identity})
def _close_output(self) -> None:
output = self.output
descriptor = self.output_descriptor
self.output = None
self.output_descriptor = None
self.output_path = None
self.output_capacity = 0
self.output_cursor = TRANSPORT_HEADER_SIZE
self.output_pid = None
if output is not None:
output.close()
if descriptor is not None:
os.close(descriptor)
def _ensure_process_output(self) -> None:
if self.output_pid == os.getpid() and self.output is not None:
return
if self.output is not None or self.output_descriptor is not None:
self._close_output()
self._open_output()
@staticmethod
def _checksum(payload: bytes) -> int:
return zlib.crc32(payload)
def _grow_output(self, required: int) -> bool:
if self.output is None or self.output_descriptor is None:
return False
capacity = self.output_capacity
while capacity < required and capacity < TRANSPORT_MAX_CAPACITY:
capacity = min(capacity * 2, TRANSPORT_MAX_CAPACITY)
if capacity < required:
return False
was_file = isinstance(self.output, _FileOutput)
self.output.close()
os.ftruncate(self.output_descriptor, capacity)
self.output = (_open_transport(self.output_descriptor, capacity) if was_file
else mmap.mmap(self.output_descriptor, capacity, access=mmap.ACCESS_WRITE))
self.output_capacity = capacity
_transport_write(self.output, 16, capacity.to_bytes(8, "little"))
return True
def _write_record(self, record: dict) -> None:
self._write_payload(_json_text(record).encode("ascii"))
def _json_id(self, text: str) -> bytes:
encoded = self._id_json.get(text)
if encoded is None:
encoded = self._id_json[text] = _json_string(str(text)).encode("ascii")
return encoded
def _write_hit(self, context: int, obligation: str) -> None:
self._write_payload(b'{"ctx":%d,"id":%s,"t":"hit"}' % (context, self._json_id(obligation)))
def _write_vector(self, context: int, decision_id: str, digits: str, outcome: bool) -> None:
self._write_payload(
b'{"ctx":%d,"id":%s,"o":%d,"t":"dec","v":"%s"}'
% (context, self._json_id(decision_id), 1 if outcome else 0, digits.encode("ascii"))
)
def _write_payload(self, payload: bytes) -> None:
if self.output is None:
raise RuntimeError("Python evidence transport is not open")
if len(payload) > TRANSPORT_MAX_RECORD_SIZE:
self._drop_record()
return
end = self.output_cursor + TRANSPORT_RECORD_HEADER_SIZE + len(payload)
next_cursor = (end + 7) & ~7
if next_cursor > self.output_capacity and not self._grow_output(next_cursor):
self._drop_record()
return
output = self.output
if isinstance(output, _FileOutput) and not output.map_failed and next_cursor > TRANSPORT_MAP_AFTER_BYTES:
try:
self.output = mmap.mmap(self.output_descriptor, self.output_capacity, access=mmap.ACCESS_WRITE)
output = self.output
except (OSError, ValueError):
output.map_failed = True
cursor = self.output_cursor
if isinstance(output, _FileOutput):
frame = (
bytes(4) + len(payload).to_bytes(4, "little") + self._checksum(payload).to_bytes(4, "little") + bytes(4) + payload
)
output.write_at(cursor, frame + b"\0" * (next_cursor - end))
output.write_at(cursor, b"\x01")
else:
output[cursor + TRANSPORT_RECORD_HEADER_SIZE : end] = payload
if next_cursor > end:
output[end:next_cursor] = b"\0" * (next_cursor - end)
output[cursor + 4 : cursor + 12] = len(payload).to_bytes(4, "little") + self._checksum(payload).to_bytes(4, "little")
output[cursor] = 1
self.output_cursor = next_cursor
def _drop_record(self) -> None:
self.dropped_records += 1
if self.output is not None:
_transport_write(self.output, 24, self.dropped_records.to_bytes(8, "little"))
def _record(self, record: dict) -> None:
with self.lock:
if self.closed:
return
self._ensure_process_output()
self._write_record(record)
def _record_payload(self, payload: bytes) -> None:
with self.lock:
if self.closed:
return
self._ensure_process_output()
self._write_payload(payload)
def flush(self) -> None:
return
def limitation(self, identifier: str, reason: str, file: str | None = None, obligation: str | None = None) -> None:
key = (identifier, file, obligation)
with self.lock:
if key in self.reported_limitations:
return
self.reported_limitations.add(key)
record = {"t": "limitation", "id": identifier, "reason": reason}
if file is not None:
record["file"] = file
if obligation is not None:
record["obligation"] = obligation
self._record(record)
def set_worker(self, worker: str) -> None:
with self.lock:
if worker != self.worker:
self.worker = worker
self._record({"t": "worker", "worker": worker})
def switch(self, identity: dict | None) -> int:
began = time.perf_counter() if TIMING else 0.0
try:
return self._switch(identity)
finally:
if TIMING:
_timing["switch"][0] += 1
_timing["switch"][1] += time.perf_counter() - began
def _switch(self, identity: dict | None) -> int:
with self.lock:
if identity is None:
context = 0
else:
context = self.next_context
self.next_context += 1
stored = {
"worker": identity.get("worker", self.worker),
"test": identity["test"],
"retry": int(identity.get("retry", 0)),
"phase": identity["phase"],
}
self.identities[context] = stored
text = self._json_id
self._record_payload(
b'{"at":%d,"ctx":%d,"phase":%s,"retry":%d,"t":"phase","test":%s,"worker":%s}'
% (_now_ms(), context, text(stored["phase"]), stored["retry"], text(stored["test"]), text(stored["worker"]))
)
leaving = self.hits_var.get()
if isinstance(leaving, _LazyHits):
leaving = leaving.resolved
if isinstance(leaving, _Hits):
self._harvest(leaving)
self.hits_var.set(self._new_hits(context))
del leaving
self._reclaim()
self.context.set(context)
return context
def current_identity(self) -> dict | None:
return self.identities.get(self.context.get())
def assertion(self) -> bool:
context = self.context.get()
if context == 0 or context in self.asserted:
return False
with self.lock:
if context in self.asserted:
return False
identity = self.identities.get(context)
if identity is None or identity.get("phase") != "call":
return False
self.asserted.add(context)
array = self.hits_var.get()
if isinstance(array, _LazyHits):
array = array.resolved
if isinstance(array, _Hits) and array.context == context:
self._harvest(array)
self._record_payload(b'{"ctx":%d,"t":"assert"}' % context)
return True
def child_environment(self) -> dict:
identity = self.current_identity()
if identity is None:
return {}
return {CONTEXT_ENV: _json_text({key: identity[key] for key in sorted(identity)})}
def outcome(self, worker: str, test: str, retry: int, phase: str, outcome: str, xfail: bool, runner: str = "pytest", file: "str | None" = None) -> None:
text = self._json_id
self._record_payload(
b'{%s"outcome":%s,"phase":%s,"retry":%d,"runner":%s,"t":"outcome","test":%s,"worker":%s,"xfail":%s}'
% (
b'"file":%s,' % text(file) if file else b"",
text(outcome),
text(phase),
int(retry),
text(runner),
text(test),
text(worker),
b"true" if xfail else b"false",
)
)
def _hit(self, context: int, obligation: str) -> None:
key = (context, obligation)
if key in self.seen_hits:
return
with self.lock:
if key in self.seen_hits:
return
self.seen_hits.add(key)
if self.closed:
return
self._ensure_process_output()
self._write_hit(context, obligation)
def _wide_vector(self, context: int, d: int, mask: int, outcome: bool) -> None:
decision = self._decision(d)
digits = _digits(mask, decision.width)
key = (context, decision.id, digits)
if key in self.seen_vectors:
return
with self.lock:
if key in self.seen_vectors:
return
self.seen_vectors.add(key)
if self.closed:
return
self._ensure_process_output()
self._write_vector(context, decision.id, digits, outcome)
implied: list = []
decision.implied(digits, outcome, implied)
for identifier in implied:
self._hit(context, identifier)
def _relative_path(self, filename: str) -> str | None:
cached = self.path_cache.get(filename, self)
if cached is not self:
return cached
relative = None
if filename and not filename.startswith("<"):
candidates = [filename]
if not os.path.isabs(filename):
candidates.append(os.path.join(os.getcwd(), filename))
for candidate in candidates:
try:
real = self._realpath(candidate)
except OSError:
continue
for root in self.roots:
if real == root or real.startswith(root + os.sep):
self.under_root = True
relative = real[len(root) + 1 :].replace(os.sep, "/")
if relative in self.probe_files or relative in self.probe_sites:
break
relative = None
if relative is not None:
break
self.path_cache[filename] = relative
return relative
def _realpath(self, path: str) -> str:
directory, name = os.path.split(os.path.abspath(path))
real = self.real_directories.get(directory)
if real is None:
real = self.real_directories[directory] = os.path.realpath(directory)
joined = os.path.join(real, name)
return os.path.realpath(joined) if os.path.islink(joined) else joined
def _decision(self, d: int) -> _Decision:
if d not in self.loaded_decisions:
self.loaded_decisions[d] = _Decision(*self.probe_decisions[d])
return self.loaded_decisions[d]
def _write_layout(self) -> None:
path = os.path.join(self.evidence_dir, LAYOUT_NAME)
if os.path.exists(path):
return
partial = f"{path}.{os.getpid()}.{self.output_token}.partial"
import json
with open(partial, "w", encoding="utf-8") as stream:
json.dump(self.index.layout(), stream, separators=(",", ":"))
try:
os.replace(partial, path)
except OSError:
if not os.path.exists(path):
raise
try:
os.unlink(partial)
except OSError:
pass
def _create_slot(self) -> _Hits:
self._ensure_process_output()
stem = f"{self.output_path[: -len('.mmap')]}.{self.next_slot}"
self.next_slot += 1
global _posixshmem
if _posixshmem is None:
try:
import _posixshmem
except ImportError:
_posixshmem = False
if _posixshmem:
try:
return self._create_shared_slot(stem)
except OSError:
pass
path = stem + SLOT_SUFFIX
descriptor = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL | getattr(os, "O_BINARY", 0), 0o600)
try:
os.ftruncate(descriptor, self.slot_bytes)
slot = _Hits(descriptor, self.slot_bytes, access=mmap.ACCESS_WRITE)
except Exception:
os.close(descriptor)
raise
slot[8:16] = self.slot_tag
slot.path = path
slot.descriptor = descriptor
slot.shared = None
return slot
def _create_shared_slot(self, stem: str) -> _Hits:
name = "/scv%x.%s.%d" % (os.getpid(), self.shared_token, self.next_slot)
path = stem + SHARED_SLOT_SUFFIX
descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.write(descriptor, b"%s %d" % (name.encode("ascii"), self.slot_bytes))
finally:
os.close(descriptor)
descriptor = _posixshmem.shm_open(name, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.ftruncate(descriptor, self.slot_bytes)
slot = _Hits(descriptor, self.slot_bytes, access=mmap.ACCESS_WRITE)
except Exception:
os.close(descriptor)
_posixshmem.shm_unlink(name)
raise
slot[8:16] = self.slot_tag
slot.path = path
slot.descriptor = descriptor
slot.shared = name
return slot
def _sink(self, context: int) -> _Sink:
sink = _Sink(self.slot_bytes)
sink.context = context
sink.states = [None] * len(self.probe_decisions)
return sink
def _new_hits(self, context: int):
return _LazyHits(self, context)
def _allocate_hits(self, context: int):
if self.closed:
return self._sink(context)
try:
slot = self.free_slots.pop() if self.free_slots else self._create_slot()
except Exception as error: self.limitation(
"python-slot-unavailable",
f"could not map a slot for a test phase ({error!r}); what it executed was not observed",
)
return self._sink(context)
slot.context = context
slot.states = [None] * len(self.probe_decisions)
slot[0:8] = context.to_bytes(8, "little")
self.live_slots.append(slot)
return slot
@staticmethod
def _holders(items: list, index: int) -> int:
return sys.getrefcount(items[index])
def _reclaim(self) -> None:
live = self.live_slots
index = 0
while index < len(live):
if self._holders(live, index) <= self.free_refs:
slot = live.pop(index)
self._harvest(slot)
self.free_slots.append(slot)
else:
index += 1
def _after_fork_in_child(self) -> None:
self.lock = _thread.RLock()
self.live_slots = []
self.free_slots = []
if self.closed or self.hits_var is None:
return
current = self.hits_var.get()
self.background_hits.resolved = None
try:
self.hits_var.set(self._new_hits(current.context))
except Exception: pass
def _harvest(self, slot: _Hits) -> None:
if STUB == "harvest":
return
began = time.perf_counter() if TIMING else 0.0
find = slot.find
if find(b"\x01", self.slot_header) == -1:
return
zeros = self.slot_zeros
end = self.slot_bytes
runs: list = []
with self.lock:
index = find(b"\x01", self.slot_header)
while index != -1:
stop = find(b"\x00", index)
if stop == -1:
stop = end
slot[index:stop] = zeros[: stop - index]
runs.append(index)
runs.append(stop - index)
index = find(b"\x01", stop)
if runs and not self.closed:
self._ensure_process_output()
prefix = b'{"ctx":%d,"r":[' % slot.context
for start in range(0, len(runs), HARVEST_CHUNK):
self._write_payload(
prefix + ",".join(map(str, runs[start : start + HARVEST_CHUNK])).encode("ascii") + b'],"t":"runs"}'
)
if TIMING:
_timing["harvest"][0] += 1
_timing["harvest"][1] += time.perf_counter() - began
def _value_probe(self, k: int, value):
self.hits_var.get()[k] = 1
return value
def _single_probe(self, start: int, value) -> bool:
truth = not not value
self.hits_var.get()[start + truth] = 1
return truth
def _condition_probe(self, d: int, index: int, value, inverted: bool) -> bool:
truth = not not value
states = self.hits_var.get().states
state = states[d]
if state is None:
state = states[d] = _DecisionState()
if index <= state.last:
state.saved.append((state.mask, state.last))
state.mask = 0
state.last = index
state.mask += (2 if truth != inverted else 1) * self.pow3[index]
return truth
def _decision_probe(self, d: int, value) -> bool:
outcome = not not value
hits = self.hits_var.get()
state = hits.states[d]
if state is None:
return outcome
mask = state.mask
if state.saved:
state.mask, state.last = state.saved.pop()
else:
state.mask = 0
state.last = -1
start = self.regions[d]
if start >= 0:
hits[start + 2 * mask + outcome] = 1
else:
self._wide_vector(hits.context, d, mask, outcome)
return outcome
def _operand_probe(self, g: int, index: int, value):
key = (self.context.get(), g)
stack = self.probe_open_boolops.get(key)
if stack is None:
stack = self.probe_open_boolops[key] = []
if not stack or index <= stack[-1][-1]:
if len(stack) >= MAX_OPEN_EVALUATIONS:
del stack[0]
stack.append([0])
stack[-1].append(index)
return value
def _boolop_probe(self, g: int, value):
context = self.context.get()
stack = self.probe_open_boolops.get((context, g))
evaluated = set(stack.pop()) if stack else {0}
for logical in self.probe_boolops[g]:
operand = logical["operand"]
self._hit(context, logical["evaluated"] if operand in evaluated else logical["shortCircuit"])
return value
def _iter_probe(self, entered: int, zero: int, iterable):
iterator = iter(iterable)
try:
first = next(iterator)
except StopIteration:
self.hits_var.get()[zero] = 1
return iter(())
self.hits_var.get()[entered] = 1
return itertools.chain((first,), iterator)
async def _aiter_probe(self, entered: int, zero: int, iterable):
iterator = iterable.__aiter__()
try:
first = await iterator.__anext__()
except StopAsyncIteration:
self.hits_var.get()[zero] = 1
return
self.hits_var.get()[entered] = 1
yield first
async for item in iterator:
yield item
def _site_probe(self, file: str, line: int) -> None:
key = (self.context.get(), file, line)
if key in self.probed_sites:
return
self.assertion()
self.probed_sites.add(key)
def _detect_unprobed(self, code, offset):
probing = self.probing
if probing is not None and id(code) not in probing.code_objects:
relative = self._relative_path(code.co_filename)
if relative is not None and relative in self.probe_files:
self.limitation(
"python-probes-unobserved-module",
"measured code reached the interpreter by a path the import hook did not see, so its execution was not observed",
relative,
)
return _monitoring.DISABLE
def _install_probes(self) -> None:
import supercov_probes
self.probing = supercov_probes.install(
self.probe_files,
self._relative_path,
os.path.join(self.root, ".supercov", "cache", "python"),
{
"hits_get": self.hits_var.get,
"value_probe": self._value_probe,
"condition_probe": self._condition_probe,
"decision_probe": self._decision_probe,
"single_probe": self._single_probe,
"operand_probe": self._operand_probe,
"boolop_probe": self._boolop_probe,
"iter_probe": self._iter_probe,
"aiter_probe": self._aiter_probe,
"site_probe": self._site_probe,
"limitation": self.limitation,
},
self.probe_sites,
)
if _monitoring is not None and STUB != "detector":
for candidate in (3, 4, 1):
if _monitoring.get_tool(candidate) is None:
_monitoring.use_tool_id(candidate, "supercov")
self.tool_id = candidate
break
if self.tool_id is not None:
_monitoring.register_callback(self.tool_id, _monitoring.events.PY_START, self._detect_unprobed)
self.registered_events = (_monitoring.events.PY_START,)
_monitoring.set_events(self.tool_id, _monitoring.events.PY_START)
def install(self) -> None:
with self.lock:
if self.output is not None:
return
self._open_output()
self._write_layout()
self.background_hits = self._new_hits(0)
self.hits_var = contextvars.ContextVar("supercov_hits", default=self.background_hits)
self._install_probes()
for module in list(sys.modules.values()):
filename = getattr(module, "__file__", None)
relative = self._relative_path(filename) if isinstance(filename, str) else None
if relative is not None and relative in self.probe_files:
self.limitation(
"python-probes-unobserved-module",
"measured code was imported before Supercov installed, so its execution was not observed",
relative,
)
entry = sys.argv[0] if sys.argv else ""
if entry and not entry.startswith("-"):
relative = self._relative_path(entry)
if relative is not None and relative in self.probe_files:
self.limitation(
"python-probes-unobserved-module",
"a test ran this file as a script (python path/to/script.py), which CPython compiles "
"outside the import system, so what that run executed was not observed; lines shown "
"uncovered may have run there",
relative,
)
if hasattr(os, "register_at_fork"):
os.register_at_fork(after_in_child=self._after_fork_in_child)
inherited = os.environ.get(CONTEXT_ENV)
if inherited:
try:
identity = _inherited_identity(inherited)
if identity is not None:
self.switch(identity)
self.inherited_identity = self.current_identity()
except (ValueError, KeyError, TypeError):
self.limitation("python-inherited-context-invalid", "SUPERCOV_CONTEXT was not a valid Supercov identity")
atexit.register(self.close)
_install_propagation(self)
def install_unittest(module):
try:
import supercov_unittest
supercov_unittest.install(self)
except Exception as error: self.limitation("python-unittest-adapter-unavailable", f"unittest adapter failed to install: {error!r}")
self.probing.after_import("unittest", install_unittest)
def _stop_observing(self) -> None:
if self.tool_id is None:
return
try:
_monitoring.set_events(self.tool_id, 0)
for event in self.registered_events:
_monitoring.register_callback(self.tool_id, event, None)
except Exception: pass
def close(self) -> None:
with self.lock:
if self.closed:
return
self._stop_observing()
for slot in self.live_slots:
self._harvest(slot)
self._record({"t": "exit", "at": _now_ms()})
self.closed = True
self._close_output()
slots = self.live_slots + self.free_slots
self.live_slots = []
self.free_slots = []
unmatched = self.worker == "main" and bool(self.path_cache) and not self.under_root
for slot in slots:
try:
slot[8:16] = b"\0" * 8
os.close(slot.descriptor)
if slot.shared is not None:
_posixshmem.shm_unlink(slot.shared)
except (OSError, ValueError):
pass
if unmatched and TIMING:
sample = next(
(name for name in self.path_cache if not name.startswith("<")),
next(iter(self.path_cache)),
)
sys.stderr.write(
f"[supercov] none of the {len(self.path_cache)} imported files lay under "
f"the measured root {self.root}; for example {sample}\n"
)
if TIMING:
sys.stderr.write(
"[supercov:timing] "
+ " ".join(f"{name}={count} calls/{seconds * 1000:.0f}ms" for name, (count, seconds) in _timing.items())
+ f" slots={self.next_slot}\n"
)
SCOPE_HEADER = "x-supercov-scope"
SCOPE_COOKIES = ("__supercov_scope", "__supercov_scope_site")
def _request_identity(header):
from urllib.parse import parse_qs, unquote
encoded = header(SCOPE_HEADER)
if not encoded:
for part in (header("cookie") or "").split(";"):
name, _, value = part.strip().partition("=")
if name in SCOPE_COOKIES and value:
encoded = unquote(value)
break
if not encoded:
return None
values = {key: items[0] for key, items in parse_qs(encoded).items() if items}
if values.get("v") != "1" or not values.get("t") or not values.get("w"):
return None
try:
retry = int(values.get("a", "0"))
except ValueError:
return None
return {
"worker": "playwright:" + values["w"],
"test": values["t"],
"retry": retry,
"phase": "call",
}
def _inherited_identity(value):
if value.startswith("{"):
return _json_load(value)
import base64
carrier = _json_load(base64.urlsafe_b64decode(value + "=" * (-len(value) % 4)).decode("utf-8"))
if not isinstance(carrier, dict) or carrier.get("version") != 1:
raise ValueError("not a Supercov carrier")
scope = carrier.get("scope")
if not scope:
return None
return {
"worker": "playwright:" + scope["workerId"],
"test": scope["testId"],
"retry": int(scope["retry"]),
"phase": "call",
}
def _environ_header(environ):
return lambda name: environ.get("HTTP_" + name.upper().replace("-", "_"))
def _scope_header(scope):
def header(name):
wanted = name.encode("latin-1")
for key, value in scope.get("headers") or ():
if key.lower() == wanted:
return value.decode("latin-1")
return None
return header
class _RequestBody:
def __init__(self, runtime, body, identity):
self._runtime, self._body, self._identity = runtime, body, identity
self._chunks = None
self._leave = None
def __iter__(self):
return self
def __next__(self):
runtime = self._runtime
if self._leave is None:
self._leave = runtime.context.get()
runtime.switch(self._identity)
try:
if self._chunks is None:
self._chunks = iter(self._body)
return next(self._chunks)
except StopIteration:
self._exit()
raise
def _exit(self):
if self._leave is not None:
leave, self._leave = self._leave, None
self._runtime.switch(self._runtime.identities.get(leave))
def close(self):
close = getattr(self._body, "close", None)
try:
if close is not None:
if self._leave is None:
self._leave = self._runtime.context.get()
self._runtime.switch(self._identity)
close()
finally:
self._exit()
REQUEST_WORKER = "supercov-request"
_requests = itertools.count(1)
_in_request = contextvars.ContextVar("supercov_in_request", default=False)
def _enter_request(runtime, identity):
if identity is None:
if _in_request.get() or runtime.inherited_identity is not None:
return None
identity = {"worker": REQUEST_WORKER, "test": str(next(_requests)), "retry": 0, "phase": "call"}
elif runtime.current_identity() == identity:
return None
leave = runtime.context.get()
runtime.switch(identity)
return leave, _in_request.set(True)
def _leave_request(runtime, entered):
if entered is not None:
leave, token = entered
runtime.switch(runtime.identities.get(leave))
try:
_in_request.reset(token)
except (ValueError, RuntimeError):
_in_request.set(False)
def _install_propagation(runtime: Runtime) -> None:
def install_threading(threading):
if getattr(threading.Thread, "_supercov_patched", False):
return
original_start = threading.Thread.start
def start_with_context(thread, *args, **kwargs):
if not hasattr(thread, "_supercov_original_run"):
context = contextvars.copy_context()
original_run = thread.run
thread._supercov_original_run = original_run
def run_with_context():
try:
return context.run(original_run)
finally:
thread.__dict__.pop("run", None)
thread.__dict__.pop("_supercov_original_run", None)
thread.run = run_with_context
return original_start(thread, *args, **kwargs)
threading.Thread.start = start_with_context
threading.Thread._supercov_patched = True
def install_executor(module):
executor_type = module.ThreadPoolExecutor
if getattr(executor_type, "_supercov_patched", False):
return
original_submit = executor_type.submit
def submit_with_context(executor, function, /, *args, **kwargs):
context = contextvars.copy_context()
return original_submit(executor, context.run, function, *args, **kwargs)
executor_type.submit = submit_with_context
executor_type._supercov_patched = True
def install_subprocess(module):
if getattr(module.Popen, "_supercov_patched", False):
return
original_init = module.Popen.__init__
def init_with_context(process, *args, **kwargs):
additions = runtime.child_environment()
if additions:
supplied = args[10] if len(args) > 10 else kwargs.get("env")
environment = dict(os.environ if supplied is None else supplied)
text_plan = environment.get(PLAN_ENV)
bytes_plan = environment.get(PLAN_ENV.encode())
if text_plan or bytes_plan:
for key, value in additions.items():
environment.pop(key, None)
environment.pop(key.encode(), None)
if bytes_plan and not text_plan:
environment[key.encode()] = value.encode()
else:
environment[key] = value
if len(args) > 10:
args = (*args[:10], environment, *args[11:])
else:
kwargs["env"] = environment
original_init(process, *args, **kwargs)
module.Popen.__init__ = init_with_context
module.Popen._supercov_patched = True
def install_multiprocessing(module):
process_type = module.BaseProcess
if getattr(process_type, "_supercov_patched", False):
return
original_process_start = process_type.start
environment_lock = _thread.allocate_lock()
def process_start_with_context(process, *args, **kwargs):
additions = runtime.child_environment()
if not additions:
return original_process_start(process, *args, **kwargs)
with environment_lock:
previous = {key: os.environ.get(key) for key in additions}
os.environ.update(additions)
try:
return original_process_start(process, *args, **kwargs)
finally:
for key, value in previous.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
process_type.start = process_start_with_context
process_type._supercov_patched = True
def install_http_server(module):
handler = module.BaseHTTPRequestHandler
if getattr(handler, "_supercov_patched", False):
return
original_parse = handler.parse_request
original_handle = handler.handle
def parse_request(request_handler):
_leave_request(runtime, request_handler.__dict__.pop("_supercov_leave", None))
parsed = original_parse(request_handler)
if parsed:
headers = request_handler.headers
request_handler._supercov_leave = _enter_request(
runtime, _request_identity(lambda name: headers.get(name))
)
return parsed
def handle(request_handler):
try:
return original_handle(request_handler)
finally:
_leave_request(runtime, request_handler.__dict__.pop("_supercov_leave", None))
handler.parse_request = parse_request
handler.handle = handle
handler._supercov_patched = True
def wsgi_entry(owner, attribute="__call__"):
original = owner.__dict__.get(attribute)
if original is None or getattr(original, "_supercov_patched", False):
return
def entry(application, environ, start_response):
leave = _enter_request(runtime, _request_identity(_environ_header(environ)))
try:
body = original(application, environ, start_response)
active = runtime.current_identity()
if active is None or isinstance(body, (list, tuple, bytes)):
return body
return _RequestBody(runtime, body, active)
finally:
_leave_request(runtime, leave)
entry._supercov_patched = True
setattr(owner, attribute, entry)
def asgi_entry(owner):
original = owner.__dict__.get("__call__")
if original is None or getattr(original, "_supercov_patched", False):
return
async def entry(application, scope, receive, send):
identity = None
if scope.get("type") in ("http", "websocket"):
identity = _request_identity(_scope_header(scope))
leave = _enter_request(runtime, identity)
try:
return await original(application, scope, receive, send)
finally:
_leave_request(runtime, leave)
entry._supercov_patched = True
owner.__call__ = entry
def install_flask(module):
wsgi_entry(module.Flask, "wsgi_app")
def install_django_wsgi(module):
wsgi_entry(module.WSGIHandler)
def install_django_asgi(module):
asgi_entry(module.ASGIHandler)
def install_starlette(module):
asgi_entry(module.Starlette)
runtime.probing.after_import("http.server", install_http_server)
runtime.probing.after_import("flask.app", install_flask)
runtime.probing.after_import("django.core.handlers.wsgi", install_django_wsgi)
runtime.probing.after_import("django.core.handlers.asgi", install_django_asgi)
runtime.probing.after_import("starlette.applications", install_starlette)
runtime.probing.after_import("threading", install_threading)
runtime.probing.after_import("concurrent.futures.thread", install_executor)
runtime.probing.after_import("subprocess", install_subprocess)
runtime.probing.after_import("multiprocessing.process", install_multiprocessing)
_RUNTIME: Runtime | None = None
_INSTALL_ERROR: str | None = None
def runtime() -> Runtime | None:
return _RUNTIME
def install() -> Runtime | None:
global _RUNTIME, _INSTALL_ERROR
if _RUNTIME is not None or _INSTALL_ERROR is not None:
return _RUNTIME
plan_path = os.environ.get(PLAN_ENV)
evidence_dir = os.environ.get(EVIDENCE_DIR_ENV)
run_id = os.environ.get(RUN_ID_ENV)
if not plan_path or not evidence_dir or not run_id:
return None
if sys.version_info < (3, 9):
_INSTALL_ERROR = f"Supercov requires CPython 3.9 or newer, found {sys.version.split()[0]}"
sys.stderr.write(f"[supercov] {_INSTALL_ERROR}\n")
return None
try:
instance = Runtime(plan_path, evidence_dir, run_id, os.environ.get(WORKER_ENV, "main"))
instance.install()
except Exception as error: _INSTALL_ERROR = repr(error)
sys.stderr.write(f"[supercov] Python runtime disabled: {_INSTALL_ERROR}\n")
return None
_RUNTIME = instance
return instance