Skip to content

Commit 1dc81d2

Browse files
committed
Stage 5
1 parent 788d55c commit 1dc81d2

File tree

14 files changed

+50
-18
lines changed

14 files changed

+50
-18
lines changed

codetracer-python-recorder/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
99
- Balanced call-stack handling for generators, coroutines, and unwinding frames by subscribing to `PY_YIELD`, `PY_UNWIND`, `PY_RESUME`, and `PY_THROW`, mapping resume/throw events to `TraceWriter::register_call`, yield/unwind to `register_return`, and capturing `PY_THROW` arguments as `exception` using the existing value encoder. Added Python + Rust integration tests that drive `.send()`/`.throw()` on coroutines and generators to guarantee the trace stays balanced and that exception payloads are recorded.
1010

1111
### Changed
12-
- Module-level call events now prefer the frame's `__name__`, fall back to filter hints, `sys.path`, and package markers, and no longer depend on the legacy resolver/cache. This keeps `<__main__>` for direct scripts while package imports continue to emit `<pkg.mod>`. Updated Rust + Python tests and documentation to describe the new semantics and opt-in flag.
12+
- Module-level call events now prefer the frame's `__name__`, fall back to filter hints, `sys.path`, and package markers, and no longer depend on the legacy resolver/cache. The globals-derived naming flag now defaults to enabled so direct scripts record `<__main__>` while package imports emit `<pkg.mod>`, with CLI and environment overrides available for the legacy resolver.
1313

1414
## [0.2.0] - 2025-10-17
1515
### Added

codetracer-python-recorder/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ action = "drop"
8585
## Trace naming semantics
8686

8787
- Module-level activations no longer appear as the ambiguous `<module>` label. When the recorder sees `co_qualname == "<module>"`, it first reuses the frame's `__name__`, then falls back to trace-filter hints, `sys.path` roots, and package markers so scripts report `<__main__>` while real modules keep their dotted names (e.g., `<my_pkg.mod>` or `<boto3.session>`).
88+
- The globals-derived naming flow ships enabled by default; disable it temporarily with `--no-module-name-from-globals`, `codetracer.configure_policy(module_name_from_globals=False)`, or `CODETRACER_MODULE_NAME_FROM_GLOBALS=0` if you need to compare against the legacy resolver.
8889
- The angle-bracket convention remains for module entries so downstream tooling can distinguish top-level activations at a glance.
8990
- Traces will still emit `<module>` for synthetic filenames (`<stdin>`, `<string>`), frozen/importlib bootstrap frames, or exotic loaders that omit filenames entirely. This preserves previous behaviour when no reliable name exists.
9091

codetracer-python-recorder/codetracer_python_recorder/cli.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,11 @@ def _parse_args(argv: Sequence[str]) -> RecorderCLIConfig:
122122
)
123123
parser.add_argument(
124124
"--module-name-from-globals",
125-
action="store_true",
125+
action=argparse.BooleanOptionalAction,
126+
default=None,
126127
help=(
127-
"Derive module names from the Python frame's __name__ attribute. "
128-
"Intended for early opt-in while the feature flag is experimental."
128+
"Derive module names from the Python frame's __name__ attribute (default: enabled). "
129+
"Use '--no-module-name-from-globals' to fall back to the legacy resolver."
129130
),
130131
)
131132

@@ -189,8 +190,8 @@ def _parse_args(argv: Sequence[str]) -> RecorderCLIConfig:
189190
policy["io_capture_fd_fallback"] = True
190191
case other: # pragma: no cover - argparse choices block this
191192
parser.error(f"unsupported io-capture mode '{other}'")
192-
if known.module_name_from_globals:
193-
policy["module_name_from_globals"] = True
193+
if known.module_name_from_globals is not None:
194+
policy["module_name_from_globals"] = known.module_name_from_globals
194195

195196
return RecorderCLIConfig(
196197
trace_dir=trace_dir,

codetracer-python-recorder/resources/trace_filters/builtin_default.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ exec = "skip"
3434
reason = "Skip builtins module instrumentation"
3535

3636
[[scope.rules]]
37-
selector = 'pkg:glob:*_distutils_hack*'
37+
selector = 'pkg:literal:_distutils_hack'
3838
exec = "skip"
3939
reason = "Skip setuptools shim module"
4040

codetracer-python-recorder/src/policy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ mod tests {
4242
assert!(snap.log_file.is_none());
4343
assert!(snap.io_capture.line_proxies);
4444
assert!(!snap.io_capture.fd_fallback);
45-
assert!(!snap.module_name_from_globals);
45+
assert!(snap.module_name_from_globals);
4646
}
4747

4848
#[test]

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ mod tests {
165165
assert!(snap.module_name_from_globals);
166166
}
167167

168+
#[test]
169+
fn configure_policy_from_env_disables_module_name_from_globals() {
170+
let _guard = EnvGuard;
171+
reset_policy_for_tests();
172+
std::env::set_var(ENV_MODULE_NAME_FROM_GLOBALS, "false");
173+
174+
configure_policy_from_env().expect("configure from env");
175+
let snap = policy_snapshot();
176+
assert!(!snap.module_name_from_globals);
177+
}
178+
168179
#[test]
169180
fn parse_capture_io_handles_aliases() {
170181
assert_eq!(parse_capture_io("proxies+fd").unwrap(), (true, true));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl Default for RecorderPolicy {
8484
log_file: None,
8585
json_errors: false,
8686
io_capture: IoCapturePolicy::default(),
87-
module_name_from_globals: false,
87+
module_name_from_globals: true,
8888
}
8989
}
9090
}

codetracer-python-recorder/tests/python/test_monitoring_events.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,13 @@ def test_module_name_follows_globals_policy(tmp_path: Path) -> None:
227227
script.write_text("VALUE = 1\n", encoding="utf-8")
228228

229229
out_dir = ensure_trace_dir(tmp_path)
230-
codetracer.configure_policy(module_name_from_globals=True)
231-
232230
session = codetracer.start(out_dir, format=codetracer.TRACE_JSON, start_on_enter=script)
233231
try:
234232
runpy.run_path(str(script), run_name="__main__")
235233
finally:
236234
codetracer.flush()
237235
codetracer.stop()
238-
codetracer.configure_policy(module_name_from_globals=False)
236+
codetracer.configure_policy(module_name_from_globals=True)
239237

240238
parsed = _parse_trace(out_dir)
241239
names = [f["name"] for f in parsed.functions]

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,19 @@ def test_parse_args_enables_module_name_from_globals(tmp_path: Path) -> None:
172172
assert config.policy_overrides == {
173173
"module_name_from_globals": True,
174174
}
175+
176+
177+
def test_parse_args_disables_module_name_from_globals(tmp_path: Path) -> None:
178+
script = tmp_path / "entry.py"
179+
_write_script(script)
180+
181+
config = _parse_args(
182+
[
183+
"--no-module-name-from-globals",
184+
str(script),
185+
]
186+
)
187+
188+
assert config.policy_overrides == {
189+
"module_name_from_globals": False,
190+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def reset_policy() -> None:
1818
log_level="",
1919
log_file="",
2020
json_errors=False,
21-
module_name_from_globals=False,
21+
module_name_from_globals=True,
2222
)
2323
yield
2424
codetracer.configure_policy(
@@ -28,7 +28,7 @@ def reset_policy() -> None:
2828
log_level="",
2929
log_file="",
3030
json_errors=False,
31-
module_name_from_globals=False,
31+
module_name_from_globals=True,
3232
)
3333

3434

0 commit comments

Comments
 (0)