headroom/tests/test_transforms
Parideboy a00fb6761e
fix(router): degrade to pure-Python detection on native panic (#1123) (#1260)
## Description

When the native (Rust) content detector panicked, the pyo3
`PanicException` (a `BaseException`, not `Exception`) escaped
`_detect_content` and surfaced as an HTTP 500 instead of degrading. This
catches `BaseException` (excluding control-flow exceptions) around the
native call and falls back to the pure-Python regex detector, logging a
single warning.

Closes #1123

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)

## Changes Made

- `headroom/transforms/content_router.py`: wrapped the native detect
call in `_detect_content` so any `BaseException` (except
`KeyboardInterrupt`/`SystemExit`/`GeneratorExit`) degrades to
`_regex_detect_content_type`, warning once via a module-level
`_detect_panic_warned` flag.
- `tests/test_transforms/test_detect_fallback_1123.py`: new regression
tests for RuntimeError fallback, BaseException-panic fallback, and
KeyboardInterrupt propagation.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] New tests added for new functionality

### Test Output

```text
$ pytest tests/test_transforms/test_detect_fallback_1123.py tests/test_transforms/test_content_router.py -q
54 passed
```

## Real Behavior Proof

- Environment: Windows 11, Python 3.12, pytest-asyncio 1.4.0
- Exact command / steps: Monkeypatched the native detector to raise
RuntimeError, a BaseException-derived fake panic, and KeyboardInterrupt,
then called `_detect_content`.
- Observed result: RuntimeError and the BaseException panic both degrade
to a valid regex detection result; KeyboardInterrupt still propagates.
54 tests pass.
- Not tested: Could not reproduce a real pyo3 panic in this build
(`pyo3_runtime` is not importable here), so the fallback is exercised
via simulated exceptions.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:57:05 -05:00
..
__init__.py Initial commit: Headroom SDK - LLM context optimization toolkit 2026-01-06 23:16:58 -08:00
test_code_compressor.py Fix CI test, bump to 0.5.7 2026-03-26 12:04:30 -07:00
test_content_router.py feat(proxy): support glob patterns in exclude_tools (#870) (#1259) 2026-06-22 18:55:22 -05:00
test_detect_fallback_1123.py fix(router): degrade to pure-Python detection on native panic (#1123) (#1260) 2026-06-22 18:57:05 -05:00
test_diff_compressor.py feat(rust): retire python diff_compressor, ship rust-only via pyo3 2026-04-26 09:15:37 -07:00
test_diff_compressor_rust_parity.py feat(rust): retire python diff_compressor, ship rust-only via pyo3 2026-04-26 09:15:37 -07:00
test_html_extractor.py fix(tests): skip HTML extractor tests when trafilatura not installed 2026-01-31 15:39:55 -08:00
test_kompress_compressor.py fix(proxy): make Kompress eager preload cache-only so a cold cache can't block startup (#783) 2026-06-11 12:53:03 -05:00
test_read_lifecycle.py feat(transforms): attribute read_lifecycle + smart_crush tags (#249) 2026-06-11 11:51:26 -05:00
test_smart_crusher_attribution.py feat(transforms): attribute read_lifecycle + smart_crush tags (#249) 2026-06-11 11:51:26 -05:00
test_smart_crusher_bugs.py feat(rust): retire python smart_crusher, ship rust-only via pyo3 2026-04-27 00:52:21 -07:00
test_smart_crusher_ccr_roundtrip.py chore(rust): SmartCrusher CCR marker injection + walker unification 2026-04-27 20:25:22 -07:00
test_smart_crusher_lossless_default.py feat(rust): SmartCrusher PR4 — lossless-first default + CCR-Dropped restoration 2026-04-27 16:30:22 -07:00
test_smart_crusher_rust_parity.py feat(rust): SmartCrusher PR4 — lossless-first default + CCR-Dropped restoration 2026-04-27 16:30:22 -07:00
test_tag_protector.py fix: A9 — tag protector discards wrap on placeholder loss 2026-05-02 18:01:24 -07:00
test_tree_sitter_thread_safety.py fix(transforms): use thread-local tree-sitter parsers to prevent pyo3 Unsendable panic (#604) 2026-06-10 18:30:00 -05:00