Repository navigation
Conversation
Modules can declare their encoding (PEP 263) using any name that Python
accepts, such as `latin-1`, `utf_8` or `euc_jp`. But encoding_rs only knows
the WHATWG labels (`latin1`, `utf-8`, `euc-jp`...), so these names weren't
recognized, and building the graph panicked with:
pyo3_runtime.PanicException: called `Result::unwrap()` on an `Err`
value: PyErr { type: <class 'TypeError'>, value: TypeError('function
takes exactly 5 arguments (1 given)'), traceback: None }
Normalize the declared name like Python does before looking it up.
Modules that really can't be decoded also caused this panic, as the error
was created as a UnicodeDecodeError, which can't be created from just a
message, and was then unwrapped. Raise it as a UnicodeError instead, which
keeps the message naming the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN
Merging this PR will not alter performance
Comparing Footnotes
|
The previous commit stored file read errors in GrimpError as a PyErr.
GrimpError's Debug and Display implementations then call into Python, so
the Rust test binaries started linking libpython, and `just test-rust`
failed wherever libpython isn't on the loader path:
error while loading shared libraries: libpython3.13.so.1.0: cannot
open shared object file: No such file or directory
Make FileSystem::read return Rust errors instead (GrimpError::FileNotFound
and GrimpError::UndecodableFile), and convert them to FileNotFoundError
and UnicodeError where they reach Python, like the other GrimpError
variants. The exception types and messages are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN
There was a problem hiding this comment.
🟡 Changes recommended
Latin-1 aliases are incorrectly decoded as Windows-1252, producing source text that differs from Python.
1 open finding
What changed in this PR
Fixes import-scanning panics caused by Python encoding aliases and undecodable source files.
Changes:
- Normalizes declared encoding names and propagates decoding failures.
- Maps filesystem errors to appropriate Python exceptions.
- Adds functional coverage and release documentation.
| File | Description |
|---|---|
rust/src/filesystem.rs |
Adds encoding normalization and structured read errors. |
rust/src/errors.rs |
Maps new errors to Python exceptions. |
rust/src/import_scanning.rs |
Propagates filesystem read failures. |
tests/functional/test_encoding_handling.py |
Tests Python-specific encoding names. |
tests/functional/test_error_handling.py |
Tests decoding failure behavior. |
CHANGELOG.rst |
Documents the fix. |
AUTHORS.rst |
Adds the contributor. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } else if ["latin-1", "iso-8859-1", "iso-latin-1"] | ||
| .into_iter() | ||
| .any(is_spelling_of) | ||
| { | ||
| "iso-8859-1" |
d5466d3
into
python-grimp:fix-encoding-declarations
|
Thanks for your contribution! I merged this into a different branch to make a couple of tweaks, but it's now on |

Fixes #321.
Since 3.10,
build_graphpanics withTypeError('function takes exactly 5 arguments (1 given)')on:latin-1,latin_1,utf_8,utf-8-sig,euc_jporiso8859_15;Changes:
RealBasicFileSystem::readnormalizes the declared name like Python does (_get_normal_nameinLib/tokenize.py, and_vs-) when encoding_rs doesn't recognize it as is. Names that encoding_rs already knows are looked up as before.UnicodeError, with the existing messages that name the file, e.g.Failed to decode file /path/to/pkg/broken.py as UTF-8: invalid utf-8 sequence of 1 bytes from index 5. Before, they were created asUnicodeDecodeError, which can't be created from a message alone.FileSystem::readreturns Rust errors (newGrimpError::FileNotFoundandGrimpError::UndecodableFilevariants), which are converted toFileNotFoundErrorandUnicodeErrorwhere they reach Python, like the otherGrimpErrorvariants.scan_for_importspropagates them instead of unwrapping.The new functional tests use
tmp_path, as each case needs its own package: one test per encoding name above, and one per kind of decoding failure (invalid UTF-8, invalid for the declared encoding, unknown encoding).This isn't on a hot path: names that encoding_rs knows take the same route as before, and only the error handling changed otherwise.
Encoding names that Python accepts but that still aren't WHATWG labels after normalizing, such as
cp932ormac_roman, now raise a clearUnicodeErrorrather than panicking. Supporting them would need a fallback to Python's codecs.latestsection at the top ofCHANGELOG.rst. (If it's not there, add it.)AUTHORS.rst.just full-check. (I ran the lint (Python, plus Rust with the pinned 1.97.0 toolchain), the docs build,cargo test, and the Python tests on 3.10 to 3.14. I didn't run them on 3.14t, 3.15 or 3.15t.)🤖 Generated with Claude Code
https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN