Skip to content

Commit 2169f96

Browse files
authored
Tracer trait (#25)
Added a `Tracer` trait for implementing Python tracers in Rust using the sys.monitoring API. Works only for Python >=3.12.. Added a test tracer `print_tracer` in tests/print_tracer.rs. The trait is not complete, it contains methods for just two events: CALL and LINE. We'll finish it in another PR.
2 parents cf420fc + 533f193 commit 2169f96

File tree

10 files changed

+670
-5
lines changed

10 files changed

+670
-5
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
1. Create a trait for a tracer implemented using the Python monitoring API. The methods of the trait should correspond to the events that one can subscribe to via the API.
2+
3+
Here's a sketch of the design of the trait. We want to support tracers which implement only some of the methods.
4+
5+
``rs
6+
use bitflags::bitflags;
7+
8+
bitflags! {
9+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10+
pub struct EventMask: u32 {
11+
const CALL = 1 << 0;
12+
//CODEX: write this
13+
...
14+
}
15+
}
16+
17+
#[non_exhaustive]
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19+
pub enum Event {
20+
Call,
21+
//CODEX: write this
22+
...
23+
// Non-exhaustive for forward compatibility.
24+
}
25+
26+
pub trait Tracer: Send {
27+
/// Tell the dispatcher what you want to receive. Default: nothing (fast path).
28+
fn interest(&self) -> EventMask { EventMask::empty() }
29+
30+
// Default no-ops let implementers pick only what they need.
31+
fn on_call(&mut self, ...) {}
32+
//CODEX: write this
33+
}
34+
35+
// Example tracer: only cares about CALL.
36+
struct CallsOnly;
37+
impl Tracer for CallsOnly {
38+
fn interest(&self) -> EventMask { EventMask::CALL | ... }
39+
fn on_call(&mut self, ...) { println!("call: {:?}", ...); }
40+
...
41+
}
42+
43+
// Dispatcher checks the mask to avoid vtable calls it doesn’t need.
44+
pub struct Dispatcher {
45+
tracer: Box<dyn Tracer>,
46+
mask: EventMask,
47+
}
48+
49+
impl Dispatcher {
50+
pub fn new(tracer: Box<dyn Tracer>) -> Self {
51+
let mask = tracer.interest();
52+
Self { tracer, mask }
53+
}
54+
55+
pub fn dispatch_call(&mut self, ...) {
56+
if self.mask.contains(EventMask::CALL) {
57+
self.tracer.on_call(...);
58+
}
59+
60+
}
61+
// CODEX: ... same for other events
62+
}
63+
``
64+
65+
2. Create code which takes a trait implementation and hooks it to the global tracing. Follow the design-docs for the specific API that needs to be implemented.
66+
67+
3. Create a test implementation of the trait which prints text to stdout. Run the test implementation. Note that the test implementation should not be included in the final build
68+
artefact, it will be used only for testing.
69+
70+
4. Update the testing framework to be able to use `just test` also for Rust tests. Specifically we want to run our test implementation from point 2. using `just test`
71+
72+
Refer to the design-docs folder for the current planned design. Add/update files in the folder to match what was implemented in this task.
73+
74+
75+
--- FOLLOW UP TASK ---
76+
1. In codetracer-python-recorder/Cargo.toml, move pyo3’s extension-module feature to an optional crate feature and enable it only for release builds.
77+
2. Update Justfile (and CI scripts) to run cargo test --no-default-features so the test binary links with the Python C library.
78+
3. Add pyo3 with auto-initialize under [dev-dependencies] if tests require the interpreter to be initialized automatically.
79+
--- FOLLOW UP TASK ---
80+
thread 'tracer_prints_on_call' panicked at tests/print_tracer.rs:21:51:\ncalled `Result::unwrap()` on an `Err` value: PyErr { type: <class 'AttributeError'>, value: AttributeError("module 'sys' has no attribute 'monitoring'"), traceback: None }\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n\nfailures:\n tracer_prints_on_call\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
python-version: ["3.10","3.11","3.12","3.13"]
16+
#python-version: ["3.10","3.11","3.12","3.13"]
17+
python-version: ["3.12","3.13"]
1718
steps:
1819
- uses: actions/checkout@v4
1920
- uses: cachix/install-nix-action@v27

Justfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ dev:
3232
uv run --directory codetracer-python-recorder maturin develop --uv
3333

3434
# Run unit tests of dev build
35-
test:
36-
uv run --group dev --group test pytest
35+
test: cargo-test py-test
36+
37+
# Run Rust unit tests without default features to link Python C library
38+
cargo-test:
39+
uv run cargo test --manifest-path codetracer-python-recorder/Cargo.toml --no-default-features
3740

41+
py-test:
42+
uv run --group dev --group test pytest
43+
3844
# Run tests only on the pure recorder
3945
test-pure:
4046
uv run --group dev --group test pytest codetracer-pure-python-recorder

0 commit comments

Comments
 (0)