Skip to content

Commit 891626f

Browse files
committed
WS1
1 parent 6e68847 commit 891626f

File tree

8 files changed

+70
-3
lines changed

8 files changed

+70
-3
lines changed

codetracer-python-recorder/codetracer_python_recorder/session.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ def start(
8989
policy:
9090
Optional mapping of runtime policy overrides forwarded to
9191
:func:`configure_policy` before tracing begins. Keys match the policy
92-
keyword arguments (``on_recorder_error``, ``require_trace``, etc.).
92+
keyword arguments (``on_recorder_error``, ``require_trace``,
93+
``propagate_script_exit``, etc.).
9394
apply_env_policy:
9495
When ``True`` (default), refresh policy settings from environment
9596
variables via :func:`configure_policy_from_env` prior to applying

codetracer-python-recorder/src/policy.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod model;
88
pub use env::{
99
configure_policy_from_env, ENV_CAPTURE_IO, ENV_JSON_ERRORS, ENV_KEEP_PARTIAL_TRACE,
1010
ENV_LOG_FILE, ENV_LOG_LEVEL, ENV_MODULE_NAME_FROM_GLOBALS, ENV_ON_RECORDER_ERROR,
11-
ENV_REQUIRE_TRACE,
11+
ENV_PROPAGATE_SCRIPT_EXIT, ENV_REQUIRE_TRACE,
1212
};
1313
#[allow(unused_imports)]
1414
pub use ffi::{configure_policy_py, py_configure_policy_from_env, py_policy_snapshot};
@@ -43,6 +43,7 @@ mod tests {
4343
assert!(snap.io_capture.line_proxies);
4444
assert!(!snap.io_capture.fd_fallback);
4545
assert!(snap.module_name_from_globals);
46+
assert!(!snap.propagate_script_exit);
4647
}
4748

4849
#[test]
@@ -58,6 +59,7 @@ mod tests {
5859
update.io_capture_line_proxies = Some(true);
5960
update.io_capture_fd_fallback = Some(true);
6061
update.module_name_from_globals = Some(true);
62+
update.propagate_script_exit = Some(true);
6163

6264
apply_policy_update(update);
6365

@@ -71,6 +73,7 @@ mod tests {
7173
assert!(snap.io_capture.line_proxies);
7274
assert!(snap.io_capture.fd_fallback);
7375
assert!(snap.module_name_from_globals);
76+
assert!(snap.propagate_script_exit);
7477
reset_policy();
7578
}
7679

@@ -86,6 +89,7 @@ mod tests {
8689
std::env::set_var(ENV_JSON_ERRORS, "yes");
8790
std::env::set_var(ENV_CAPTURE_IO, "proxies,fd");
8891
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "true");
92+
std::env::set_var(ENV_PROPAGATE_SCRIPT_EXIT, "true");
8993

9094
configure_policy_from_env().expect("configure from env");
9195

@@ -101,6 +105,7 @@ mod tests {
101105
assert!(snap.io_capture.line_proxies);
102106
assert!(snap.io_capture.fd_fallback);
103107
assert!(snap.module_name_from_globals);
108+
assert!(snap.propagate_script_exit);
104109
reset_policy();
105110
}
106111

@@ -163,6 +168,7 @@ mod tests {
163168
ENV_JSON_ERRORS,
164169
ENV_CAPTURE_IO,
165170
ENV_MODULE_NAME_FROM_GLOBALS,
171+
ENV_PROPAGATE_SCRIPT_EXIT,
166172
] {
167173
std::env::remove_var(key);
168174
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub const ENV_JSON_ERRORS: &str = "CODETRACER_JSON_ERRORS";
2121
pub const ENV_CAPTURE_IO: &str = "CODETRACER_CAPTURE_IO";
2222
/// Environment variable toggling globals-based module name resolution.
2323
pub const ENV_MODULE_NAME_FROM_GLOBALS: &str = "CODETRACER_MODULE_NAME_FROM_GLOBALS";
24+
/// Environment variable toggling whether the recorder mirrors script exit codes.
25+
pub const ENV_PROPAGATE_SCRIPT_EXIT: &str = "CODETRACER_PROPAGATE_SCRIPT_EXIT";
2426

2527
/// Load policy overrides from environment variables.
2628
pub fn configure_policy_from_env() -> RecorderResult<()> {
@@ -66,6 +68,10 @@ pub fn configure_policy_from_env() -> RecorderResult<()> {
6668
update.module_name_from_globals = Some(parse_bool(&value)?);
6769
}
6870

71+
if let Ok(value) = env::var(ENV_PROPAGATE_SCRIPT_EXIT) {
72+
update.propagate_script_exit = Some(parse_bool(&value)?);
73+
}
74+
6975
apply_policy_update(update);
7076
Ok(())
7177
}
@@ -148,6 +154,7 @@ mod tests {
148154
std::env::set_var(ENV_JSON_ERRORS, "yes");
149155
std::env::set_var(ENV_CAPTURE_IO, "proxies,fd");
150156
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "true");
157+
std::env::set_var(ENV_PROPAGATE_SCRIPT_EXIT, "true");
151158

152159
configure_policy_from_env().expect("configure from env");
153160
let snap = policy_snapshot();
@@ -163,17 +170,20 @@ mod tests {
163170
assert!(snap.io_capture.line_proxies);
164171
assert!(snap.io_capture.fd_fallback);
165172
assert!(snap.module_name_from_globals);
173+
assert!(snap.propagate_script_exit);
166174
}
167175

168176
#[test]
169177
fn configure_policy_from_env_disables_module_name_from_globals() {
170178
let _guard = EnvGuard;
171179
reset_policy_for_tests();
172180
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "false");
181+
std::env::set_var(ENV_PROPAGATE_SCRIPT_EXIT, "false");
173182

174183
configure_policy_from_env().expect("configure from env");
175184
let snap = policy_snapshot();
176185
assert!(!snap.module_name_from_globals);
186+
assert!(!snap.propagate_script_exit);
177187
}
178188

179189
#[test]
@@ -202,6 +212,7 @@ mod tests {
202212
ENV_JSON_ERRORS,
203213
ENV_CAPTURE_IO,
204214
ENV_MODULE_NAME_FROM_GLOBALS,
215+
ENV_PROPAGATE_SCRIPT_EXIT,
205216
] {
206217
std::env::remove_var(key);
207218
}

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

Lines changed: 17 additions & 1 deletion
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, module_name_from_globals=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, propagate_script_exit=None))]
1515
pub fn configure_policy_py(
1616
on_recorder_error: Option<&str>,
1717
require_trace: Option<bool>,
@@ -22,6 +22,7 @@ pub fn configure_policy_py(
2222
io_capture_line_proxies: Option<bool>,
2323
io_capture_fd_fallback: Option<bool>,
2424
module_name_from_globals: Option<bool>,
25+
propagate_script_exit: Option<bool>,
2526
) -> PyResult<()> {
2627
let mut update = PolicyUpdate::default();
2728

@@ -69,6 +70,10 @@ pub fn configure_policy_py(
6970
update.module_name_from_globals = Some(value);
7071
}
7172

73+
if let Some(value) = propagate_script_exit {
74+
update.propagate_script_exit = Some(value);
75+
}
76+
7277
apply_policy_update(update);
7378
Ok(())
7479
}
@@ -106,6 +111,7 @@ pub fn py_policy_snapshot(py: Python<'_>) -> PyResult<PyObject> {
106111
"module_name_from_globals",
107112
snapshot.module_name_from_globals,
108113
)?;
114+
dict.set_item("propagate_script_exit", snapshot.propagate_script_exit)?;
109115

110116
let io_dict = PyDict::new(py);
111117
io_dict.set_item("line_proxies", snapshot.io_capture.line_proxies)?;
@@ -133,6 +139,7 @@ mod tests {
133139
Some(true),
134140
Some(true),
135141
Some(true),
142+
Some(true),
136143
)
137144
.expect("configure policy via PyO3 facade");
138145

@@ -152,6 +159,7 @@ mod tests {
152159
assert!(snap.io_capture.line_proxies);
153160
assert!(snap.io_capture.fd_fallback);
154161
assert!(snap.module_name_from_globals);
162+
assert!(snap.propagate_script_exit);
155163
reset_policy_for_tests();
156164
}
157165

@@ -168,6 +176,7 @@ mod tests {
168176
None,
169177
None,
170178
None,
179+
None,
171180
)
172181
.expect_err("invalid variant should error");
173182
// Ensure the error maps through map_recorder_error by checking the display text.
@@ -208,6 +217,7 @@ mod tests {
208217
Some(false),
209218
Some(false),
210219
Some(false),
220+
Some(true),
211221
)
212222
.expect("configure policy");
213223

@@ -224,6 +234,11 @@ mod tests {
224234
dict.contains("io_capture").expect("check io_capture key"),
225235
"expected io_capture in snapshot"
226236
);
237+
assert!(
238+
dict.contains("propagate_script_exit")
239+
.expect("check propagate_script_exit key"),
240+
"expected propagate_script_exit in snapshot"
241+
);
227242
});
228243
reset_policy_for_tests();
229244
}
@@ -241,6 +256,7 @@ mod tests {
241256
super::super::env::ENV_JSON_ERRORS,
242257
super::super::env::ENV_CAPTURE_IO,
243258
super::super::env::ENV_MODULE_NAME_FROM_GLOBALS,
259+
super::super::env::ENV_PROPAGATE_SCRIPT_EXIT,
244260
] {
245261
std::env::remove_var(key);
246262
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub struct RecorderPolicy {
7272
pub json_errors: bool,
7373
pub io_capture: IoCapturePolicy,
7474
pub module_name_from_globals: bool,
75+
pub propagate_script_exit: bool,
7576
}
7677

7778
impl Default for RecorderPolicy {
@@ -85,6 +86,7 @@ impl Default for RecorderPolicy {
8586
json_errors: false,
8687
io_capture: IoCapturePolicy::default(),
8788
module_name_from_globals: true,
89+
propagate_script_exit: false,
8890
}
8991
}
9092
}
@@ -128,6 +130,9 @@ impl RecorderPolicy {
128130
if let Some(module_name_from_globals) = update.module_name_from_globals {
129131
self.module_name_from_globals = module_name_from_globals;
130132
}
133+
if let Some(propagate_script_exit) = update.propagate_script_exit {
134+
self.propagate_script_exit = propagate_script_exit;
135+
}
131136
}
132137
}
133138

@@ -150,6 +155,7 @@ pub(crate) struct PolicyUpdate {
150155
pub(crate) io_capture_line_proxies: Option<bool>,
151156
pub(crate) io_capture_fd_fallback: Option<bool>,
152157
pub(crate) module_name_from_globals: Option<bool>,
158+
pub(crate) propagate_script_exit: Option<bool>,
153159
}
154160

155161
/// 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
@@ -401,6 +401,7 @@ mod tests {
401401
None,
402402
None,
403403
Some(false),
404+
Some(false),
404405
)
405406
.expect("reset recorder policy");
406407
}
@@ -633,6 +634,7 @@ result = compute()\n"
633634
Some(true),
634635
Some(false),
635636
Some(false),
637+
Some(false),
636638
)
637639
.expect("enable io capture proxies");
638640

@@ -724,6 +726,7 @@ result = compute()\n"
724726
Some(true),
725727
Some(true),
726728
Some(false),
729+
Some(false),
727730
)
728731
.expect("enable io capture with fd fallback");
729732

@@ -832,6 +835,7 @@ result = compute()\n"
832835
Some(true),
833836
Some(false),
834837
Some(false),
838+
Some(false),
835839
)
836840
.expect("enable proxies without fd fallback");
837841

@@ -2205,6 +2209,7 @@ snapshot()
22052209
None,
22062210
None,
22072211
Some(false),
2212+
Some(false),
22082213
)
22092214
.expect("enable require_trace policy");
22102215

