Skip to content

Commit f97a56e

Browse files
authored
Support running stubtest in non-UTF8 terminals (#19085)
Fixes #19071. I looked through the `open()` calls in the codebase, and only `reports.py` raises some concerns. Stubtest crashes due to this `print` call with incompatible encoding. I tested this on Linux with `LC_CTYPE=ru_RU.CP1251` (random non-utf8 locale I found in `/usr/share/i18n/SUPPORTED`) and confirmed that `stubtest` crashes without the patch and passes with it. Using a simple MRE (empty stub file and `A = "╙"` in a file, this symbol is `$'\u2559'`), I got this: ``` error: package.A is not present in stub Stub: in file /tmp/tmp.Cs4RioNSuR/demo/stub/package/__init__.pyi MISSING Runtime: '?' Found 1 error (checked 1 module) ``` Without the patch I get a crash - same as in the linked issue.
1 parent b18d3f8 commit f97a56e

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

mypy/stubtest.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2062,7 +2062,7 @@ def warning_callback(msg: str) -> None:
20622062
if args.generate_allowlist:
20632063
generated_allowlist.add(error.object_desc)
20642064
continue
2065-
print(error.get_description(concise=args.concise))
2065+
safe_print(error.get_description(concise=args.concise))
20662066
error_count += 1
20672067

20682068
# Print unused allowlist entries
@@ -2102,6 +2102,19 @@ def warning_callback(msg: str) -> None:
21022102
return exit_code
21032103

21042104

2105+
def safe_print(text: str) -> None:
2106+
"""Print a text replacing chars not representable in stdout encoding."""
2107+
# If `sys.stdout` encoding is not the same as out (usually UTF8) string,
2108+
# if may cause painful crashes. I don't want to reconfigure `sys.stdout`
2109+
# to do `errors = "replace"` as that sounds scary.
2110+
out_encoding = sys.stdout.encoding
2111+
if out_encoding is not None:
2112+
# Can be None if stdout is replaced (including our own tests). This should be
2113+
# safe to omit if the actual stream doesn't care about encoding.
2114+
text = text.encode(out_encoding, errors="replace").decode(out_encoding, errors="replace")
2115+
print(text)
2116+
2117+
21052118
def parse_options(args: list[str]) -> _Arguments:
21062119
parser = argparse.ArgumentParser(
21072120
description="Compares stubs to objects introspected from the runtime."

0 commit comments

Comments
 (0)