Skip to content

Commit b19c1ed

Browse files
committed
Stage 0
1 parent 0701527 commit b19c1ed

File tree

9 files changed

+109
-3
lines changed

9 files changed

+109
-3
lines changed

codetracer-python-recorder/codetracer_python_recorder/cli.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ def _parse_args(argv: Sequence[str]) -> RecorderCLIConfig:
120120
"'proxies+fd' also mirrors raw file-descriptor writes."
121121
),
122122
)
123+
parser.add_argument(
124+
"--module-name-from-globals",
125+
action="store_true",
126+
help=(
127+
"Derive module names from the Python frame's __name__ attribute. "
128+
"Intended for early opt-in while the feature flag is experimental."
129+
),
130+
)
123131

124132
known, remainder = parser.parse_known_args(argv)
125133
pending: list[str] = list(remainder)
@@ -181,6 +189,8 @@ def _parse_args(argv: Sequence[str]) -> RecorderCLIConfig:
181189
policy["io_capture_fd_fallback"] = True
182190
case other: # pragma: no cover - argparse choices block this
183191
parser.error(f"unsupported io-capture mode '{other}'")
192+
if known.module_name_from_globals:
193+
policy["module_name_from_globals"] = True
184194

185195
return RecorderCLIConfig(
186196
trace_dir=trace_dir,

codetracer-python-recorder/src/policy.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ mod model;
77
#[allow(unused_imports)]
88
pub use env::{
99
configure_policy_from_env, ENV_CAPTURE_IO, ENV_JSON_ERRORS, ENV_KEEP_PARTIAL_TRACE,
10-
ENV_LOG_FILE, ENV_LOG_LEVEL, ENV_ON_RECORDER_ERROR, ENV_REQUIRE_TRACE,
10+
ENV_LOG_FILE, ENV_LOG_LEVEL, ENV_MODULE_NAME_FROM_GLOBALS, ENV_ON_RECORDER_ERROR,
11+
ENV_REQUIRE_TRACE,
1112
};
1213
#[allow(unused_imports)]
1314
pub use ffi::{configure_policy_py, py_configure_policy_from_env, py_policy_snapshot};
@@ -41,6 +42,7 @@ mod tests {
4142
assert!(snap.log_file.is_none());
4243
assert!(snap.io_capture.line_proxies);
4344
assert!(!snap.io_capture.fd_fallback);
45+
assert!(!snap.module_name_from_globals);
4446
}
4547

4648
#[test]
@@ -55,6 +57,7 @@ mod tests {
5557
update.json_errors = Some(true);
5658
update.io_capture_line_proxies = Some(true);
5759
update.io_capture_fd_fallback = Some(true);
60+
update.module_name_from_globals = Some(true);
5861

5962
apply_policy_update(update);
6063

@@ -67,6 +70,7 @@ mod tests {
6770
assert!(snap.json_errors);
6871
assert!(snap.io_capture.line_proxies);
6972
assert!(snap.io_capture.fd_fallback);
73+
assert!(snap.module_name_from_globals);
7074
reset_policy();
7175
}
7276

@@ -81,6 +85,7 @@ mod tests {
8185
std::env::set_var(ENV_LOG_FILE, "/tmp/out.log");
8286
std::env::set_var(ENV_JSON_ERRORS, "yes");
8387
std::env::set_var(ENV_CAPTURE_IO, "proxies,fd");
88+
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "true");
8489

8590
configure_policy_from_env().expect("configure from env");
8691

@@ -95,6 +100,7 @@ mod tests {
95100
assert!(snap.json_errors);
96101
assert!(snap.io_capture.line_proxies);
97102
assert!(snap.io_capture.fd_fallback);
103+
assert!(snap.module_name_from_globals);
98104
reset_policy();
99105
}
100106

@@ -156,6 +162,7 @@ mod tests {
156162
ENV_LOG_FILE,
157163
ENV_JSON_ERRORS,
158164
ENV_CAPTURE_IO,
165+
ENV_MODULE_NAME_FROM_GLOBALS,
159166
] {
160167
std::env::remove_var(key);
161168
}

codetracer-python-recorder/src/policy/env.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub const ENV_LOG_FILE: &str = "CODETRACER_LOG_FILE";
1919
pub const ENV_JSON_ERRORS: &str = "CODETRACER_JSON_ERRORS";
2020
/// Environment variable toggling IO capture strategies.
2121
pub const ENV_CAPTURE_IO: &str = "CODETRACER_CAPTURE_IO";
22+
/// Environment variable toggling globals-based module name resolution.
23+
pub const ENV_MODULE_NAME_FROM_GLOBALS: &str = "CODETRACER_MODULE_NAME_FROM_GLOBALS";
2224

2325
/// Load policy overrides from environment variables.
2426
pub fn configure_policy_from_env() -> RecorderResult<()> {
@@ -60,6 +62,10 @@ pub fn configure_policy_from_env() -> RecorderResult<()> {
6062
update.io_capture_fd_fallback = Some(fd_fallback);
6163
}
6264

65+
if let Ok(value) = env::var(ENV_MODULE_NAME_FROM_GLOBALS) {
66+
update.module_name_from_globals = Some(parse_bool(&value)?);
67+
}
68+
6369
apply_policy_update(update);
6470
Ok(())
6571
}
@@ -141,6 +147,7 @@ mod tests {
141147
std::env::set_var(ENV_LOG_FILE, "/tmp/out.log");
142148
std::env::set_var(ENV_JSON_ERRORS, "yes");
143149
std::env::set_var(ENV_CAPTURE_IO, "proxies,fd");
150+
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "true");
144151

145152
configure_policy_from_env().expect("configure from env");
146153
let snap = policy_snapshot();
@@ -155,6 +162,7 @@ mod tests {
155162
assert!(snap.json_errors);
156163
assert!(snap.io_capture.line_proxies);
157164
assert!(snap.io_capture.fd_fallback);
165+
assert!(snap.module_name_from_globals);
158166
}
159167

160168
#[test]
@@ -182,6 +190,7 @@ mod tests {
182190
ENV_LOG_FILE,
183191
ENV_JSON_ERRORS,
184192
ENV_CAPTURE_IO,
193+
ENV_MODULE_NAME_FROM_GLOBALS,
185194
] {
186195
std::env::remove_var(key);
187196
}

codetracer-python-recorder/src/policy/ffi.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::path::PathBuf;
1111
use std::str::FromStr;
1212

1313
#[pyfunction(name = "configure_policy")]
14-
#[pyo3(signature = (on_recorder_error=None, require_trace=None, keep_partial_trace=None, log_level=None, log_file=None, json_errors=None, io_capture_line_proxies=None, io_capture_fd_fallback=None))]
14+
#[pyo3(signature = (on_recorder_error=None, require_trace=None, keep_partial_trace=None, log_level=None, log_file=None, json_errors=None, io_capture_line_proxies=None, io_capture_fd_fallback=None, module_name_from_globals=None))]
1515
pub fn configure_policy_py(
1616
on_recorder_error: Option<&str>,
1717
require_trace: Option<bool>,
@@ -21,6 +21,7 @@ pub fn configure_policy_py(
2121
json_errors: Option<bool>,
2222
io_capture_line_proxies: Option<bool>,
2323
io_capture_fd_fallback: Option<bool>,
24+
module_name_from_globals: Option<bool>,
2425
) -> PyResult<()> {
2526
let mut update = PolicyUpdate::default();
2627

@@ -64,6 +65,10 @@ pub fn configure_policy_py(
6465
update.io_capture_fd_fallback = Some(value);
6566
}
6667

68+
if let Some(value) = module_name_from_globals {
69+
update.module_name_from_globals = Some(value);
70+
}
71+
6772
apply_policy_update(update);
6873
Ok(())
6974
}
@@ -97,6 +102,10 @@ pub fn py_policy_snapshot(py: Python<'_>) -> PyResult<PyObject> {
97102
dict.set_item("log_file", py.None())?;
98103
}
99104
dict.set_item("json_errors", snapshot.json_errors)?;
105+
dict.set_item(
106+
"module_name_from_globals",
107+
snapshot.module_name_from_globals,
108+
)?;
100109

101110
let io_dict = PyDict::new(py);
102111
io_dict.set_item("line_proxies", snapshot.io_capture.line_proxies)?;
@@ -123,6 +132,7 @@ mod tests {
123132
Some(true),
124133
Some(true),
125134
Some(true),
135+
Some(true),
126136
)
127137
.expect("configure policy via PyO3 facade");
128138

@@ -141,13 +151,24 @@ mod tests {
141151
assert!(snap.json_errors);
142152
assert!(snap.io_capture.line_proxies);
143153
assert!(snap.io_capture.fd_fallback);
154+
assert!(snap.module_name_from_globals);
144155
reset_policy_for_tests();
145156
}
146157

147158
#[test]
148159
fn configure_policy_py_rejects_invalid_on_recorder_error() {
149160
reset_policy_for_tests();
150-
let err = configure_policy_py(Some("unknown"), None, None, None, None, None, None, None)
161+
let err = configure_policy_py(
162+
Some("unknown"),
163+
None,
164+
None,
165+
None,
166+
None,
167+
None,
168+
None,
169+
None,
170+
None,
171+
)
151172
.expect_err("invalid variant should error");
152173
// Ensure the error maps through map_recorder_error by checking the display text.
153174
let message = Python::with_gil(|py| err.value(py).to_string());
@@ -186,6 +207,7 @@ mod tests {
186207
Some(true),
187208
Some(false),
188209
Some(false),
210+
Some(false),
189211
)
190212
.expect("configure policy");
191213

@@ -218,6 +240,7 @@ mod tests {
218240
super::super::env::ENV_LOG_FILE,
219241
super::super::env::ENV_JSON_ERRORS,
220242
super::super::env::ENV_CAPTURE_IO,
243+
super::super::env::ENV_MODULE_NAME_FROM_GLOBALS,
221244
] {
222245
std::env::remove_var(key);
223246
}

codetracer-python-recorder/src/policy/model.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub struct RecorderPolicy {
7171
pub log_file: Option<PathBuf>,
7272
pub json_errors: bool,
7373
pub io_capture: IoCapturePolicy,
74+
pub module_name_from_globals: bool,
7475
}
7576

7677
impl Default for RecorderPolicy {
@@ -83,6 +84,7 @@ impl Default for RecorderPolicy {
8384
log_file: None,
8485
json_errors: false,
8586
io_capture: IoCapturePolicy::default(),
87+
module_name_from_globals: false,
8688
}
8789
}
8890
}
@@ -123,6 +125,9 @@ impl RecorderPolicy {
123125
// fd fallback requires proxies to be on.
124126
self.io_capture.fd_fallback = fd_fallback && self.io_capture.line_proxies;
125127
}
128+
if let Some(module_name_from_globals) = update.module_name_from_globals {
129+
self.module_name_from_globals = module_name_from_globals;
130+
}
126131
}
127132
}
128133

@@ -144,6 +149,7 @@ pub(crate) struct PolicyUpdate {
144149
pub(crate) json_errors: Option<bool>,
145150
pub(crate) io_capture_line_proxies: Option<bool>,
146151
pub(crate) io_capture_fd_fallback: Option<bool>,
152+
pub(crate) module_name_from_globals: Option<bool>,
147153
}
148154

149155
/// Snapshot the current policy.

codetracer-python-recorder/src/runtime/tracer/runtime_tracer.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ mod tests {
370370
Some(false),
371371
None,
372372
None,
373+
Some(false),
373374
)
374375
.expect("reset recorder policy");
375376
}
@@ -593,6 +594,7 @@ result = compute()\n"
593594
Some(false),
594595
Some(true),
595596
Some(false),
597+
Some(false),
596598
)
597599
.expect("enable io capture proxies");
598600

