Skip to content

Commit 2a58895

Browse files
authored
Fix/dont substitute emoji in log text (#312)
* fix(console): don't substitute emoji glyphs in log text Rich replaced shortcodes like :Package: with an emoji, so a concept named :Package: showed up as a glyph in error messages instead of its name. The log methods now print with emoji substitution off. * test(console): move the bracket test into TestTerminalTextRendersVerbatim The test checks that text prints unchanged, not that colors are applied, so it sat in the wrong class. The test body is unchanged. * test(console): give each log-text setting its own test One test checked markup and highlighting at once, so a markup failure hid the highlighting assertion. The highlighting assertion could also never fail under pytest, because Rich emits no color codes unless it believes it is writing to a colored terminal, so that test now forces one. * fix(console): turn emoji substitution off globally print_list and print_files print directly instead of going through the log methods, so they still replaced shortcodes with glyphs. Passing emoji=False to print does not reach file names inside the tree, so the console itself is now built with emoji off. * docs(console): move the emoji comment
1 parent 37ce1c7 commit 2a58895

2 files changed

Lines changed: 66 additions & 9 deletions

File tree

‎plain2code_console.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Plain2CodeConsole(Console):
2727
DEBUG_STYLE = Style(color="purple")
2828

2929
def __init__(self):
30-
super().__init__()
30+
super().__init__(emoji=False)
3131
try:
3232
import tiktoken
3333

@@ -66,9 +66,10 @@ def _log_and_print(self, level, base_style, args, color, kwargs):
6666
"""
6767
logger.log(level, " ".join(map(str, args)), extra={"log_color": color})
6868
style = base_style + Style(color=color) if color else base_style
69-
# Log messages must render exactly as logged: don't interpret square brackets
70-
# in interpolated content (error texts, file names) as Rich markup, and don't
71-
# let the repr highlighter restyle brackets and numbers inside them.
69+
# Messages must render exactly as logged and not pick its own styling:
70+
# -- no Rich markup (via square brackets) in interpolated content (error texts, file names)
71+
# -- no repr highlighter restyling brackets and numbers inside them.
72+
# -- no emoji substitution, but it's set False in __init__, otherwise it won't reach renderables like Trees
7273
kwargs.setdefault("markup", False)
7374
kwargs.setdefault("highlight", False)
7475
super().print(*args, **kwargs, style=style)

‎tests/test_console_log_styles.py‎

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,70 @@ def test_no_color_defaults_to_none(self):
7070

7171
assert handler.records[0].log_color is None
7272

73-
def test_bracketed_error_text_prints_verbatim(self):
74-
console, handler = make_console_with_capture()
75-
console.quiet = False
73+
74+
class TestTerminalTextRendersVerbatim:
75+
"""
76+
Message text must reach the terminal unchanged, and must not influence its own
77+
styling. Colors are still applied but they are chosen by the call, never parsed
78+
out of the text:
79+
- Rich markup tags must not be interpreted
80+
- the repr highlighter must not restyle brackets and numbers
81+
- emoji shortcodes must not be substituted by glyphs
82+
"""
83+
84+
def test_markup_closing_tag_prints_verbatim(self):
85+
console = Plain2CodeConsole()
7686
with console.capture() as capture:
77-
console.info("Error: [Errno 8] failed [/closing] tag", color=RETRY_COLOR)
78-
assert "[Errno 8]" in capture.get()
87+
console.error("Error: [/closing] tag")
88+
# markup=True raises MarkupError on an unmatched closing tag
7989
assert "[/closing]" in capture.get()
8090

91+
def test_markup_style_tag_prints_verbatim(self):
92+
console = Plain2CodeConsole()
93+
with console.capture() as capture:
94+
console.error("Error: [bold]important text")
95+
# markup=True silently consumes the tag and styles the rest
96+
assert "[bold]" in capture.get()
97+
98+
def test_bracketed_number_prints_verbatim(self, monkeypatch):
99+
# Rich emits no ANSI unless it believes it is writing to a colored terminal
100+
# (FORCE_COLOR alone is not enough: TERM=dumb still resolves to no colors)
101+
monkeypatch.setenv("FORCE_COLOR", "1")
102+
monkeypatch.setenv("TERM", "xterm-256color")
103+
console = Plain2CodeConsole()
104+
assert console.color_system is not None # should fail test immediately if no colors
105+
106+
with console.capture() as capture:
107+
console.error("Error: [Errno 8] failed")
108+
# highlight=True breaks this up with colour codes
109+
assert "[Errno 8]" in capture.get()
110+
111+
def test_concept_name_matching_emoji_prints_verbatim(self):
112+
console = Plain2CodeConsole()
113+
with console.capture() as capture:
114+
console.error("Syntax error: Invalid concept: :Package:")
115+
assert ":Package:" in capture.get()
116+
assert "📦" not in capture.get()
117+
118+
119+
class TestPrintHelpersDoNotSubstituteEmoji:
120+
"""
121+
Same rule as TestTerminalTextRendersVerbatim, for the two helpers that bypass _log_and_print.
122+
Only emoji is covered here, markup and highlighting still apply to them.
123+
"""
124+
125+
def test_print_list_does_not_substitute_emoji(self):
126+
console = Plain2CodeConsole()
127+
with console.capture() as capture:
128+
console.print_list(["The :Package: is a concept"])
129+
assert ":Package:" in capture.get()
130+
131+
def test_print_files_does_not_substitute_emoji_in_tree_labels(self):
132+
console = Plain2CodeConsole()
133+
with console.capture() as capture:
134+
console.print_files("Files added:", "build", {":Package:.py": "x"})
135+
assert ":Package:" in capture.get()
136+
81137

82138
class TestLoggingHandlerForwardsColor:
83139
"""LoggingHandler must forward the log_color record attribute into LogMessageEmitted."""

0 commit comments

Comments
 (0)