pytest-codspeed 4.3.0__tar.gz → 4.4.0__tar.gz
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.
- {pytest_codspeed-4.3.0/src/pytest_codspeed.egg-info → pytest_codspeed-4.4.0}/PKG-INFO +1 -1
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/pyproject.toml +1 -1
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/__init__.py +2 -2
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/analysis.py +1 -0
- pytest_codspeed-4.4.0/src/pytest_codspeed/instruments/hooks/__init__.py +197 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/build.py +11 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/dist_instrument_hooks.pyi +18 -0
- pytest_codspeed-4.4.0/src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c +30987 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/includes/core.h +30 -15
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/walltime.py +2 -1
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0/src/pytest_codspeed.egg-info}/PKG-INFO +1 -1
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/tests/test_pytest_plugin_walltime.py +56 -1
- pytest_codspeed-4.3.0/src/pytest_codspeed/instruments/hooks/__init__.py +0 -94
- pytest_codspeed-4.3.0/src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c +0 -11220
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/LICENSE +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/README.md +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/setup.cfg +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/setup.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/config.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/__init__.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/includes/callgrind.h +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/includes/compat.h +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/includes/valgrind.h +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/includes/zig.h +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/instrument-hooks/scripts/release.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/plugin.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/py.typed +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/utils.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed.egg-info/SOURCES.txt +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed.egg-info/dependency_links.txt +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed.egg-info/entry_points.txt +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed.egg-info/requires.txt +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed.egg-info/top_level.txt +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/tests/test_format_time.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/tests/test_pytest_plugin.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/tests/test_pytest_plugin_cpu_instrumentation.py +0 -0
- {pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/tests/test_utils.py +0 -0
|
@@ -77,7 +77,7 @@ version = { attr = "pytest_codspeed.__version__" }
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
[tool.bumpver]
|
|
80
|
-
current_version = "4.
|
|
80
|
+
current_version = "4.4.0"
|
|
81
81
|
version_pattern = "MAJOR.MINOR.PATCH[-TAG[NUM]]"
|
|
82
82
|
commit_message = "Release v{new_version} 🚀"
|
|
83
83
|
tag_message = "Release v{new_version} 🚀"
|
|
@@ -33,6 +33,7 @@ class AnalysisInstrument(Instrument):
|
|
|
33
33
|
try:
|
|
34
34
|
self.instrument_hooks = InstrumentHooks()
|
|
35
35
|
self.instrument_hooks.set_integration("pytest-codspeed", __semver_version__)
|
|
36
|
+
self.instrument_hooks.collect_and_write_python_environment()
|
|
36
37
|
except RuntimeError as e:
|
|
37
38
|
if os.environ.get("CODSPEED_ENV") is not None:
|
|
38
39
|
raise Exception(
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import shlex
|
|
6
|
+
import sys
|
|
7
|
+
import sysconfig
|
|
8
|
+
import warnings
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from pytest_codspeed.utils import SUPPORTS_PERF_TRAMPOLINE
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from cffi import FFI
|
|
15
|
+
|
|
16
|
+
from .dist_instrument_hooks import InstrumentHooksPointer, LibType
|
|
17
|
+
|
|
18
|
+
# Feature flags for instrument hooks
|
|
19
|
+
FEATURE_DISABLE_CALLGRIND_MARKERS = 0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InstrumentHooks:
|
|
23
|
+
"""Zig library wrapper class providing benchmark measurement functionality."""
|
|
24
|
+
|
|
25
|
+
lib: LibType
|
|
26
|
+
ffi: FFI
|
|
27
|
+
instance: InstrumentHooksPointer
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
if os.environ.get("CODSPEED_ENV") is None:
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
"Can't run benchmarks outside of CodSpeed environment."
|
|
33
|
+
"Please set the CODSPEED_ENV environment variable."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
from .dist_instrument_hooks import ffi, lib # type: ignore
|
|
38
|
+
except ImportError as e:
|
|
39
|
+
raise RuntimeError(f"Failed to load instrument hooks library: {e}") from e
|
|
40
|
+
self.lib = lib
|
|
41
|
+
self.ffi = ffi
|
|
42
|
+
|
|
43
|
+
self.instance = self.lib.instrument_hooks_init()
|
|
44
|
+
if self.instance == 0:
|
|
45
|
+
raise RuntimeError("Failed to initialize CodSpeed instrumentation library.")
|
|
46
|
+
|
|
47
|
+
if SUPPORTS_PERF_TRAMPOLINE and not sys.is_stack_trampoline_active():
|
|
48
|
+
sys.activate_stack_trampoline("perf") # type: ignore
|
|
49
|
+
|
|
50
|
+
def __del__(self):
|
|
51
|
+
if hasattr(self, "lib") and hasattr(self, "instance"):
|
|
52
|
+
self.lib.instrument_hooks_deinit(self.instance)
|
|
53
|
+
|
|
54
|
+
def start_benchmark(self) -> None:
|
|
55
|
+
"""Start a new benchmark measurement."""
|
|
56
|
+
ret = self.lib.instrument_hooks_start_benchmark(self.instance)
|
|
57
|
+
if ret != 0:
|
|
58
|
+
warnings.warn("Failed to start benchmark measurement", RuntimeWarning)
|
|
59
|
+
|
|
60
|
+
def stop_benchmark(self) -> None:
|
|
61
|
+
"""Stop the current benchmark measurement."""
|
|
62
|
+
ret = self.lib.instrument_hooks_stop_benchmark(self.instance)
|
|
63
|
+
if ret != 0:
|
|
64
|
+
warnings.warn("Failed to stop benchmark measurement", RuntimeWarning)
|
|
65
|
+
|
|
66
|
+
def set_executed_benchmark(self, uri: str, pid: int | None = None) -> None:
|
|
67
|
+
"""Set the executed benchmark URI and process ID.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
uri: The benchmark URI string identifier
|
|
71
|
+
pid: Optional process ID (defaults to current process)
|
|
72
|
+
"""
|
|
73
|
+
if pid is None:
|
|
74
|
+
pid = os.getpid()
|
|
75
|
+
|
|
76
|
+
ret = self.lib.instrument_hooks_set_executed_benchmark(
|
|
77
|
+
self.instance, pid, uri.encode("ascii")
|
|
78
|
+
)
|
|
79
|
+
if ret != 0:
|
|
80
|
+
warnings.warn("Failed to set executed benchmark", RuntimeWarning)
|
|
81
|
+
|
|
82
|
+
def set_integration(self, name: str, version: str) -> None:
|
|
83
|
+
"""Set the integration name and version."""
|
|
84
|
+
ret = self.lib.instrument_hooks_set_integration(
|
|
85
|
+
self.instance, name.encode("ascii"), version.encode("ascii")
|
|
86
|
+
)
|
|
87
|
+
if ret != 0:
|
|
88
|
+
warnings.warn("Failed to set integration name and version", RuntimeWarning)
|
|
89
|
+
|
|
90
|
+
def is_instrumented(self) -> bool:
|
|
91
|
+
"""Check if simulation is active."""
|
|
92
|
+
return self.lib.instrument_hooks_is_instrumented(self.instance)
|
|
93
|
+
|
|
94
|
+
def set_feature(self, feature: int, enabled: bool) -> None:
|
|
95
|
+
"""Set a feature flag in the instrument hooks library.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
feature: The feature flag to set
|
|
99
|
+
enabled: Whether to enable or disable the feature
|
|
100
|
+
"""
|
|
101
|
+
self.lib.instrument_hooks_set_feature(feature, enabled)
|
|
102
|
+
|
|
103
|
+
def set_environment(self, section_name: str, key: str, value: str) -> None:
|
|
104
|
+
"""Register a key-value pair under a named section for environment collection.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
section_name: The section name (e.g. "Python")
|
|
108
|
+
key: The key (e.g. "version")
|
|
109
|
+
value: The value (e.g. "3.13.12")
|
|
110
|
+
"""
|
|
111
|
+
ret = self.lib.instrument_hooks_set_environment(
|
|
112
|
+
self.instance,
|
|
113
|
+
section_name.encode("utf-8"),
|
|
114
|
+
key.encode("utf-8"),
|
|
115
|
+
value.encode("utf-8"),
|
|
116
|
+
)
|
|
117
|
+
if ret != 0:
|
|
118
|
+
warnings.warn("Failed to set environment data", RuntimeWarning)
|
|
119
|
+
|
|
120
|
+
def set_environment_list(
|
|
121
|
+
self, section_name: str, key: str, values: list[str]
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Register a list of values under a named section for environment collection.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
section_name: The section name (e.g. "python")
|
|
127
|
+
key: The key (e.g. "build_args")
|
|
128
|
+
values: The list of string values
|
|
129
|
+
"""
|
|
130
|
+
encoded = [self.ffi.new("char[]", v.encode("utf-8")) for v in values]
|
|
131
|
+
c_values = self.ffi.new("char*[]", encoded)
|
|
132
|
+
ret = self.lib.instrument_hooks_set_environment_list(
|
|
133
|
+
self.instance,
|
|
134
|
+
section_name.encode("utf-8"),
|
|
135
|
+
key.encode("utf-8"),
|
|
136
|
+
c_values,
|
|
137
|
+
len(encoded),
|
|
138
|
+
)
|
|
139
|
+
if ret != 0:
|
|
140
|
+
warnings.warn("Failed to set environment list data", RuntimeWarning)
|
|
141
|
+
|
|
142
|
+
def write_environment(self, pid: int | None = None) -> None:
|
|
143
|
+
"""Flush all registered environment sections to disk.
|
|
144
|
+
|
|
145
|
+
Writes to $CODSPEED_PROFILE_FOLDER/environment-<pid>.json.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
pid: Optional process ID (defaults to current process)
|
|
149
|
+
"""
|
|
150
|
+
if pid is None:
|
|
151
|
+
pid = os.getpid()
|
|
152
|
+
ret = self.lib.instrument_hooks_write_environment(self.instance, pid)
|
|
153
|
+
if ret != 0:
|
|
154
|
+
warnings.warn("Failed to write environment data", RuntimeWarning)
|
|
155
|
+
|
|
156
|
+
def collect_and_write_python_environment(self) -> None:
|
|
157
|
+
"""Collect Python toolchain information and write it to disk."""
|
|
158
|
+
section = "python"
|
|
159
|
+
set_env = self.set_environment
|
|
160
|
+
|
|
161
|
+
# Core identity
|
|
162
|
+
set_env(section, "version", sys.version.strip())
|
|
163
|
+
set_env(section, "implementation", sys.implementation.name.strip())
|
|
164
|
+
set_env(section, "compiler", platform.python_compiler().strip())
|
|
165
|
+
|
|
166
|
+
config_vars = sysconfig.get_config_vars()
|
|
167
|
+
|
|
168
|
+
# Build arguments as a list
|
|
169
|
+
config_args = config_vars.get("CONFIG_ARGS", "")
|
|
170
|
+
if config_args:
|
|
171
|
+
build_args = [arg.strip() for arg in shlex.split(config_args)]
|
|
172
|
+
self.set_environment_list(section, "build_args", build_args)
|
|
173
|
+
|
|
174
|
+
# Performance-relevant build configuration as "KEY=value" list
|
|
175
|
+
_SYSCONFIG_KEYS = (
|
|
176
|
+
"abiflags",
|
|
177
|
+
"PY_ENABLE_SHARED",
|
|
178
|
+
"Py_GIL_DISABLED",
|
|
179
|
+
"Py_DEBUG",
|
|
180
|
+
"WITH_PYMALLOC",
|
|
181
|
+
"WITH_MIMALLOC",
|
|
182
|
+
"WITH_FREELISTS",
|
|
183
|
+
"HAVE_COMPUTED_GOTOS",
|
|
184
|
+
"Py_STATS",
|
|
185
|
+
"Py_TRACE_REFS",
|
|
186
|
+
"WITH_VALGRIND",
|
|
187
|
+
"WITH_DTRACE",
|
|
188
|
+
)
|
|
189
|
+
config_items = []
|
|
190
|
+
for key in _SYSCONFIG_KEYS:
|
|
191
|
+
value = config_vars.get(key)
|
|
192
|
+
if value is not None:
|
|
193
|
+
config_items.append(f"{key}={str(value).strip()}")
|
|
194
|
+
config_items.append(f"perf_trampoline={SUPPORTS_PERF_TRAMPOLINE}")
|
|
195
|
+
self.set_environment_list(section, "config", config_items)
|
|
196
|
+
|
|
197
|
+
self.write_environment()
|
{pytest_codspeed-4.3.0 → pytest_codspeed-4.4.0}/src/pytest_codspeed/instruments/hooks/build.py
RENAMED
|
@@ -36,6 +36,15 @@ void callgrind_start_instrumentation();
|
|
|
36
36
|
void callgrind_stop_instrumentation();
|
|
37
37
|
|
|
38
38
|
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_set_environment_list(InstrumentHooks *,
|
|
43
|
+
const char *section_name,
|
|
44
|
+
const char *key,
|
|
45
|
+
const char *const *values,
|
|
46
|
+
uint32_t count);
|
|
47
|
+
uint8_t instrument_hooks_write_environment(InstrumentHooks *, uint32_t pid);
|
|
39
48
|
""")
|
|
40
49
|
|
|
41
50
|
ffibuilder.set_source(
|
|
@@ -47,6 +56,8 @@ ffibuilder.set_source(
|
|
|
47
56
|
"src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c",
|
|
48
57
|
],
|
|
49
58
|
include_dirs=[str(includes_dir)],
|
|
59
|
+
# IMPORTANT: Keep in sync with instrument-hooks/ci.yml (COMMON_CFLAGS)
|
|
60
|
+
extra_compile_args=["-Wno-format-security"],
|
|
50
61
|
)
|
|
51
62
|
|
|
52
63
|
if __name__ == "__main__":
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from cffi import FFI
|
|
2
|
+
|
|
1
3
|
InstrumentHooksPointer = object
|
|
2
4
|
|
|
3
5
|
class lib:
|
|
@@ -31,5 +33,21 @@ class lib:
|
|
|
31
33
|
def callgrind_stop_instrumentation() -> int: ...
|
|
32
34
|
@staticmethod
|
|
33
35
|
def instrument_hooks_set_feature(feature: int, enabled: bool) -> None: ...
|
|
36
|
+
@staticmethod
|
|
37
|
+
def instrument_hooks_set_environment(
|
|
38
|
+
hooks: InstrumentHooksPointer, section_name: bytes, key: bytes, value: bytes
|
|
39
|
+
) -> int: ...
|
|
40
|
+
@staticmethod
|
|
41
|
+
def instrument_hooks_set_environment_list(
|
|
42
|
+
hooks: InstrumentHooksPointer,
|
|
43
|
+
section_name: bytes,
|
|
44
|
+
key: bytes,
|
|
45
|
+
values: FFI.CData,
|
|
46
|
+
count: int,
|
|
47
|
+
) -> int: ...
|
|
48
|
+
@staticmethod
|
|
49
|
+
def instrument_hooks_write_environment(
|
|
50
|
+
hooks: InstrumentHooksPointer, pid: int
|
|
51
|
+
) -> int: ...
|
|
34
52
|
|
|
35
53
|
LibType = type[lib]
|