@@ -682,6 +684,7 @@ result = compute()\n"
682684
Some(false),
683685
Some(true),
684686
Some(true),
687+
Some(false),
685688
)
686689
.expect("enable io capture with fd fallback");
687690

@@ -788,6 +791,7 @@ result = compute()\n"
788791
Some(false),
789792
Some(true),
790793
Some(false),
794+
Some(false),
791795
)
792796
.expect("enable proxies without fd fallback");
793797

@@ -2142,6 +2146,7 @@ snapshot()
21422146
Some(false),
21432147
None,
21442148
None,
2149+
Some(false),
21452150
)
21462151
.expect("enable require_trace policy");
21472152

@@ -2219,6 +2224,7 @@ snapshot()
22192224
Some(false),
22202225
None,
22212226
None,
2227+
Some(false),
22222228
)
22232229
.expect("enable keep_partial policy");
22242230

codetracer-python-recorder/tests/python/unit/test_cli.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,19 @@ def test_parse_args_enables_io_capture_fd_mirroring(tmp_path: Path) -> None:
156156
"io_capture_line_proxies": True,
157157
"io_capture_fd_fallback": True,
158158
}
159+
160+
161+
def test_parse_args_enables_module_name_from_globals(tmp_path: Path) -> None:
162+
script = tmp_path / "entry.py"
163+
_write_script(script)
164+
165+
config = _parse_args(
166+
[
167+
"--module-name-from-globals",
168+
str(script),
169+
]
170+
)
171+
172+
assert config.policy_overrides == {
173+
"module_name_from_globals": True,
174+
}

