Skip to content

Commit 0089b51

Browse files
committed
docs: propose CodeObjectWrapper design
1 parent b2bec88 commit 0089b51

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

design-docs/code-object.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Code Object Wrapper Design
2+
3+
## Overview
4+
5+
The Python Monitoring API delivers a generic `CodeType` object to every tracing callback. The current `Tracer` trait surfaces this object as `&Bound<'_, PyAny>`, forcing every implementation to perform attribute lookups and type conversions manually. This document proposes a `CodeObjectWrapper` type that exposes a stable, typed interface to the underlying code object while minimizing per-event overhead.
6+
7+
## Goals
8+
- Provide a strongly typed API for common `CodeType` attributes needed by tracers and recorders.
9+
- Ensure lookups are cheap by caching values and avoiding repeated Python attribute access.
10+
- Maintain a stable identity for each code object to correlate events across callbacks.
11+
- Avoid relying on the unstable `PyCodeObject` layout from the C API.
12+
13+
## Non-Goals
14+
- Full re‑implementation of every `CodeType` attribute. Only the fields required for tracing and time‑travel debugging are exposed.
15+
- Direct mutation of `CodeType` objects. The wrapper offers read‑only access.
16+
17+
## Proposed API
18+
19+
```rs
20+
pub struct CodeObjectWrapper {
21+
/// Strong reference to the Python `CodeType` object.
22+
obj: Py<PyAny>,
23+
/// Stable identity equivalent to `id(code)`.
24+
id: usize,
25+
/// Lazily populated cache for expensive lookups.
26+
cache: CodeObjectCache,
27+
}
28+
29+
pub struct CodeObjectCache {
30+
filename: OnceCell<String>,
31+
qualname: OnceCell<String>,
32+
firstlineno: OnceCell<u32>,
33+
argcount: OnceCell<u16>,
34+
flags: OnceCell<u32>,
35+
/// Mapping of instruction offsets to line numbers.
36+
lines: OnceCell<Vec<LineEntry>>,
37+
}
38+
39+
pub struct LineEntry {
40+
pub offset: u32,
41+
pub line: u32,
42+
}
43+
44+
impl CodeObjectWrapper {
45+
/// Construct from a generic Python object. Computes `id` eagerly.
46+
pub fn new(py: Python<'_>, obj: &Bound<'_, PyAny>) -> Self;
47+
48+
/// Accessors fetch from the cache or perform a one‑time lookup under the GIL.
49+
pub fn filename<'py>(&'py self, py: Python<'py>) -> PyResult<&'py str>;
50+
pub fn qualname<'py>(&'py self, py: Python<'py>) -> PyResult<&'py str>;
51+
pub fn first_line(&self, py: Python<'_>) -> PyResult<u32>;
52+
pub fn arg_count(&self, py: Python<'_>) -> PyResult<u16>;
53+
pub fn flags(&self, py: Python<'_>) -> PyResult<u32>;
54+
55+
/// Return the source line for a given instruction offset using a binary search on `lines`.
56+
pub fn line_for_offset(&self, py: Python<'_>, offset: u32) -> PyResult<Option<u32>>;
57+
58+
/// Expose the stable identity for cross‑event correlation.
59+
pub fn id(&self) -> usize;
60+
}
61+
```
62+
63+
### Trait Integration
64+
65+
The `Tracer` trait will be adjusted so every callback receives `&CodeObjectWrapper` instead of a generic `&Bound<'_, PyAny>`:
66+
67+
```rs
68+
fn on_line(&mut self, py: Python<'_>, code: &CodeObjectWrapper, lineno: u32);
69+
fn on_py_start(&mut self, py: Python<'_>, code: &CodeObjectWrapper, offset: i32);
70+
// ...and similarly for the remaining callbacks.
71+
```
72+
73+
## Performance Considerations
74+
- `Py<PyAny>` allows cloning the wrapper without holding the GIL, enabling cheap event propagation.
75+
- Fields are loaded lazily and stored inside `OnceCell` containers to avoid repeated attribute lookups.
76+
- `line_for_offset` memoizes the full line table the first time it is requested; subsequent calls perform an in‑memory binary search.
77+
- Storing strings and small integers directly in the cache eliminates conversion cost on hot paths.
78+
79+
## Open Questions
80+
- Additional attributes such as `co_consts` or `co_varnames` may be required for richer debugging features; these can be added later as new `OnceCell` fields.
81+
- Thread‑safety requirements may necessitate wrapping the cache in `UnsafeCell` or providing internal mutability strategies compatible with `Send`/`Sync`.
82+
83+
## References
84+
- [Python `CodeType` objects](https://docs.python.org/3/reference/datamodel.html#code-objects)
85+
- [Python monitoring API](https://docs.python.org/3/library/sys.monitoring.html)

0 commit comments

Comments
 (0)