Skip to content

Commit 33c1bfa

Browse files
committed
feat: remove cffi in favor of native extension
1 parent 70df77a commit 33c1bfa

File tree

6 files changed

+264
-95
lines changed

6 files changed

+264
-95
lines changed

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ dev = [
6363
pytest11 = { codspeed = "pytest_codspeed.plugin" }
6464

6565
[build-system]
66-
requires = ["setuptools >= 61", "cffi >= 1.17.1"]
66+
requires = ["setuptools >= 61"]
6767
build-backend = "setuptools.build_meta"
6868

6969
[tool.setuptools]
7070
license-files = [] # Workaround of https://github.com/astral-sh/uv/issues/9513
7171

72+
[tool.setuptools.package-data]
73+
pytest_codspeed = [
74+
"instruments/hooks/instrument-hooks/includes/*.h",
75+
]
76+
7277
[tool.setuptools.dynamic]
7378
version = { attr = "pytest_codspeed.__version__" }
7479

setup.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
1-
import importlib.util
21
import os
32
import platform
4-
from pathlib import Path
53

6-
from setuptools import setup
7-
8-
build_path = Path(__file__).parent / "src/pytest_codspeed/instruments/hooks/build.py"
9-
10-
spec = importlib.util.spec_from_file_location("build", build_path)
11-
assert spec is not None, "The spec should be initialized"
12-
build = importlib.util.module_from_spec(spec)
13-
assert spec.loader is not None, "The loader should be initialized"
14-
spec.loader.exec_module(build)
4+
from setuptools import Extension, setup
155

166
system = platform.system()
177
current_arch = platform.machine()
@@ -39,22 +29,26 @@
3929
"The extension is required but the current platform is not supported"
4030
)
4131

42-
ffi_extension = build.ffibuilder.distutils_extension()
43-
ffi_extension.optional = not IS_EXTENSION_REQUIRED
32+
# Build native C extension
33+
native_extension = Extension(
34+
"pytest_codspeed.instruments.hooks.dist_instrument_hooks",
35+
sources=[
36+
"src/pytest_codspeed/instruments/hooks/instrument_hooks_module.c",
37+
"src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c",
38+
],
39+
include_dirs=["src/pytest_codspeed/instruments/hooks/instrument-hooks/includes"],
40+
optional=not IS_EXTENSION_REQUIRED,
41+
)
4442

4543
print(
4644
"CodSpeed native extension is "
4745
+ ("required" if IS_EXTENSION_REQUIRED else "optional")
4846
)
4947

5048
setup(
51-
package_data={
52-
"pytest_codspeed": [
53-
"instruments/hooks/instrument-hooks/includes/*.h",
54-
"instruments/hooks/instrument-hooks/dist/*.c",
55-
]
56-
},
5749
ext_modules=(
58-
[ffi_extension] if IS_EXTENSION_BUILDABLE and not SKIP_EXTENSION_BUILD else []
50+
[native_extension]
51+
if IS_EXTENSION_BUILDABLE and not SKIP_EXTENSION_BUILD
52+
else []
5953
),
6054
)

src/pytest_codspeed/instruments/hooks/__init__.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
import warnings
66
from typing import TYPE_CHECKING
77

8-
from pytest_codspeed.utils import SUPPORTS_PERF_TRAMPOLINE
9-
108
if TYPE_CHECKING:
11-
from .dist_instrument_hooks import InstrumentHooksPointer, LibType
9+
from typing import Any
10+
11+
from pytest_codspeed.utils import SUPPORTS_PERF_TRAMPOLINE
1212

1313

1414
class InstrumentHooks:
15-
"""Zig library wrapper class providing benchmark measurement functionality."""
15+
"""Native library wrapper class providing benchmark measurement functionality."""
1616

17-
lib: LibType
18-
instance: InstrumentHooksPointer
17+
_module: Any
18+
_instance: Any
1919

2020
def __init__(self) -> None:
2121
if os.environ.get("CODSPEED_ENV") is None:
@@ -25,31 +25,31 @@ def __init__(self) -> None:
2525
)
2626

2727
try:
28-
from .dist_instrument_hooks import lib # type: ignore
28+
from . import dist_instrument_hooks # type: ignore
2929
except ImportError as e:
3030
raise RuntimeError(f"Failed to load instrument hooks library: {e}") from e
31-
self.lib = lib
31+
self._module = dist_instrument_hooks
3232

33-
self.instance = self.lib.instrument_hooks_init()
34-
if self.instance == 0:
33+
self._instance = self._module.instrument_hooks_init()
34+
if self._instance is None:
3535
raise RuntimeError("Failed to initialize CodSpeed instrumentation library.")
3636

3737
if SUPPORTS_PERF_TRAMPOLINE:
3838
sys.activate_stack_trampoline("perf") # type: ignore
3939

4040
def __del__(self):
41-
if hasattr(self, "lib") and hasattr(self, "instance"):
42-
self.lib.instrument_hooks_deinit(self.instance)
41+
# Don't manually deinit - let the capsule destructor handle it
42+
pass
4343

4444
def start_benchmark(self) -> None:
4545
"""Start a new benchmark measurement."""
46-
ret = self.lib.instrument_hooks_start_benchmark(self.instance)
46+
ret = self._module.instrument_hooks_start_benchmark(self._instance)
4747
if ret != 0:
4848
warnings.warn("Failed to start benchmark measurement", RuntimeWarning)
4949

5050
def stop_benchmark(self) -> None:
5151
"""Stop the current benchmark measurement."""
52-
ret = self.lib.instrument_hooks_stop_benchmark(self.instance)
52+
ret = self._module.instrument_hooks_stop_benchmark(self._instance)
5353
if ret != 0:
5454
warnings.warn("Failed to stop benchmark measurement", RuntimeWarning)
5555

@@ -63,20 +63,28 @@ def set_executed_benchmark(self, uri: str, pid: int | None = None) -> None:
6363
if pid is None:
6464
pid = os.getpid()
6565

66-
ret = self.lib.instrument_hooks_set_executed_benchmark(
67-
self.instance, pid, uri.encode("ascii")
66+
ret = self._module.instrument_hooks_set_executed_benchmark(
67+
self._instance, pid, uri.encode("ascii")
6868
)
6969
if ret != 0:
7070
warnings.warn("Failed to set executed benchmark", RuntimeWarning)
7171

7272
def set_integration(self, name: str, version: str) -> None:
7373
"""Set the integration name and version."""
74-
ret = self.lib.instrument_hooks_set_integration(
75-
self.instance, name.encode("ascii"), version.encode("ascii")
74+
ret = self._module.instrument_hooks_set_integration(
75+
self._instance, name.encode("ascii"), version.encode("ascii")
7676
)
7777
if ret != 0:
7878
warnings.warn("Failed to set integration name and version", RuntimeWarning)
7979

8080
def is_instrumented(self) -> bool:
8181
"""Check if instrumentation is active."""
82-
return self.lib.instrument_hooks_is_instrumented(self.instance)
82+
return self._module.instrument_hooks_is_instrumented(self._instance)
83+
84+
def callgrind_start_instrumentation(self) -> None:
85+
"""Start callgrind instrumentation."""
86+
self._module.callgrind_start_instrumentation()
87+
88+
def callgrind_stop_instrumentation(self) -> None:
89+
"""Stop callgrind instrumentation."""
90+
self._module.callgrind_stop_instrumentation()

src/pytest_codspeed/instruments/hooks/build.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

0 commit comments

Comments
 (0)