Skip to content

Decode ShExC as UTF-8 first; use chardet only as a fallback - #117

Merged
ericprud merged 1 commit into
masterfrom
shexc-utf8-first
Oct 11, 2026
Merged

ericprud merged 1 commit into
masterfrom
shexc-utf8-first

Conversation

@ericprud

Copy link
Copy Markdown
Collaborator

load_shex_file() (in pyshex/utils/schema_loader.py) decoded with whatever chardet guessed whenever its confidence was above 0.9. chardet only samples the start of the data, so a large file that is ASCII for a long stretch before its first non-ASCII character gets reported as ascii with confidence 1.0, and loading crashes:

$ shexeval https://hl7.org/fhir/R4/observation-example-haplotype2.ttl https://hl7.org/fhir/R4/fhir.shex ...
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 253640

(chardet 7.4.3 on FHIR R4's 690 KB fhir.shex.)

Fix: ShExC is UTF-8 by definition, so decode as UTF-8 first (utf-8-sig, which also strips a BOM). Only if that fails, use chardet's guess, with latin-1 as a last resort that can't fail. This also fixes a second crash: a short non-UTF-8 file got low confidence from chardet, so the old code sent it to the UTF-8 default and failed. chardet also no longer runs over every schema, only over files that aren't valid UTF-8.

Tests (in test_schema_loader.py, using tmp_path so nothing tracked is written):

  • UTF-8 after ~320 KB of ASCII: decodes and parses (failed before with the ascii error)
  • A Latin-1 file: loads without crashing (failed before with a utf-8 error)
  • A UTF-8 BOM is still stripped

Full suite (SKIP_EXTERNAL_URLS=true): 452 passed, 0 failed. FHIR R4's fhir.shex now decodes and parses (896 shapes).

pyshexc's own generate_shexj.load_shex_file has the same chardet-first logic. PyShEx doesn't call it, but pyshexc's CLI would hit the same crash.

🤖 Generated with Claude Code

load_shex_file() trusted chardet whenever it was > 0.9 confident, but
chardet samples only the start of the data: for FHIR R4's fhir.shex
(690 KB, first non-ASCII byte ~250 KB in) it reports ascii with
confidence 1.0, and decode('ascii') raises UnicodeDecodeError. ShExC is
UTF-8 by definition, so try that first (utf-8-sig, which also strips a
BOM) and only guess for files that aren't valid UTF-8.

The old code also crashed on short non-UTF-8 files: chardet's low
confidence sent them to the UTF-8 default. Now chardet's guess is used
whatever its confidence, with latin-1 as a last resort that can't fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ericprud
ericprud merged commit 5ae5ed6 into master Oct 11, 2026
15 checks passed
@ericprud
ericprud deleted the shexc-utf8-first branch October 11, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant