Skip to content

Commit 1560644

Browse files
feat: collect Python toolchain information via instrument hooks environment API
Update instrument-hooks submodule to pick up the new environment collection API and use it to report Python version and build info at runtime. Generated with AI Agent (Claude Code)
1 parent aa267f3 commit 1560644

6 files changed

Lines changed: 86 additions & 1 deletion

File tree

src/pytest_codspeed/instruments/analysis.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, config: CodSpeedConfig, mode: MeasurementMode) -> None:
3333
try:
3434
self.instrument_hooks = InstrumentHooks()
3535
self.instrument_hooks.set_integration("pytest-codspeed", __semver_version__)
36+
self.instrument_hooks.collect_and_write_python_environment()
3637
except RuntimeError as e:
3738
if os.environ.get("CODSPEED_ENV") is not None:
3839
raise Exception(

src/pytest_codspeed/instruments/hooks/__init__.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
22

33
import os
4+
import platform
45
import sys
6+
import sysconfig
57
import warnings
68
from typing import TYPE_CHECKING
79

@@ -92,3 +94,70 @@ def set_feature(self, feature: int, enabled: bool) -> None:
9294
enabled: Whether to enable or disable the feature
9395
"""
9496
self.lib.instrument_hooks_set_feature(feature, enabled)
97+
98+
def set_environment(self, section_name: str, key: str, value: str) -> None:
99+
"""Register a key-value pair under a named section for environment collection.
100+
101+
Args:
102+
section_name: The section name (e.g. "Python")
103+
key: The key (e.g. "version")
104+
value: The value (e.g. "3.13.12")
105+
"""
106+
ret = self.lib.instrument_hooks_set_environment(
107+
self.instance,
108+
section_name.encode("utf-8"),
109+
key.encode("utf-8"),
110+
value.encode("utf-8"),
111+
)
112+
if ret != 0:
113+
warnings.warn("Failed to set environment data", RuntimeWarning)
114+
115+
def write_environment(self, pid: int | None = None) -> None:
116+
"""Flush all registered environment sections to disk.
117+
118+
Writes to $CODSPEED_PROFILE_FOLDER/environment-<pid>.json.
119+
120+
Args:
121+
pid: Optional process ID (defaults to current process)
122+
"""
123+
if pid is None:
124+
pid = os.getpid()
125+
ret = self.lib.instrument_hooks_write_environment(self.instance, pid)
126+
if ret != 0:
127+
warnings.warn("Failed to write environment data", RuntimeWarning)
128+
129+
def collect_and_write_python_environment(self) -> None:
130+
"""Collect Python toolchain information and write it to disk."""
131+
section = "python"
132+
set_env = self.set_environment
133+
134+
# Core identity
135+
set_env(section, "version", sys.version)
136+
set_env(section, "implementation", sys.implementation.name)
137+
set_env(section, "compiler", platform.python_compiler())
138+
139+
# Performance-relevant build configuration
140+
_SYSCONFIG_KEYS = (
141+
"CONFIG_ARGS",
142+
"OPT",
143+
"abiflags",
144+
"PY_ENABLE_SHARED",
145+
"Py_GIL_DISABLED",
146+
"Py_DEBUG",
147+
"WITH_PYMALLOC",
148+
"WITH_MIMALLOC",
149+
"WITH_FREELISTS",
150+
"HAVE_COMPUTED_GOTOS",
151+
"Py_STATS",
152+
"Py_TRACE_REFS",
153+
"WITH_VALGRIND",
154+
"WITH_DTRACE",
155+
)
156+
config_vars = sysconfig.get_config_vars()
157+
for key in _SYSCONFIG_KEYS:
158+
value = config_vars.get(key)
159+
if value is not None:
160+
set_env(section, key, str(value))
161+
set_env(section, "perf_trampoline", str(SUPPORTS_PERF_TRAMPOLINE))
162+
163+
self.write_environment()

src/pytest_codspeed/instruments/hooks/build.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
void callgrind_stop_instrumentation();
3737
3838
void instrument_hooks_set_feature(uint64_t feature, bool enabled);
39+
40+
uint8_t instrument_hooks_set_environment(InstrumentHooks *, const char *section_name,
41+
const char *key, const char *value);
42+
uint8_t instrument_hooks_write_environment(InstrumentHooks *, uint32_t pid);
3943
""")
4044

4145
ffibuilder.set_source(
@@ -47,6 +51,8 @@
4751
"src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c",
4852
],
4953
include_dirs=[str(includes_dir)],
54+
# IMPORTANT: Keep in sync with instrument-hooks/ci.yml (COMMON_CFLAGS)
55+
extra_compile_args=["-Wno-format-security"],
5056
)
5157

5258
if __name__ == "__main__":

src/pytest_codspeed/instruments/hooks/dist_instrument_hooks.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,13 @@ class lib:
3131
def callgrind_stop_instrumentation() -> int: ...
3232
@staticmethod
3333
def instrument_hooks_set_feature(feature: int, enabled: bool) -> None: ...
34+
@staticmethod
35+
def instrument_hooks_set_environment(
36+
hooks: InstrumentHooksPointer, section_name: bytes, key: bytes, value: bytes
37+
) -> int: ...
38+
@staticmethod
39+
def instrument_hooks_write_environment(
40+
hooks: InstrumentHooksPointer, pid: int
41+
) -> int: ...
3442

3543
LibType = type[lib]

src/pytest_codspeed/instruments/walltime.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def __init__(self, config: CodSpeedConfig, _mode: MeasurementMode) -> None:
163163
try:
164164
self.instrument_hooks = InstrumentHooks()
165165
self.instrument_hooks.set_integration("pytest-codspeed", __semver_version__)
166+
self.instrument_hooks.collect_and_write_python_environment()
166167
except RuntimeError as e:
167168
if os.environ.get("CODSPEED_ENV") is not None:
168169
warnings.warn(

0 commit comments

Comments
 (0)