From 833c912b4155f5316e7f3354f57901422a87c0f7 Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Tue, 5 Jul 2022 09:44:49 -0400 Subject: [PATCH 1/6] Add support for tuples as positional arguments in rich repr Currently, it isn't possible to use tuples as positional arguments from `__rich_repr__`, because they are special cased to support adding a key and a default value. This adds support for them, by allowing `None` as a key. --- docs/source/pretty.rst | 2 ++ rich/pretty.py | 13 ++++++------- tests/test_pretty.py | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/docs/source/pretty.rst b/docs/source/pretty.rst index 7a050b943..29c0110a8 100644 --- a/docs/source/pretty.rst +++ b/docs/source/pretty.rst @@ -157,6 +157,8 @@ Each tuple specifies an element in the output. - ``yield name, value`` will generate a keyword argument. - ``yield name, value, default`` will generate a keyword argument *if* ``value`` is not equal to ``default``. +If you use ``None`` as the ``name``, then it will be treated as a positional argument as well, in order to support having ``tuple`` positional arguments. + You can also tell Rich to generate the *angular bracket* style of repr, which tend to be used where there is no easy way to recreate the object's constructor. To do this set the function attribute ``"angular"`` to ``True`` immediately after your ``__rich_repr__`` method. For example:: __rich_repr__.angular = True diff --git a/rich/pretty.py b/rich/pretty.py index 1c6b16716..de743e4c7 100644 --- a/rich/pretty.py +++ b/rich/pretty.py @@ -680,15 +680,14 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: for last, arg in loop_last(args): if _safe_isinstance(arg, tuple): key, child = arg - child_node = _traverse(child, depth=depth + 1) - child_node.last = last + else: + key, child = None, arg + child_node = _traverse(child, depth=depth + 1) + child_node.last = last + if key is not None: child_node.key_repr = key child_node.key_separator = "=" - append(child_node) - else: - child_node = _traverse(arg, depth=depth + 1) - child_node.last = last - append(child_node) + append(child_node) else: node = Node( value_repr=f"<{class_name}>" if angular else f"{class_name}()", diff --git a/tests/test_pretty.py b/tests/test_pretty.py index d45b0c22d..89cb79895 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -322,6 +322,7 @@ def __rich_repr__(self): ) + def test_max_depth_attrs(): @attr.define class Foo: @@ -544,3 +545,24 @@ def test_measure_pretty(): measurement = console.measure(pretty) assert measurement == Measurement(12, 12) + + +def test_tuple_rich_repr(): + """ + Test that can use None as key to have tuple positional values. + """ + class Foo: + def __rich_repr__(self): + yield None, (1,) + + assert pretty_repr(Foo()) == "Foo((1,))" + +def test_tuple_rich_repr_default(): + """ + Test that can use None as key to have tuple positional values and with a default. + """ + class Foo: + def __rich_repr__(self): + yield None, (1,), (1,) + + assert pretty_repr(Foo()) == "Foo()" From 25b8f622e8e36612b66be81d0c44260ff581e8a6 Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Tue, 5 Jul 2022 09:47:04 -0400 Subject: [PATCH 2/6] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78dd01c1a..44f334d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix edges used in first row of tables when `show_header=False` https://github.com/Textualize/rich/pull/2330 - Fix interaction between `Capture` contexts and `Console(record=True)` https://github.com/Textualize/rich/pull/2343 - Fixed hash issue in Styles class https://github.com/Textualize/rich/pull/2346 +- Fixes using tuples as positional arguments in `__rich_repr__` https://github.com/Textualize/rich/pull/2379 ### Changed From 0232b2b85028302bcd3843829de2b4d7ad1e497b Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Tue, 5 Jul 2022 09:51:02 -0400 Subject: [PATCH 3/6] Remove extra newline --- tests/test_pretty.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_pretty.py b/tests/test_pretty.py index 89cb79895..3e6c5a3c6 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -322,7 +322,6 @@ def __rich_repr__(self): ) - def test_max_depth_attrs(): @attr.define class Foo: From 21b51aa9b3c931ac023492a7103a3083ea281593 Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Tue, 5 Jul 2022 09:51:02 -0400 Subject: [PATCH 4/6] Remove extra newline --- tests/test_pretty.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_pretty.py b/tests/test_pretty.py index 3e6c5a3c6..32d3ffb59 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -550,16 +550,19 @@ def test_tuple_rich_repr(): """ Test that can use None as key to have tuple positional values. """ + class Foo: def __rich_repr__(self): yield None, (1,) assert pretty_repr(Foo()) == "Foo((1,))" + def test_tuple_rich_repr_default(): """ Test that can use None as key to have tuple positional values and with a default. """ + class Foo: def __rich_repr__(self): yield None, (1,), (1,) From 28e5b52ef7b014e0ac6e9e914557796097ed49d4 Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Mon, 11 Jul 2022 09:48:20 -0400 Subject: [PATCH 5/6] Switch changelog to reflect doc changes --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f334d66..be28bb072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix edges used in first row of tables when `show_header=False` https://github.com/Textualize/rich/pull/2330 - Fix interaction between `Capture` contexts and `Console(record=True)` https://github.com/Textualize/rich/pull/2343 - Fixed hash issue in Styles class https://github.com/Textualize/rich/pull/2346 -- Fixes using tuples as positional arguments in `__rich_repr__` https://github.com/Textualize/rich/pull/2379 +- Document using `None` as name in `__rich_repr__` for tuple posotional args https://github.com/Textualize/rich/pull/2379 ### Changed From 4ae2be62f62863034cbaa11dfc1b397499c971d8 Mon Sep 17 00:00:00 2001 From: Saul Shanabrook Date: Mon, 11 Jul 2022 09:49:27 -0400 Subject: [PATCH 6/6] Revert changes to pretty to account for None --- rich/pretty.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rich/pretty.py b/rich/pretty.py index de743e4c7..1c6b16716 100644 --- a/rich/pretty.py +++ b/rich/pretty.py @@ -680,14 +680,15 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: for last, arg in loop_last(args): if _safe_isinstance(arg, tuple): key, child = arg - else: - key, child = None, arg - child_node = _traverse(child, depth=depth + 1) - child_node.last = last - if key is not None: + child_node = _traverse(child, depth=depth + 1) + child_node.last = last child_node.key_repr = key child_node.key_separator = "=" - append(child_node) + append(child_node) + else: + child_node = _traverse(arg, depth=depth + 1) + child_node.last = last + append(child_node) else: node = Node( value_repr=f"<{class_name}>" if angular else f"{class_name}()",