Skip to content

fix(cli): force UTF-8 stdout/stderr at startup, no PYTHONUTF8 needed (closes #179)#183

Merged
Wolfvin merged 1 commit into
mainfrom
fix/issue-179-windows-unicode-encode
Jul 3, 2026
Merged

fix(cli): force UTF-8 stdout/stderr at startup, no PYTHONUTF8 needed (closes #179)#183
Wolfvin merged 1 commit into
mainfrom
fix/issue-179-windows-unicode-encode

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Closes #179

Sebelum / Sesudah

Sebelum: Windows users running codelens trace (or any command emitting Unicode chars like ) without PYTHONUTF8=1 crashed with:

UnicodeEncodeError: 'charmap' codec can't encode character '→' in position 440: character maps to <undefined>

Root cause: Windows default stdout encoding (cp1252 or similar) cannot represent Unicode chars used in trace path strings like sidepanel.ts → auth/google-auth-cache.ts.

Sesudah: scripts/codelens.py now wraps sys.stdout / sys.stderr as UTF-8 TextIOWrapper at import time, before any user-code imports. Windows users no longer need PYTHONUTF8=1 to get working output.

Changes

scripts/codelens.py (+64 lines)

Added _force_utf8_stdio() at the top of the file (after the module docstring, before any other imports). Behaviour:

  • Linux/macOS: no-op (sys.stdout.encoding is already utf-8).
  • pytest capsys: no-op (capture's encoding is also utf-8).
  • Windows without PYTHONUTF8=1: wraps both stdout and stderr as io.TextIOWrapper(buffer, encoding='utf-8', errors='replace'). Unencodable bytes become ? instead of crashing.
  • Streams without .buffer (IDLE REPL, custom capture objects): skipped gracefully.
  • Old stream's buffer is detach()-ed before the new wrapper is built, so garbage-collection of the old TextIOWrapper does not close the underlying buffer (which would otherwise make the new wrapper unusable with ValueError: I/O operation on closed file — caught by the regression test).

The fix runs unconditionally at module import time, before any user-code imports that might emit Unicode output (warnings, deprecation notices, etc.).

tests/test_codelens.py (+195 lines)

Added TestForceUtf8Stdio class with 7 regression tests:

  1. test_function_is_callable — sanity check that _force_utf8_stdio is exposed.
  2. test_wraps_non_utf8_stdout — cp1252 stream is rewrapped as UTF-8; arrow writes succeed; underlying buffer receives UTF-8 bytes.
  3. test_noop_when_stream_already_utf8 — UTF-8 stream is left untouched (same object).
  4. test_noop_for_stream_without_buffer — bufferless streams (IDLE REPL, custom capture) are skipped, not crashed.
  5. test_handles_stream_with_none_encoding — streams with encoding=None are wrapped.
  6. test_writes_unicode_arrow_to_replaced_stream — end-to-end test for the exact sidepanel.ts → auth/google-auth-cache.ts trace pattern from the issue.
  7. test_both_stdout_and_stderr_wrapped — verifies stderr is also handled (error messages like [CodeLens] Warning: ... would also crash on Windows if they contained Unicode).

Tests

tests/test_codelens.py::TestForceUtf8Stdio: 7 passed
tests/test_codelens.py (full file): all pass

Pre-existing failures in test_adr.py, test_codelensignore.py, test_confidence.py, test_formatters_phase2.py, test_universal_grammar_loader.py verified to also fail on origin/main (run with git stash && git checkout main && pytest ... && git checkout - && git stash pop). They are environment-specific (missing tree_sitter module, stale fixtures, schema_version field not yet implemented) and not regressions from this PR.

Definition of Done check

  • All commands produce correct output on Windows without PYTHONUTF8=1sys.stdout is forced to UTF-8 at import time.
  • and other Unicode chars in trace paths render correctly or are replaced with ASCII fallback (? via errors='replace') without crashing.
  • No change in behavior on Linux/macOS — the fix is a no-op when sys.stdout.encoding is already utf-8.

Findings

  • tests/test_cli.py::TestArgparseFormatConflictRegression::test_scan_with_format_long_does_not_crash has a hardcoded timeout=60 in its subprocess.run call. On this Linux sandbox it takes ~55-59s to scan the CodeLens repo itself, which is borderline. On my branch it ran in 55-56s on retry (faster than main's 58.98s), so no real regression — but the test will be flaky on any slow CI runner. Out of scope for this issue; flagging for awareness.

…loses #179)

Windows without PYTHONUTF8=1 crashes with UnicodeEncodeError when commands
emit Unicode chars like '\u2192' in trace paths (e.g.
'sidepanel.ts \u2192 auth/google-auth-cache.ts'). Default Windows stdout
encoding (cp1252) cannot represent those chars.

Fix: scripts/codelens.py wraps sys.stdout/sys.stderr as UTF-8 TextIOWrapper
at import time, before any user-code imports. Behaviour:
- Linux/macOS: no-op (encoding already utf-8)
- pytest capsys: no-op (encoding is utf-8)
- Windows without PYTHONUTF8=1: wrap with errors='replace' so unencodable
  bytes become '?' instead of crashing
- Streams without .buffer (IDLE REPL, custom capture): skip gracefully
- Old stream's buffer is detach()-ed before new wrapper is built, so GC
  of old wrapper does not close the underlying buffer

Tests: tests/test_codelens.py::TestForceUtf8Stdio -- 7 regression tests
covering all 4 scenarios + end-to-end test for the exact trace pattern
from the issue.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sonarqubecloud

sonarqubecloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

@Wolfvin Wolfvin merged commit 5e1a89a into main Jul 3, 2026
1 of 7 checks passed
@Wolfvin Wolfvin deleted the fix/issue-179-windows-unicode-encode branch July 3, 2026 09:29
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.

fix(cli): UnicodeEncodeError on Windows without PYTHONUTF8=1

1 participant