Skip to content

Commit d91336f

Browse files
committed
cli: force UTF-8 stdio on startup + use platform-native fake path in run-update test
Fixes two Windows-only CLI regressions: - UnicodeEncodeError: 'charmap' codec can't encode character '\U0001f30d' — Windows console stdout defaults to the ANSI code page (cp1252) which can't encode the emoji / box-drawing characters abxpkg prints (🌍, 📦, —, …). Added _force_utf8_stdio which reconfigure()s sys.stdout / sys.stderr to UTF-8 (with errors='replace' as belt-and-suspenders), wired into both main() and abx_main() entrypoints. Unix stdio is already UTF-8 so this is a no-op there. Fixes test_abxpkg_version_runs_without_error and test_version_report_includes_provider_local_cached_binary_list. - test_run_update_skips_env_for_the_update_step: the hardcoded Path("/tmp/fake-bin") literal stringifies differently on Windows (\tmp\fake-bin) vs POSIX. Use tmp_path / 'fake-bin' on both sides of the assertion so the comparison holds on every platform.
1 parent e530aaa commit d91336f

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

abxpkg/cli.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,26 @@ def _console_for_stream(*, err: bool):
549549
)
550550

551551

552+
def _force_utf8_stdio() -> None:
553+
"""Reconfigure ``sys.stdout`` / ``sys.stderr`` to UTF-8 at CLI startup.
554+
555+
Windows console streams default to the ANSI code page (``cp1252``
556+
on US-English hosts), which can't encode the emoji / box-drawing
557+
characters abxpkg prints (``🌍``, ``📦``, ``—`` …). Without this,
558+
the first such character raises ``UnicodeEncodeError`` and the CLI
559+
crashes mid-output. Unix stdio is already UTF-8 by default so this
560+
is effectively a no-op there; the ``errors='replace'`` fallback is
561+
belt-and-suspenders in case the platform refuses UTF-8 outright.
562+
"""
563+
for stream in (sys.stdout, sys.stderr):
564+
reconfigure = getattr(stream, "reconfigure", None)
565+
if reconfigure is not None:
566+
try:
567+
reconfigure(encoding="utf-8", errors="replace")
568+
except (OSError, ValueError):
569+
pass
570+
571+
552572
def _echo(message: Any, *, err: bool = False) -> None:
553573
console = _console_for_stream(err=err)
554574
if console is not None:
@@ -1849,6 +1869,7 @@ def _expand_bare_bool_flags(argv: list[str]) -> list[str]:
18491869

18501870

18511871
def main() -> None:
1872+
_force_utf8_stdio()
18521873
cli(_expand_bare_bool_flags(sys.argv[1:]))
18531874

18541875

@@ -1939,6 +1960,7 @@ def abx_main() -> None:
19391960
surface area — every option is still documented and parsed exactly
19401961
once, by ``abxpkg`` itself.
19411962
"""
1963+
_force_utf8_stdio()
19421964
argv = list(sys.argv[1:])
19431965
pre, rest = _split_abx_argv(argv)
19441966
# Expand bare bool flags only in the pre-binary-name slice; rest is

tests/test_cli.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,11 @@ def exec(self, bin_name, cmd=(), capture_output=False):
542542
"",
543543
)
544544

545+
fake_bin_path = tmp_path / "fake-bin"
546+
545547
class FakeRunBinary:
546548
def __init__(self):
547-
self.loaded_abspath = Path("/tmp/fake-bin")
549+
self.loaded_abspath = fake_bin_path
548550
self.loaded_version = SemVer("1.2.3")
549551
self.loaded_binprovider = FakeLoadedProvider("env")
550552
self.binproviders = []
@@ -585,7 +587,7 @@ def update(self, binproviders=None, dry_run=None, no_cache=None):
585587
assert calls == [
586588
("load", (False,)),
587589
("update", (("brew",), False, False)),
588-
("exec", ("brew", "/tmp/fake-bin", ("--version",), False)),
590+
("exec", ("brew", str(fake_bin_path), ("--version",), False)),
589591
]
590592

591593

0 commit comments

Comments
 (0)