bf: validate --eval-filter up-front, fail with a clear error (#440)#441
Open
yarikoptic wants to merge 2 commits into
Open
bf: validate --eval-filter up-front, fail with a clear error (#440)#441yarikoptic wants to merge 2 commits into
yarikoptic wants to merge 2 commits into
Conversation
`con-duct ls -e EXPR` used to call `eval(EXPR, ...)` per-file inside a broad `except Exception` block. A stray U+00A0 non-breaking space in EXPR (easy to produce with macOS Option+Space or when copy-pasting from a browser or document) then surfaced as one misleading "Failed to load file <_io.TextIOWrapper name=...>: invalid non-printable character U+00A0 (<string>, line 1)" warning per file, blaming the JSON files for a filter-expression typo. Compile the filter once up-front via a new `compile_eval_filter()` helper. On `SyntaxError`, raise `ValueError` with the offending expression and an explicit hint about hidden non-printable characters like U+00A0. `ls()` catches that, logs it via `lgr.error`, and returns exit code 2 -- so the user gets a single, clear message with no traceback and no per-file noise. Reuse the compiled `CodeType` in the per-file `eval()` loop, which also avoids re-parsing the expression once per file. Co-Authored-By: Claude Code 2.1.209 / Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #441 +/- ##
==========================================
- Coverage 91.08% 91.01% -0.07%
==========================================
Files 15 15
Lines 1245 1258 +13
Branches 170 171 +1
==========================================
+ Hits 1134 1145 +11
- Misses 77 78 +1
- Partials 34 35 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes confusing per-file “Failed to load file …” noise when con-duct ls --eval-filter contains a syntax error (notably hidden U+00A0 non-breaking spaces), by compiling/validating the expression once up-front and failing with a single clear error message and exit code 2.
Changes:
- Add
compile_eval_filter()to compile--eval-filteronce (with<eval-filter>filename) and convertSyntaxErrorinto a user-facingValueError. - Update
ls()to validate the filter before globbing/opening any files and to exit cleanly (rc=2) on invalid expressions. - Update
load_duct_runs()to accept a precompiledCodeTypeand reuse it in the per-fileeval()loop; add tests covering the new behavior and the gh-440 regression.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/con_duct/ls.py |
Adds upfront filter compilation and updates ls() / load_duct_runs() to reuse a compiled filter and fail cleanly on invalid expressions. |
test/test_ls.py |
Adds unit/regression tests for filter compilation, fast failure before file I/O, and ls() exit code/logging behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Replace three literal U+00A0 non-breaking spaces in the test source with ` ` escapes. A literal invisible byte in the source is fragile (an editor, pre-commit hook, or accidental copy-paste could silently strip or replace it — the very failure mode this test is guarding against); the escape re-materialises as the same U+00A0 at runtime, so the test still asserts identical behavior. - Fix nonstandard spelling "mis-classified" → "misclassified" in the `compile_eval_filter` docstring and a comment in `test_load_duct_runs_fails_fast_on_bad_filter`. Spotted by Copilot review on PR #441. Co-Authored-By: Claude Code 2.1.209 / Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
con-duct ls -e EXPRused to calleval(EXPR, ...)inside the per-file loopof
load_duct_runs(), inside a broadexcept Exceptionblock. The reporterin #440 had a hidden U+00A0 non-breaking space in their filter expression
(easy to produce on macOS with Option+Space, or when copy-pasting from a
browser/document). The resulting
SyntaxErrorwas caught, misattributedto each JSON file, and logged as:
— which sent the user hunting through their JSON logs for corruption that
was never there.
This PR centralises filter compilation so a bad expression fails once,
up-front, with a clear message, before any file is opened.
compile_eval_filter()helper compiles the expression once viacompile(..., '<eval-filter>', 'eval'). OnSyntaxErrorit raisesValueErrornaming the offending expression and explicitly hinting atU+00A0 / macOS Option+Space as a common cause.
ls()calls it first thing, catchesValueError, logs it vialgr.error, and returns exit code 2 — no traceback, no per-file noise.load_duct_runs()now accepts either a string (which it compiles) or apre-compiled
CodeType, and reuses it in the per-fileeval()loop —also avoids re-parsing the expression once per file.
After the fix, the reporter's command produces:
and exits with code 2.
Test plan
tox -e py3 -- test/test_ls.py— 29 passed (23 pre-existing + 6 new)tox -- test/— 397 passedtox -e lint— cleantox -e typing— cleanprintf '%b') now prints the single ERROR above and exits 2.New tests added to
test/test_ls.py:test_compile_eval_filter_none_returns_nonetest_compile_eval_filter_validtest_compile_eval_filter_rejects_nbsp(regression test for running con-duct ls on Mac leads to many error messages #440)test_compile_eval_filter_rejects_syntax_errortest_load_duct_runs_fails_fast_on_bad_filter— verifies no file iseven opened when the filter is invalid, and no per-file "Failed to load
file" warning is emitted.
test_ls_exits_cleanly_on_bad_filter— verifies exit code 2 and auser-facing ERROR log line mentioning
U+00A0.