From 417bf5839717a6e12f42779d81eb5e1e57601b0f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 2 Mar 2026 15:39:55 -0500 Subject: [PATCH 1/4] Added emoji field to console.ConsoleOptions. --- CHANGELOG.md | 5 +++++ CONTRIBUTORS.md | 1 + rich/console.py | 18 ++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdd4f15974..58228cc95e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed +- Added `emoji` field to `console.ConsoleOptions` https://github.com/Textualize/rich/issues/4028 + ## [14.3.3] - 2026-02-19 ### Fixed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4d77a0e3ed..b21afe35df 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -76,6 +76,7 @@ The following people have contributed to the development of Rich: - [Aaron Stephens](https://github.com/aaronst) - [Karolina Surma](https://github.com/befeleme) - [Gabriele N. Tornetta](https://github.com/p403n1x87) +- [Kevin Van Brunt](https://github.com/kmvanbrunt) - [Nils Vu](https://github.com/nilsvu) - [Arian Mollik Wasi](https://github.com/wasi-master) - [Jan van Wijk](https://github.com/jdvanwijk) diff --git a/rich/console.py b/rich/console.py index ad92d529c0..8c1ea235bb 100644 --- a/rich/console.py +++ b/rich/console.py @@ -143,6 +143,8 @@ class ConsoleOptions: """Highlight override for render_str.""" markup: Optional[bool] = None """Enable markup when rendering strings.""" + emoji: Optional[bool] = None + """Enable emoji code.""" height: Optional[int] = None @property @@ -171,6 +173,7 @@ def update( no_wrap: Union[Optional[bool], NoChange] = NO_CHANGE, highlight: Union[Optional[bool], NoChange] = NO_CHANGE, markup: Union[Optional[bool], NoChange] = NO_CHANGE, + emoji: Union[Optional[bool], NoChange] = NO_CHANGE, height: Union[Optional[int], NoChange] = NO_CHANGE, ) -> "ConsoleOptions": """Update values, return a copy.""" @@ -191,6 +194,8 @@ def update( options.highlight = highlight if not isinstance(markup, NoChange): options.markup = markup + if not isinstance(emoji, NoChange): + options.emoji = emoji if not isinstance(height, NoChange): if height is not None: options.max_height = height @@ -1325,7 +1330,10 @@ def render( render_iterable = renderable.__rich_console__(self, _options) elif isinstance(renderable, str): text_renderable = self.render_str( - renderable, highlight=_options.highlight, markup=_options.markup + renderable, + highlight=_options.highlight, + markup=_options.markup, + emoji=_options.emoji, ) render_iterable = text_renderable.__rich_console__(self, _options) else: @@ -1714,6 +1722,7 @@ def print( no_wrap=no_wrap, markup=markup, highlight=highlight, + emoji=emoji, ) new_segments: List[Segment] = [] @@ -2005,7 +2014,12 @@ def log( new_segments: List[Segment] = [] extend = new_segments.extend render = self.render - render_options = self.options + render_options = self.options.update( + justify=justify, + emoji=emoji, + markup=markup, + highlight=highlight, + ) for renderable in renderables: extend(render(renderable, render_options)) buffer_extend = self._buffer.extend From 4fd60f06e4f7e930cd00052d013ac4413e55337f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 2 Mar 2026 17:48:10 -0500 Subject: [PATCH 2/4] Populate render_str() with emoji, markup, and highlight options in Table, Rule, Columns, and Measure. --- rich/columns.py | 9 ++++++++- rich/measure.py | 2 +- rich/rule.py | 8 +++++++- rich/table.py | 8 +++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/rich/columns.py b/rich/columns.py index 669a3a7074..04d8253233 100644 --- a/rich/columns.py +++ b/rich/columns.py @@ -64,7 +64,14 @@ def __rich_console__( ) -> RenderResult: render_str = console.render_str renderables = [ - render_str(renderable) if isinstance(renderable, str) else renderable + render_str( + renderable, + markup=options.markup, + emoji=options.emoji, + highlight=False, + ) + if isinstance(renderable, str) + else renderable for renderable in self.renderables ] if not renderables: diff --git a/rich/measure.py b/rich/measure.py index a508ffa80b..99cf5ebf9b 100644 --- a/rich/measure.py +++ b/rich/measure.py @@ -97,7 +97,7 @@ def get( return Measurement(0, 0) if isinstance(renderable, str): renderable = console.render_str( - renderable, markup=options.markup, highlight=False + renderable, markup=options.markup, emoji=options.emoji, highlight=False ) renderable = rich_cast(renderable) if is_renderable(renderable): diff --git a/rich/rule.py b/rich/rule.py index fb3d43271d..9896015d65 100644 --- a/rich/rule.py +++ b/rich/rule.py @@ -65,7 +65,13 @@ def __rich_console__( if isinstance(self.title, Text): title_text = self.title else: - title_text = console.render_str(self.title, style="rule.text") + title_text = console.render_str( + self.title, + style="rule.text", + markup=options.markup, + emoji=options.emoji, + highlight=False, + ) title_text.plain = title_text.plain.replace("\n", " ") title_text.expand_tabs() diff --git a/rich/table.py b/rich/table.py index 1e2eb2b45a..9b805769ba 100644 --- a/rich/table.py +++ b/rich/table.py @@ -498,7 +498,13 @@ def render_annotation( text: TextType, style: StyleType, justify: "JustifyMethod" = "center" ) -> "RenderResult": render_text = ( - console.render_str(text, style=style, highlight=False) + console.render_str( + text, + style=style, + highlight=False, + markup=render_options.markup, + emoji=render_options.emoji, + ) if isinstance(text, str) else text ) From fd3118ef86f6ef3c2599a3d6082603a1e32af357 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 18 Apr 2026 22:15:15 -0400 Subject: [PATCH 3/4] Added tests. --- tests/test_columns.py | 26 ++++++++++++++++++++++++++ tests/test_console.py | 18 ++++++++++++++++++ tests/test_measure.py | 10 ++++++++++ tests/test_rule.py | 26 ++++++++++++++++++++++++++ tests/test_table.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+) diff --git a/tests/test_columns.py b/tests/test_columns.py index d4c1113d34..385a262f4e 100644 --- a/tests/test_columns.py +++ b/tests/test_columns.py @@ -75,3 +75,29 @@ def test_render(): result = render() print(result) print(repr(result)) + + +def test_columns_emoji_propagation(): + console = Console() + columns = Columns([":1234:"]) + + with console.capture() as capture: + console.print(columns, emoji=True) + assert "🔢" in capture.get() + + with console.capture() as capture: + console.print(columns, emoji=False) + assert ":1234:" in capture.get() + + +def test_columns_markup_propagation(): + console = Console(force_terminal=True, _environ={}) + columns = Columns(["[blue]text[/blue]"]) + + with console.capture() as capture: + console.print(columns, markup=True) + assert "\x1b[34mtext\x1b[0m" in capture.get() + + with console.capture() as capture: + console.print(columns, markup=False) + assert "[blue]text[/blue]" in capture.get() diff --git a/tests/test_console.py b/tests/test_console.py index e52c1b3633..e1092bd02a 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -841,6 +841,24 @@ def test_update_options_markup() -> None: assert options.update(markup=True).markup == True +def test_update_options_emoji() -> None: + console = Console() + options = console.options + assert options.update(emoji=False).emoji == False + assert options.update(emoji=True).emoji == True + + +def test_print_emoji() -> None: + console = Console() + with console.capture() as capture: + console.print(":1234:", emoji=True) + assert "🔢" in capture.get() + + with console.capture() as capture: + console.print(":1234:", emoji=False) + assert ":1234:" in capture.get() + + def test_print_width_zero() -> None: console = Console() with console.capture() as capture: diff --git a/tests/test_measure.py b/tests/test_measure.py index 6155922da1..05d99e82db 100644 --- a/tests/test_measure.py +++ b/tests/test_measure.py @@ -34,3 +34,13 @@ def test_clamp(): assert measurement.clamp(None, 50) == Measurement(20, 50) assert measurement.clamp(30, None) == Measurement(30, 100) assert measurement.clamp(None, None) == Measurement(20, 100) + + +def test_measurement_emoji_propagation(): + console = Console() + options = console.options + + m_emoji = Measurement.get(console, options.update(emoji=True), ":1234:") + m_no_emoji = Measurement.get(console, options.update(emoji=False), ":1234:") + + assert m_emoji.maximum < m_no_emoji.maximum diff --git a/tests/test_rule.py b/tests/test_rule.py index 398c75585b..f698036f5e 100644 --- a/tests/test_rule.py +++ b/tests/test_rule.py @@ -128,3 +128,29 @@ def test_repr(): def test_error(): with pytest.raises(ValueError): Rule(characters="") + + +def test_rule_emoji_propagation(): + console = Console() + rule = Rule(title=":1234:") + + with console.capture() as capture: + console.print(rule, emoji=True) + assert "🔢" in capture.get() + + with console.capture() as capture: + console.print(rule, emoji=False) + assert ":1234:" in capture.get() + + +def test_rule_markup_propagation(): + console = Console(force_terminal=True, _environ={}) + rule = Rule(title="[blue]text[/blue]") + + with console.capture() as capture: + console.print(rule, markup=True) + assert "\x1b[34mtext\x1b[0m" in capture.get() + + with console.capture() as capture: + console.print(rule, markup=False) + assert "[blue]text[/blue]" in capture.get() diff --git a/tests/test_table.py b/tests/test_table.py index 77a7533835..98655ee45e 100644 --- a/tests/test_table.py +++ b/tests/test_table.py @@ -410,3 +410,31 @@ def test_padding_width(): render = render_tables() print(render) print(repr(render)) + + +def test_table_emoji_propagation(): + console = Console() + table = Table() + table.add_row(":1234:") + + with console.capture() as capture: + console.print(table, emoji=True) + assert "🔢" in capture.get() + + with console.capture() as capture: + console.print(table, emoji=False) + assert ":1234:" in capture.get() + + +def test_table_markup_propagation(): + console = Console(force_terminal=True, _environ={}) + table = Table() + table.add_row("[blue]text[/blue]") + + with console.capture() as capture: + console.print(table, markup=True) + assert "\x1b[34mtext\x1b[0m" in capture.get() + + with console.capture() as capture: + console.print(table, markup=False) + assert "[blue]text[/blue]" in capture.get() From a0aa10f67544d84d4d2b334367986f3d67a1c8ad Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 18 Apr 2026 22:25:26 -0400 Subject: [PATCH 4/4] Updated CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 761f82fc68..236ac5ed02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed -- Added `emoji` field to `console.ConsoleOptions` https://github.com/Textualize/rich/issues/4028 +- Fixed `ConsoleOptions` propagation https://github.com/Textualize/rich/issues/4028 ## [15.0.0] - 2026-04-12