Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,43 @@ on:

jobs:
nix-tests:
name: Testing on Python ${{matrix.python-version}}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10","3.11","3.12","3.13"]
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-25.05
extra_nix_config: |
experimental-features = nix-command flakes
- name: Run tests via Nix
run: nix develop --command just test
- name: Build Rust module and run tests via Nix
run: nix develop --command bash -lc 'just venv ${{matrix.python-version}} dev test'

rust-tests:
name: Rust module test on ${{ matrix.os }} (Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["10", "11", "12", "13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.${{ matrix.python-version }}
- uses: astral-sh/setup-uv@v4
- uses: messense/maturin-action@v1
with:
command: build
args: --interpreter python3.${{ matrix.python-version }} -m crates/codetracer-python-recorder/Cargo.toml --release
- name: Install and test built wheel with uv (pytest)
shell: bash
run: |
v=${{matrix.python-version}}
file=(crates/codetracer-python-recorder/target/wheels/*.whl)
file="${file[0]}"
uv run -p python3.$v --with "${file}" --with pytest -- python -m pytest crates/codetracer-python-recorder/test -q
# rust-tests:
# name: Rust module test on ${{ matrix.os }} (Python ${{ matrix.python-version }})
# runs-on: ${{ matrix.os }}
# strategy:
# matrix:
# os: [ubuntu-latest, macos-latest, windows-latest]
# python-version: ["10", "11", "12", "13"]
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-python@v5
# with:
# python-version: 3.${{ matrix.python-version }}
# - uses: astral-sh/setup-uv@v4
# - uses: messense/maturin-action@v1
# with:
# command: build
# args: --interpreter python3.${{ matrix.python-version }} -m crates/codetracer-python-recorder/Cargo.toml --release
# - name: Install and test built wheel with uv (pytest)
# shell: bash
# run: |
# v=${{matrix.python-version}}
# file=(crates/codetracer-python-recorder/target/wheels/*.whl)
# file="${file[0]}"
# uv run -p python3.$v --with "${file}" --with pytest -- \
# python -m pytest crates/codetracer-python-recorder/test tests/test_codetracer_api.py -q
58 changes: 34 additions & 24 deletions Justfile
Original file line number Diff line number Diff line change
@@ -1,48 +1,58 @@
default:
@just --list

# Development helpers for the monorepo

# Python version used for development
PYTHON_DEFAULT_VERSION := "3.13"

# Python versions used for multi-version testing/building with uv
PY_VERSIONS := "3.10 3.11 3.12 3.13"
PY_SHORT_VERSIONS := "10 11 12 13"

# Print toolchain versions to verify the dev environment
env:
uv --version
python3 --version
cargo --version
rustc --version
maturin --version

# Create a local virtualenv for Python tooling
venv:
test -d .venv || python3 -m venv .venv
clean:
rm -rf .venv **/__pycache__ **/*.pyc **/*.pyo **/.pytest_cache
rm -rf codetracer-python-recorder/target codetracer-python-recorder/**/*.so


# Build and develop-install the Rust-backed Python module
build-rust:
test -d .venv || python3 -m venv .venv
VIRTUAL_ENV=.venv maturin develop -m crates/codetracer-python-recorder/Cargo.toml
# Create a clean local virtualenv for Python tooling (without editable packages installed)
venv version=PYTHON_DEFAULT_VERSION:
uv sync -p {{version}}

# Smoke test the Rust module after build
smoke-rust:
.venv/bin/python -m pip install -U pip pytest
.venv/bin/python -m pytest crates/codetracer-python-recorder/test -q
# Build the module in dev mode
dev:
uv run --directory codetracer-python-recorder maturin develop --uv

# Run the Python test suite for the pure-Python recorder
# Run unit tests of dev build
test:
python3 -m unittest discover -v
uv run --group dev --group test pytest

# Run the test suite across multiple Python versions using uv
test-uv-all:
uv python install {{PY_VERSIONS}}
for v in {{PY_VERSIONS}}; do uv run -p "$v" -m unittest discover -v; done
# Run tests only on the pure recorder
test-pure:
uv run --group dev --group test pytest codetracer-pure-python-recorder

# Build the module in release mode
build:
just venv \
uv run --directory codetracer-python-recorder maturin build --release

# Build wheels for all target Python versions with maturin
build-rust-uv-all:
for v in {{PY_VERSIONS}}; do \
maturin build --interpreter "python$v" -m crates/codetracer-python-recorder/Cargo.toml --release; \
done
build-all:
just venv
uv run --directory codetracer-python-recorder maturin build --release --interpreter {{PY_VERSIONS}}

# Smoke the built Rust wheels across versions using uv
smoke-rust-uv-all:
test-all:
for v in {{PY_SHORT_VERSIONS}}; do \
file=(crates/codetracer-python-recorder/target/wheels/codetracer_python_recorder-*-cp3$v-cp3$v-*.whl); \
file=(codetracer-python-recorder/target/wheels/codetracer_python_recorder-*-cp3$v-cp3$v-*.whl); \
file="${file[0]}"; \
uv run -p "python3.$v" --with "${file}" --with pytest -- python -m pytest crates/codetracer-python-recorder/test -q; \
uv run -p "python3.$v" --with "${file}" --with pytest -- pytest -q; \
done
28 changes: 28 additions & 0 deletions codetracer-pure-python-recorder/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "codetracer-pure-python-recorder"
version = "0.1.0"
description = "Pure-Python prototype recorder producing CodeTracer traces"
authors = [{name = "Metacraft Labs Ltd"}]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
]

[tool.setuptools]
py-modules = ["trace"]
package-dir = {"" = "src"}

[tool.setuptools.packages.find]
where = ["src"]

[project.scripts]
codetracer-record = "codetracer_pure_python_recorder.cli:main"
codetracer-record-pure = "codetracer_pure_python_recorder.cli:main"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.so
13 changes: 13 additions & 0 deletions codetracer-python-recorder/codetracer_python_recorder/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""High-level tracing API built on a Rust backend.

This module exposes a minimal interface for starting and stopping
runtime traces. The heavy lifting is delegated to the
`codetracer_python_recorder` Rust extension which will eventually hook
into `runtime_tracing` and `sys.monitoring`. For now the Rust side only
maintains placeholder state and performs no actual tracing.
"""

from .api import *

__all__ = api.__all__

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pathlib import Path
from typing import Iterable, Iterator, Optional

from codetracer_python_recorder import (
from .codetracer_python_recorder import (
flush_tracing as _flush_backend,
is_tracing as _is_tracing_backend,
start_tracing as _start_backend,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
[build-system]
requires = ["maturin>=1.5,<2"]
build-backend = "maturin"

[project]
name = "codetracer-python-recorder"
version = "0.1.0"
description = "Rust-backed Python module for CodeTracer recording (PyO3)"
description = "Low-level Rust-backed Python module for CodeTracer recording (PyO3)"
authors = [{name = "Metacraft Labs Ltd"}]
license = {text = "MIT"}
requires-python = ">=3.8"
Expand All @@ -20,3 +16,7 @@ classifiers = [
# Build the PyO3 extension module
bindings = "pyo3"
# Use the library name as the Python import name: codetracer_python_recorder

[build-system]
requires = ["maturin>=1.5,<2"]
build-backend = "maturin"
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,12 @@ fn flush_tracing() -> PyResult<()> {
Ok(())
}

/// Trivial function kept for smoke tests verifying the module builds.
#[pyfunction]
fn hello() -> PyResult<String> {
Ok("Hello from codetracer-python-recorder (Rust)".to_string())
}

/// Python module definition.
#[pymodule]
fn codetracer_python_recorder(_py: Python<'_>, m: Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(start_tracing, &m)?)?;
m.add_function(wrap_pyfunction!(stop_tracing, &m)?)?;
m.add_function(wrap_pyfunction!(is_tracing, &m)?)?;
m.add_function(wrap_pyfunction!(flush_tracing, &m)?)?;
m.add_function(wrap_pyfunction!(hello, &m)?)?;
fn codetracer_python_recorder(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(start_tracing, m)?)?;
m.add_function(wrap_pyfunction!(stop_tracing, m)?)?;
m.add_function(wrap_pyfunction!(is_tracing, m)?)?;
m.add_function(wrap_pyfunction!(flush_tracing, m)?)?;
Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import unittest
from pathlib import Path

import codetracer
import codetracer_python_recorder as codetracer


class TracingApiTests(unittest.TestCase):
Expand Down Expand Up @@ -34,7 +34,7 @@ def test_context_manager(self) -> None:
self.assertFalse(codetracer.is_tracing())

def test_environment_auto_start(self) -> None:
script = "import codetracer, sys; sys.stdout.write(str(codetracer.is_tracing()))"
script = "import codetracer_python_recorder as codetracer, sys; sys.stdout.write(str(codetracer.is_tracing()))"
with tempfile.TemporaryDirectory() as tmpdir:
env = os.environ.copy()
env["CODETRACER_TRACE"] = str(Path(tmpdir) / "trace.bin")
Expand Down
5 changes: 5 additions & 0 deletions codetracer-python-recorder/test/test_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import codetracer_python_recorder as m

def test_is_tracing_returns_false() -> None:
assert m.is_tracing() == False

5 changes: 0 additions & 5 deletions crates/codetracer-python-recorder/test/test_smoke.py

This file was deleted.

8 changes: 0 additions & 8 deletions crates/codetracer-python-recorder/uv.lock

This file was deleted.

7 changes: 6 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
ruff
black
mypy
python3Packages.pytest

# Rust toolchain for the Rust-backed Python module
cargo
Expand All @@ -38,6 +37,12 @@
uv
pkg-config
];

shellHook = ''
# When having more than one python version in the shell this variable breaks `maturin build`
# because it always leads to having SOABI be the one from the highest version
unset PYTHONPATH
'';
};
});
};
Expand Down
48 changes: 27 additions & 21 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "codetracer-pure-python-recorder"
name = "codetracer-python-recorders"
version = "0.1.0"
description = "Pure-Python prototype recorder producing CodeTracer traces"
authors = [{name = "Metacraft Labs Ltd"}]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
dependencies = []

[tool.uv]
managed = true

[tool.uv.workspace]
members = [
"codetracer-python-recorder",
"codetracer-pure-python-recorder",
]

[tool.setuptools]
py-modules = ["trace"]
package-dir = {"" = "src"}
[tool.uv.sources]
codetracer-python-recorder = {workspace= true}
codetracer-pure-python-recorder = {workspace= true}

[tool.ruff]
line-length = 100

[tool.setuptools.packages.find]
where = ["src"]
[tool.pyright]
typeCheckingMode = "basic"

[project.scripts]
codetracer-record = "codetracer_pure_python_recorder.cli:main"
codetracer-record-pure = "codetracer_pure_python_recorder.cli:main"
[dependency-groups]
dev = [
"pytest>=8.3.5",
]

test = [
"codetracer-python-recorder",
"codetracer-pure-python-recorder"
]
Loading