Skip to content

Commit 2ace132

Browse files
committed
Address Codex comment
1 parent 620fbd4 commit 2ace132

File tree

1 file changed

+110
-9
lines changed

1 file changed

+110
-9
lines changed

codetracer-python-recorder/src/session/bootstrap.rs

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ impl TraceSessionBootstrap {
6565
enverr!(ErrorCode::Io, "failed to collect program metadata")
6666
.with_context("details", err.to_string())
6767
})?;
68-
let trace_filter = load_trace_filter(explicit_trace_filters, &metadata.program)?;
68+
let trace_filter =
69+
load_trace_filter(explicit_trace_filters, activation_path, &metadata.program)?;
6970
Ok(Self {
7071
trace_directory: trace_directory.to_path_buf(),
7172
format,
@@ -164,11 +165,12 @@ pub fn collect_program_metadata(py: Python<'_>) -> PyResult<ProgramMetadata> {
164165

165166
fn load_trace_filter(
166167
explicit: Option<&[PathBuf]>,
168+
activation_path: Option<&Path>,
167169
program: &str,
168170
) -> Result<Option<Arc<TraceFilterEngine>>> {
169171
let mut chain: Vec<PathBuf> = Vec::new();
170172

171-
if let Some(default) = discover_default_trace_filter(program)? {
173+
if let Some(default) = discover_default_trace_filter(activation_path, program)? {
172174
chain.push(default);
173175
}
174176

@@ -183,17 +185,42 @@ fn load_trace_filter(
183185
Ok(Some(Arc::new(TraceFilterEngine::new(config))))
184186
}
185187

186-
fn discover_default_trace_filter(program: &str) -> Result<Option<PathBuf>> {
187-
let start_dir = resolve_program_directory(program)?;
188-
let mut current: Option<&Path> = Some(start_dir.as_path());
189-
while let Some(dir) = current {
188+
fn discover_default_trace_filter(
189+
activation_path: Option<&Path>,
190+
program: &str,
191+
) -> Result<Option<PathBuf>> {
192+
if let Some(path) = activation_path {
193+
let activation_dir = resolve_activation_directory(path)?;
194+
if let Some(found) = discover_filter_near(activation_dir) {
195+
return Ok(Some(found));
196+
}
197+
}
198+
199+
let program_dir = resolve_program_directory(program)?;
200+
if let Some(found) = discover_filter_near(program_dir) {
201+
return Ok(Some(found));
202+
}
203+
Ok(None)
204+
}
205+
206+
fn discover_filter_near(mut dir: PathBuf) -> Option<PathBuf> {
207+
loop {
190208
let candidate = dir.join(TRACE_FILTER_DIR).join(TRACE_FILTER_FILE);
191209
if matches!(fs::metadata(&candidate), Ok(metadata) if metadata.is_file()) {
192-
return Ok(Some(candidate));
210+
return Some(candidate);
211+
}
212+
if !dir.pop() {
213+
break;
193214
}
194-
current = dir.parent();
195215
}
196-
Ok(None)
216+
None
217+
}
218+
219+
fn resolve_activation_directory(path: &Path) -> Result<PathBuf> {
220+
if path.as_os_str().is_empty() {
221+
return current_directory();
222+
}
223+
resolve_path_directory(path)
197224
}
198225

199226
fn resolve_program_directory(program: &str) -> Result<PathBuf> {
@@ -203,6 +230,10 @@ fn resolve_program_directory(program: &str) -> Result<PathBuf> {
203230
}
204231

205232
let path = Path::new(trimmed);
233+
resolve_path_directory(path)
234+
}
235+
236+
fn resolve_path_directory(path: &Path) -> Result<PathBuf> {
206237
if path.is_absolute() {
207238
if path.is_dir() {
208239
return Ok(path.to_path_buf());
@@ -434,6 +465,76 @@ mod tests {
434465
});
435466
}
436467

468+
#[test]
469+
fn prepare_bootstrap_prefers_activation_path_filter_over_host_program() {
470+
Python::with_gil(|py| {
471+
let project = tempdir().expect("project");
472+
let project_root = project.path();
473+
let trace_dir = project_root.join("trace-out");
474+
475+
let wrapper_dir = project_root.join("wrapper");
476+
std::fs::create_dir_all(&wrapper_dir).expect("create wrapper dir");
477+
let wrapper_script = wrapper_dir.join("launcher.py");
478+
std::fs::write(&wrapper_script, "print('wrapper')\n").expect("write wrapper");
479+
480+
let target_dir = project_root.join("target");
481+
std::fs::create_dir_all(&target_dir).expect("create target dir");
482+
let target_script = target_dir.join("app.py");
483+
std::fs::write(&target_script, "print('target')\n").expect("write target");
484+
485+
let filters_dir = target_dir.join(TRACE_FILTER_DIR);
486+
std::fs::create_dir_all(&filters_dir).expect("create filter dir");
487+
let filter_path = filters_dir.join(TRACE_FILTER_FILE);
488+
std::fs::write(
489+
&filter_path,
490+
r#"
491+
[meta]
492+
name = "target"
493+
version = 1
494+
495+
[scope]
496+
default_exec = "trace"
497+
default_value_action = "allow"
498+
499+
[[scope.rules]]
500+
selector = "pkg:target"
501+
exec = "trace"
502+
value_default = "allow"
503+
"#,
504+
)
505+
.expect("write project filter");
506+
507+
let sys = py.import("sys").expect("import sys");
508+
let original = sys.getattr("argv").expect("argv").unbind();
509+
let argv = PyList::new(
510+
py,
511+
[wrapper_script.to_str().expect("utf8 path"), "--forwarded"],
512+
)
513+
.expect("argv");
514+
sys.setattr("argv", argv).expect("set argv");
515+
516+
let result = TraceSessionBootstrap::prepare(
517+
py,
518+
trace_dir.as_path(),
519+
"json",
520+
Some(target_script.as_path()),
521+
None,
522+
);
523+
sys.setattr("argv", original.bind(py))
524+
.expect("restore argv");
525+
526+
let bootstrap = result.expect("bootstrap");
527+
let engine = bootstrap.trace_filter().expect("filter engine");
528+
let summary = engine.summary();
529+
assert_eq!(summary.entries.len(), 2);
530+
assert_eq!(
531+
summary.entries[0].path,
532+
PathBuf::from("<inline:builtin-default>")
533+
);
534+
assert_eq!(summary.entries[1].path, filter_path);
535+
});
536+
}
537+
437538
#[test]
438539
fn prepare_bootstrap_merges_explicit_trace_filters() {
439540
Python::with_gil(|py| {

0 commit comments

Comments
 (0)