diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b4f70f3..ef9e50ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,16 @@ 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] +## [10.10.0] - 2021-09-18 ### Added - Added stdin support to `rich.json` +### Fixed + +- Fixed pretty printing of objects with fo magic with **getattr** https://github.com/willmcgugan/rich/issues/1492 + ## [10.9.0] - 2021-08-29 ### Added diff --git a/pyproject.toml b/pyproject.toml index aa4b9ea6..7f9329aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "10.9.0" +version = "10.10.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" diff --git a/rich/pretty.py b/rich/pretty.py index 6d029541..4997d5b8 100644 --- a/rich/pretty.py +++ b/rich/pretty.py @@ -1,5 +1,6 @@ import builtins import os +from rich.repr import RichReprResult import sys from array import array from collections import Counter, defaultdict, deque, UserDict, UserList @@ -503,9 +504,24 @@ def traverse( else: yield arg - if hasattr(obj, "__rich_repr__") and not isclass(obj): + try: + fake_attributes = hasattr( + obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492" + ) + except Exception: + fake_attributes = False + + rich_repr_result: Optional[RichReprResult] = None + if not fake_attributes: + try: + if hasattr(obj, "__rich_repr__") and not isclass(obj): + rich_repr_result = obj.__rich_repr__() + except Exception: + pass + + if rich_repr_result is not None: angular = getattr(obj.__rich_repr__, "angular", False) - args = list(iter_rich_args(obj.__rich_repr__())) + args = list(iter_rich_args(rich_repr_result)) class_name = obj.__class__.__name__ if args: @@ -544,7 +560,7 @@ def traverse( children=[], last=root, ) - elif _is_attr_object(obj): + elif _is_attr_object(obj) and not fake_attributes: children = [] append = children.append @@ -592,6 +608,7 @@ def traverse( elif ( is_dataclass(obj) and not isinstance(obj, type) + and not fake_attributes and ( "__create_fn__" in obj.__repr__.__qualname__ or py_version == (3, 6) ) # Check if __repr__ wasn't overridden diff --git a/tests/test_pretty.py b/tests/test_pretty.py index cf838db2..938f216b 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -250,3 +250,15 @@ def test_user_dict(): result = pretty_repr(d2, expand_all=True) print(repr(result)) assert result == "FOO" + + +def test_lying_attribute(): + """Test getattr doesn't break rich repr protocol""" + + class Foo: + def __getattr__(self, attr): + return "foo" + + foo = Foo() + result = pretty_repr(foo) + assert "Foo" in result