codetracer-python-recorder/tests/python/unit/test_policy_configuration.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def reset_policy() -> None:
1818
log_level="",
1919
log_file="",
2020
json_errors=False,
21+
module_name_from_globals=False,
2122
)
2223
yield
2324
codetracer.configure_policy(
@@ -27,6 +28,7 @@ def reset_policy() -> None:
2728
log_level="",
2829
log_file="",
2930
json_errors=False,
31+
module_name_from_globals=False,
3032
)
3133

3234

@@ -39,6 +41,7 @@ def test_configure_policy_overrides_values(tmp_path: Path) -> None:
3941
log_level="info",
4042
log_file=str(target_log),
4143
json_errors=True,
44+
module_name_from_globals=True,
4245
)
4346

4447
snapshot = codetracer.policy_snapshot()
@@ -48,6 +51,7 @@ def test_configure_policy_overrides_values(tmp_path: Path) -> None:
4851
assert snapshot["log_level"] == "info"
4952
assert snapshot["log_file"] == str(target_log)
5053
assert snapshot["json_errors"] is True
54+
assert snapshot["module_name_from_globals"] is True
5155

5256

5357
def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
@@ -58,6 +62,7 @@ def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Pat
5862
log_file = tmp_path / "policy.log"
5963
monkeypatch.setenv("CODETRACER_LOG_FILE", str(log_file))
6064
monkeypatch.setenv("CODETRACER_JSON_ERRORS", "yes")
65+
monkeypatch.setenv("CODETRACER_MODULE_NAME_FROM_GLOBALS", "true")
6166

6267
codetracer.configure_policy_from_env()
6368

@@ -68,6 +73,7 @@ def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Pat
6873
assert snapshot["log_level"] == "debug"
6974
assert snapshot["log_file"] == str(log_file)
7075
assert snapshot["json_errors"] is True
76+
assert snapshot["module_name_from_globals"] is True
7177

7278

7379
def test_clearing_log_configuration(tmp_path: Path) -> None:
@@ -83,6 +89,7 @@ def test_session_start_applies_policy_overrides(tmp_path: Path) -> None:
8389
"on_recorder_error": "disable",
8490
"log_file": tmp_path / "policy.log",
8591
"json_errors": True,
92+
"module_name_from_globals": True,
8693
}
8794

8895
session = session_api.start(tmp_path, policy=policy, apply_env_policy=False)
@@ -91,6 +98,7 @@ def test_session_start_applies_policy_overrides(tmp_path: Path) -> None:
9198
assert snapshot["on_recorder_error"] == "disable"
9299
assert snapshot["log_file"] == str(tmp_path / "policy.log")
93100
assert snapshot["json_errors"] is True
101+
assert snapshot["module_name_from_globals"] is True
94102
finally:
95103
session.stop()
96104

0 commit comments

Comments
 (0)