From cf35636a1d88239efc65577b20e8df79bd089b09 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Tue, 13 May 2025 01:26:19 +0200 Subject: [PATCH 1/2] Support running stubtest in non-UTF8 terminals. --- mypy/stubtest.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index f9e6f7d337be..b10d65b7d857 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -2062,7 +2062,7 @@ def warning_callback(msg: str) -> None: if args.generate_allowlist: generated_allowlist.add(error.object_desc) continue - print(error.get_description(concise=args.concise)) + safe_print(error.get_description(concise=args.concise)) error_count += 1 # Print unused allowlist entries @@ -2102,6 +2102,18 @@ def warning_callback(msg: str) -> None: return exit_code +def safe_print(text: str) -> None: + """Print a text replacing chars not representable in stdout encoding.""" + # If `sys.stdout` encoding is not the same as out (usually UTF8) string, + # if may cause painful crashes. I don't want to reconfigure `sys.stdout` + # to do `errors = "replace"` as that sounds scary. + print( + text.encode(sys.stdout.encoding, errors="replace").decode( + sys.stdout.encoding, errors="replace" + ) + ) + + def parse_options(args: list[str]) -> _Arguments: parser = argparse.ArgumentParser( description="Compares stubs to objects introspected from the runtime." From 39e315a754a2925e30dca8b32bba149393cf1a89 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Tue, 13 May 2025 01:46:51 +0200 Subject: [PATCH 2/2] Make it work with replaced stdout --- mypy/stubtest.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index b10d65b7d857..8ea9d786be22 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -2107,11 +2107,12 @@ def safe_print(text: str) -> None: # If `sys.stdout` encoding is not the same as out (usually UTF8) string, # if may cause painful crashes. I don't want to reconfigure `sys.stdout` # to do `errors = "replace"` as that sounds scary. - print( - text.encode(sys.stdout.encoding, errors="replace").decode( - sys.stdout.encoding, errors="replace" - ) - ) + out_encoding = sys.stdout.encoding + if out_encoding is not None: + # Can be None if stdout is replaced (including our own tests). This should be + # safe to omit if the actual stream doesn't care about encoding. + text = text.encode(out_encoding, errors="replace").decode(out_encoding, errors="replace") + print(text) def parse_options(args: list[str]) -> _Arguments: