Skip to content

Commit d17e771

Browse files
committed
Fix Windows cp1252 encoding error by detecting console encoding (#55)
Add encoding detection to conditionally display emoji based on console capabilities. This fixes the UnicodeEncodeError on Windows while preserving the emoji for UTF-8 capable consoles. Changes: - Add supports_unicode() function to detect if console can handle emojis - Conditionally prefix message with 🎂 emoji only when supported - Add comprehensive tests for different encoding scenarios The fix works by: 1. Checking sys.stdout.encoding 2. Testing if the encoding can encode the emoji 3. Using emoji only if encoding succeeds 4. Falling back to plain text for cp1252, ASCII, latin-1, etc. This ensures: - UTF-8 consoles (Linux/macOS) see the emoji - cp1252 consoles (Windows) see plain text without errors - No logging errors appear in CI output Related to #55
1 parent 26def49 commit d17e771

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

src/mxdev/main.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import argparse
1313
import logging
14+
import sys
1415

1516

1617
parser = argparse.ArgumentParser(
@@ -44,6 +45,23 @@
4445
parser.add_argument("-v", "--verbose", help="Increase verbosity", action="store_true")
4546

4647

48+
def supports_unicode() -> bool:
49+
"""Check if stdout supports Unicode/emoji encoding.
50+
51+
Returns True if the console encoding can handle Unicode emojis,
52+
False otherwise (e.g., cp1252 on Windows).
53+
"""
54+
try:
55+
encoding = sys.stdout.encoding
56+
if not encoding:
57+
return False
58+
# Test if the encoding can handle the cake emoji
59+
"🎂".encode(encoding)
60+
return True
61+
except (AttributeError, UnicodeEncodeError, LookupError):
62+
return False
63+
64+
4765
def main() -> None:
4866
args = parser.parse_args()
4967
loglevel = logging.INFO
@@ -78,5 +96,7 @@ def main() -> None:
7896
write(state)
7997
write_hooks(state, hooks)
8098
out_requirements = state.configuration.out_requirements
81-
logger.info(f"🎂 You are now ready for: pip install -r {out_requirements}")
99+
# Use emoji only if console encoding supports it (avoid cp1252 errors on Windows)
100+
prefix = "🎂 " if supports_unicode() else ""
101+
logger.info(f"{prefix}You are now ready for: pip install -r {out_requirements}")
82102
logger.info(" (path to pip may vary dependent on your installation method)")

tests/test_main.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import io
12
import pathlib
23
import pytest
4+
import sys
35
from unittest.mock import patch, MagicMock
46
import logging
57

@@ -93,3 +95,63 @@ def test_parser_verbose():
9395

9496
args = parser.parse_args(["-v"])
9597
assert args.verbose is True
98+
99+
100+
def test_supports_unicode_with_utf8():
101+
"""Test supports_unicode returns True for UTF-8 encoding."""
102+
from mxdev.main import supports_unicode
103+
104+
# Mock stdout with UTF-8 encoding
105+
mock_stdout = MagicMock()
106+
mock_stdout.encoding = "utf-8"
107+
108+
with patch("sys.stdout", mock_stdout):
109+
assert supports_unicode() is True
110+
111+
112+
def test_supports_unicode_with_cp1252():
113+
"""Test supports_unicode returns False for cp1252 encoding."""
114+
from mxdev.main import supports_unicode
115+
116+
# Mock stdout with cp1252 encoding
117+
mock_stdout = MagicMock()
118+
mock_stdout.encoding = "cp1252"
119+
120+
with patch("sys.stdout", mock_stdout):
121+
assert supports_unicode() is False
122+
123+
124+
def test_supports_unicode_with_no_encoding():
125+
"""Test supports_unicode returns False when encoding is None."""
126+
from mxdev.main import supports_unicode
127+
128+
# Mock stdout with no encoding
129+
mock_stdout = MagicMock()
130+
mock_stdout.encoding = None
131+
132+
with patch("sys.stdout", mock_stdout):
133+
assert supports_unicode() is False
134+
135+
136+
def test_supports_unicode_with_ascii():
137+
"""Test supports_unicode returns False for ASCII encoding."""
138+
from mxdev.main import supports_unicode
139+
140+
# Mock stdout with ASCII encoding
141+
mock_stdout = MagicMock()
142+
mock_stdout.encoding = "ascii"
143+
144+
with patch("sys.stdout", mock_stdout):
145+
assert supports_unicode() is False
146+
147+
148+
def test_supports_unicode_with_latin1():
149+
"""Test supports_unicode returns False for latin-1 encoding."""
150+
from mxdev.main import supports_unicode
151+
152+
# Mock stdout with latin-1 encoding
153+
mock_stdout = MagicMock()
154+
mock_stdout.encoding = "latin-1"
155+
156+
with patch("sys.stdout", mock_stdout):
157+
assert supports_unicode() is False

0 commit comments

Comments
 (0)