@@ -2285,6 +2290,7 @@ snapshot()
22852290
None,
22862291
None,
22872292
Some(false),
2293+
Some(false),
22882294
)
22892295
.expect("enable keep_partial policy");
22902296

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def reset_policy() -> None:
1919
log_file="",
2020
json_errors=False,
2121
module_name_from_globals=True,
22+
propagate_script_exit=False,
2223
)
2324
yield
2425
codetracer.configure_policy(
@@ -29,6 +30,7 @@ def reset_policy() -> None:
2930
log_file="",
3031
json_errors=False,
3132
module_name_from_globals=True,
33+
propagate_script_exit=False,
3234
)
3335

3436

@@ -42,6 +44,7 @@ def test_configure_policy_overrides_values(tmp_path: Path) -> None:
4244
log_file=str(target_log),
4345
json_errors=True,
4446
module_name_from_globals=True,
47+
propagate_script_exit=True,
4548
)
4649

4750
snapshot = codetracer.policy_snapshot()
@@ -52,6 +55,7 @@ def test_configure_policy_overrides_values(tmp_path: Path) -> None:
5255
assert snapshot["log_file"] == str(target_log)
5356
assert snapshot["json_errors"] is True
5457
assert snapshot["module_name_from_globals"] is True
58+
assert snapshot["propagate_script_exit"] is True
5559

5660

5761
def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
@@ -63,6 +67,7 @@ def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Pat
6367
monkeypatch.setenv("CODETRACER_LOG_FILE", str(log_file))
6468
monkeypatch.setenv("CODETRACER_JSON_ERRORS", "yes")
6569
monkeypatch.setenv("CODETRACER_MODULE_NAME_FROM_GLOBALS", "true")
70+
monkeypatch.setenv("CODETRACER_PROPAGATE_SCRIPT_EXIT", "true")
6671

6772
codetracer.configure_policy_from_env()
6873

@@ -74,6 +79,7 @@ def test_policy_env_configuration(monkeypatch: pytest.MonkeyPatch, tmp_path: Pat
7479
assert snapshot["log_file"] == str(log_file)
7580
assert snapshot["json_errors"] is True
7681
assert snapshot["module_name_from_globals"] is True
82+
assert snapshot["propagate_script_exit"] is True
7783

7884

7985
def test_clearing_log_configuration(tmp_path: Path) -> None:
@@ -90,6 +96,7 @@ def test_session_start_applies_policy_overrides(tmp_path: Path) -> None:
9096
"log_file": tmp_path / "policy.log",
9197
"json_errors": True,
9298
"module_name_from_globals": True,
99+
"propagate_script_exit": True,
93100
}
94101

95102
session = session_api.start(tmp_path, policy=policy, apply_env_policy=False)
@@ -99,6 +106,7 @@ def test_session_start_applies_policy_overrides(tmp_path: Path) -> None:
99106
assert snapshot["log_file"] == str(tmp_path / "policy.log")
100107
assert snapshot["json_errors"] is True
101108
assert snapshot["module_name_from_globals"] is True
109+
assert snapshot["propagate_script_exit"] is True
102110
finally:
103111
session.stop()
104112

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Recorder Exit Code Policy – Status
2+
3+
## Relevant Design Docs
4+
- `design-docs/adr/0017-recorder-exit-code-policy.md`
5+
- `design-docs/recorder-exit-code-policy-implementation-plan.md`
6+
7+
## Workstream Progress
8+
-**WS1 – Policy & Configuration Plumbing:** Added `propagate_script_exit` across `RecorderPolicy`, `PolicyUpdate`, PyO3 bindings, env parsing, and Python helpers; introduced `CODETRACER_PROPAGATE_SCRIPT_EXIT`; updated Rust + Python unit coverage; rebuilt the dev wheel (`maturin develop --features integration-test`) and verified via `just test`.
9+
-**WS2 – CLI Behaviour & Warning Surface:** _Not started._
10+
-**WS3 – Documentation, Tooling, and Release Notes:** _Not started._
11+
12+
## Current Focus
13+
- Confirm no downstream consumers rely on the legacy policy snapshot schema; prepare CLI changes for WS2.

0 commit comments

Comments
 (0)