Repository navigation
fix: run the engine in the "C" locale so Windows keeps UTF-8 whole - #40
Merged
Merged
Conversation
On Windows, Python sets LC_CTYPE to the user's code page (e.g. 1252) at startup. The engine tests UTF-8 a byte at a time with <ctype.h>, and in cp1252 byte 0xA0 is a no-break space, so the continuation byte 0xA0 of "à" (C3 A0) or "전" (EC A0 84) read as whitespace: clean_str() (used by phrasetext() and friends) cut words mid-character and wrote invalid UTF-8. nlp.exe (C locale) and Linux (UTF-8 locale) were unaffected. Wrap the constructor, analyze() and compile() in a per-thread guard that switches to the "C" locale and restores the caller's on return (_configthreadlocale on Windows, uselocale elsewhere). Add tests/test_locale.py, which forces a single-byte locale, analyzes "Voilà" and "오전" with parse-en-us and checks the output is valid UTF-8 with both words whole and the caller's locale restored. Run it against every repaired Windows wheel in publish.yml. Co-Authored-By: Claude Opus 5.5 <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.
Problem
On Windows, NLPPlus (2.2.39 through 2.2.43) splits words at byte
0xA0when that byte is part of a multi-byte UTF-8 character. "Voilà" (… C3 A0) comes out asVoil\xC3, and "오전" (EC 98 A4 EC A0 84) gets a space in place ofA0. The output text and output files are then invalid UTF-8, andanalyze()raisesUnicodeDecodeError. The Linux wheel andnlp.exeare not affected.Cause
The tokenizer is not at fault:
dicttokzproduces correct tokens, and every per-pass tree is valid UTF-8. The damage is done byclean_str()(lite/str.cpp), whichphrasetext()and similar functions use. It tests each byte with_istspace→isspace, whose answer depends on the locale.nlp.exeruns in the "C" locale.LC_CTYPEfrom the user's environment at startup. On Windows that is a code page such as 1252, where0xA0is a no-break space, soisspace(0xA0)is true.isspace(0xA0)is false.Forcing
LC_CTYPE=Cin the same Python process with the 2.2.39 wheel makes every output file valid.Fix
bindings.cppnow wraps the constructor,analyze()andcompile()in a small RAII guard. It switches the calling thread to the "C" locale, the same asnlp.exe, and restores the caller's locale on return. Windows uses_configthreadlocale; other platforms useuselocale. Other Python threads are not affected.The guard also covers the other locale-dependent
<ctype.h>calls in the engine (isalpha,toupper, …), which could fail the same way.Test
tests/test_locale.pyforces a single-byte locale and analyzes "Voilà, the form. 오전 9시." withparse-en-us. It checks that:clause.txthas both phrases whole;It skips where no single-byte locale exists, which includes Linux CI.
publish.ymlnow runs it against every repaired Windows wheel throughCIBW_TEST_COMMAND_WINDOWS.Results:
-Wall -Wextra). No Linux or macOS wheel was built locally; CI covers that.Notes
An engine-side fix in
my_tchar.h(ASCII-only character tests) would also protect other programs that embed the engine. That belongs in a separate PR to nlp-engine.🤖 Generated with Claude Code