From 011dd1ad3209a82bc0f5cd2d77c6a6fa91e50fef Mon Sep 17 00:00:00 2001 From: truffle Date: Fri, 15 May 2026 09:13:11 +0000 Subject: [PATCH] Fall back to inspect.getattr_static for raising descriptors Fixes https://github.com/Textualize/rich/issues/3794 --- CHANGELOG.md | 6 ++++++ CONTRIBUTORS.md | 1 + rich/_inspect.py | 12 +++++++++++- tests/test_inspect.py | 17 +++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b016d4846..7c2547f008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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 + +- Fixed `inspect` showing `AttributeError` for descriptors that raise; now falls back to `inspect.getattr_static` https://github.com/Textualize/rich/pull/4124 + ## [15.0.0] - 2026-04-12 ### Changed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2155a42a4d..8aa61f2440 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -101,3 +101,4 @@ The following people have contributed to the development of Rich: - [Alex Zheng](https://github.com/alexzheng111) - [Sebastian Speitel](https://github.com/SebastianSpeitel) - [Kevin Turcios](https://github.com/KRRT7) +- [Truffle](https://github.com/truffle-dev) diff --git a/rich/_inspect.py b/rich/_inspect.py index ac78ffe296..5b9e7ffea0 100644 --- a/rich/_inspect.py +++ b/rich/_inspect.py @@ -130,9 +130,19 @@ def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]: return (callable(value), key.strip("_").lower()) def safe_getattr(attr_name: str) -> Tuple[Any, Any]: - """Get attribute or any exception.""" + """Get attribute or any exception. + + Falls back to ``inspect.getattr_static`` if ``getattr`` raises + ``AttributeError``, so that descriptors which signal "no such + attribute" (e.g. SWIG bindings, lazy properties) are still surfaced. + """ try: return (None, getattr(obj, attr_name)) + except AttributeError: + try: + return (None, inspect.getattr_static(obj, attr_name)) + except AttributeError as error: + return (error, None) except Exception as error: return (error, None) diff --git a/tests/test_inspect.py b/tests/test_inspect.py index d6972fb5ed..623ba69404 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -382,6 +382,23 @@ def __class__(self): assert False, f"Object with no __class__ shouldn't raise {e}" +def test_inspect_getattr_static_fallback(): + """Issue #3794 - Properties that raise AttributeError fall back to + inspect.getattr_static so the descriptor is shown instead of the error.""" + + class Thing: + @property + def maybe(self): + raise AttributeError("not available on this instance") + + def __dir__(self): + return ["maybe"] + + rendered = render(Thing()) + assert "AttributeError" not in rendered + assert "property" in rendered + + def test_inspect_module_with_class(): def function(): pass