resolved RESOLVED PASS · 21 tool calls · 80 s · Textualize/rich
Handle unusual __qualname__ in inspect
<!--
Please note that Rich isn't accepting any new features at this point.
If a feature can be implemented without modifying the core library, then
they should be released as a third-party module. I can accept updates to the
core library that make it easier to extend (think hooks).
Bugfixes are always welcome of course.
Sometimes it is not clear what is a feature and what is a bug fix.
If there is any doubt, please open a discussion first.
-->
<!--
*Are you contributing typo fixes?*
If your PR solely consists of typos, at least one must be in the docs to warrant an addition to CONTRIBUTORS.md
-->
## Type of changes
- [x] Bug fix
- [ ] New feature
- [ ] Documentation / docstrings
- [x] Tests
- [ ] Other
## Checklist
- [x] I've run the latest [black](https://github.com/psf/black) with default args on new code.
- [x] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate (see note about typos above).
- [x] I've added tests for new code.
- [x] I accept that @willmcgugan may be pedantic in the code review.
## Description
I discovered, that an object with an unusual `__qualname__` attribute (not a `str`, e.g. a descriptor) causes `inspect` to fail.
This can happen if an object is created this way intentionally, but I discovered it, when decorating a class with `@functools.lru_cache` which forwards the underlying attributes using a descriptor when it can't add the to it's `__dict__` because the class uses `__slots__`:
```python
from functools import lru_cache
from rich import inspect
@lru_cache
class Klaas:
__slots__ = ("__qualname__",)
if __name__ == "__main__":
from rich.traceback import install
install()
inspect(Klaas.__qualname__)
inspect(Klaas, all=True)
```
I fixed it by checking the type of `__qualname__` falling back to `__name__` and if that isn't a `str` using the already provided fallback.
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nif __name__ == \"__main__\":\n try:\n inspect(Klaas.__qualname__)\n inspect(Klaas, all=True)\n print(\"Success\")\n except Exception as e:\n print(f\"Failed: {e}\")\nPY\n"
} | {"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \full{"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e \u2502\n\u2502 \u2502 <member '__qualname__' of 'Klaas' objects> \u2502 \u2502\n\u2502 \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f \u2502\n\u2502 \u2502\n\u2502 30 attribute(s) not shown. Run inspect(inspect) for options. \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\nFailed: Only str or Text can be appended to Text\n", "stderr": "", "exit_code": 0} |
| 2 | run_command | {
"command": "grep -rn \"inspect\" rich | grep \"def inspect\""
} | {"status": "ok", "stdout": "rich/__init__.py:120:def inspect(\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "rich/__init__.py:120:def inspect(\n", "stderr": "", "exit_code": 0} |
| 3 | run_command | {
"command": "sed -n '120,180p' rich/__init__.py"
} | {"status": "ok", "stdout": "def inspect(\n obj: Any,\n *,\n console: Optional[\"Console\"] = None,\n title: Optional[str] = None,\n help: bool = False,\n methodfull{"status": "ok", "stdout": "def inspect(\n obj: Any,\n *,\n console: Optional[\"Console\"] = None,\n title: Optional[str] = None,\n help: bool = False,\n methods: bool = False,\n docs: bool = True,\n private: bool = False,\n dunder: bool = False,\n sort: bool = True,\n all: bool = False,\n value: bool = True,\n) -> None:\n \"\"\"Inspect any Python object.\n\n * inspect(<OBJECT>) to see summarized info.\n * inspect(<OBJECT>, methods=True) to see methods.\n * inspect(<OBJECT>, help=True) to see full (non-abbreviated) help.\n * inspect(<OBJECT>, private=True) to see private attributes (single underscore).\n * inspect(<OBJECT>, dunder=True) to see attributes beginning with double underscore.\n * inspect(<OBJECT>, all=True) to see all attributes.\n\n Args:\n obj (Any): An object to inspect.\n title (str, optional): Title to display over inspect result, or None use type. Defaults to None.\n help (bool, optional): Show full help text rather than just first paragraph. Defaults to False.\n methods (bool, optional): Enable inspection of callables. Defaults to False.\n docs (bool, optional): Also render doc strings. Defaults to True.\n private (bool, optional): Show private attributes (beginning with underscore). Defaults to False.\n dunder (bool, optional): Show attributes starting with double underscore. Defaults to False.\n sort (bool, optional): Sort attributes alphabetically. Defaults to True.\n all (bool, optional): Show all attributes. Defaults to False.\n value (bool, optional): Pretty print value. Defaults to True.\n \"\"\"\n _console = console or get_console()\n from rich._inspect import Inspect\n\n # Special case for inspect(inspect)\n is_inspect = obj is inspect\n\n _inspect = Inspect(\n obj,\n title=title,\n help=is_inspect or help,\n methods=is_inspect or methods,\n docs=is_inspect o |
| 4 | run_command | {
"command": "grep -rn \"__qualname__\" rich | head -20"
} | {"status": "ok", "stdout": "rich/_inspect.py:103: qualname = name or getattr(obj, \"__qualname__\", name)\nrich/_inspect.py:253: f'{getattr(type_, \"__module__\full{"status": "ok", "stdout": "rich/_inspect.py:103: qualname = name or getattr(obj, \"__qualname__\", name)\nrich/_inspect.py:253: f'{getattr(type_, \"__module__\", \"\")}.{getattr(type_, \"__qualname__\", \"\")}'\n", "stderr": "", "exit_code": 0} |
| 5 | run_command | {
"command": "sed -n '90,110p' rich/_inspect.py"
} | {"status": "ok", "stdout": "\n source_filename: Optional[str] = None\n try:\n source_filename = getfile(obj)\n except (OSError, TypeError):\n # OSError is raisefull{"status": "ok", "stdout": "\n source_filename: Optional[str] = None\n try:\n source_filename = getfile(obj)\n except (OSError, TypeError):\n # OSError is raised if obj has no source file, e.g. when defined in REPL.\n pass\n\n callable_name = Text(name, style=\"inspect.callable\")\n if source_filename:\n callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or getattr(obj, \"__qualname__\", name)\n\n # If obj is a module, there may be classes (which are callable) to display\n if inspect.isclass(obj):\n prefix = \"class\"\n elif inspect.iscoroutinefunction(obj):\n prefix = \"async def\"\n else:\n", "stderr": "", "exit_code": 0} |
| 6 | run_command | {
"command": "sed -n '100,110p' rich/_inspect.py"
} | {"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or gefull{"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or getattr(obj, \"__qualname__\", name)\n\n # If obj is a module, there may be classes (which are callable) to display\n if inspect.isclass(obj):\n prefix = \"class\"\n elif inspect.iscoroutinefunction(obj):\n prefix = \"async def\"\n else:\n", "stderr": "", "exit_code": 0} |
| 7 | run_command | {
"command": "sed -n '111,120p' rich/_inspect.py"
} | {"status": "ok", "stdout": " prefix = \"def\"\n\n qual_signature = Text.assemble(\n (f\"{prefix} \", f\"inspect.{prefix.replace(' ', '_')}\"),\n (qualname, \"infull{"status": "ok", "stdout": " prefix = \"def\"\n\n qual_signature = Text.assemble(\n (f\"{prefix} \", f\"inspect.{prefix.replace(' ', '_')}\"),\n (qualname, \"inspect.callable\"),\n signature_text,\n )\n\n return qual_signature\n\n", "stderr": "", "exit_code": 0} |
| 8 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nif __name__ == \"__main__\":\n try:\n inspect(Klaas, all=True)\n print(\"Success\")\n except Exception as e:\n print(f\"Failed: {e}\")\nPY\n"
} | {"status": "ok", "stdout": "Failed: Only str or Text can be appended to Text\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Failed: Only str or Text can be appended to Text\n", "stderr": "", "exit_code": 0} |
| 9 | run_command | {
"command": "sed -n '100,120p' rich/_inspect.py"
} | {"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or gefull{"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or getattr(obj, \"__qualname__\", name)\n\n # If obj is a module, there may be classes (which are callable) to display\n if inspect.isclass(obj):\n prefix = \"class\"\n elif inspect.iscoroutinefunction(obj):\n prefix = \"async def\"\n else:\n prefix = \"def\"\n\n qual_signature = Text.assemble(\n (f\"{prefix} \", f\"inspect.{prefix.replace(' ', '_')}\"),\n (qualname, \"inspect.callable\"),\n signature_text,\n )\n\n return qual_signature\n\n", "stderr": "", "exit_code": 0} |
| 10 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nprint(f\"Klaas.__qualname__ type: {type(Klaas.__qualname__)}\")\nPY\n"
} | {"status": "ok", "stdout": "Klaas.__qualname__ type: <class 'member_descriptor'>\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Klaas.__qualname__ type: <class 'member_descriptor'>\n", "stderr": "", "exit_code": 0} |
| 11 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nprint(f\"Klaas.__name__ type: {type(Klaas.__name__)}\")\nPY\n"
} | {"status": "ok", "stdout": "Klaas.__name__ type: <class 'str'>\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Klaas.__name__ type: <class 'str'>\n", "stderr": "", "exit_code": 0} |
| 12 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\n# This is where it fails in rich/_inspect.py\n# qualname = name or getattr(obj, \"__qualname__\", name)\n# Text.assemble(..., (qualname, \"inspect.callable\"), ...)\n\n# If qualname is not a string, Text.assemble fails.\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 13 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.text import Text\ntry:\n Text.assemble((\"test\", \"style\"), (<1,), \"style\")\nexcept Exception as e:\n print(e)\nPY\n"
} | {"status": "error", "error_type": "CommandError", "error_message": " File \"<stdin>\", line 3\n Text.assemble((\"test\", \"style\"), (<1,), \"style\")\n ^\nSyntfull{"status": "error", "error_type": "CommandError", "error_message": " File \"<stdin>\", line 3\n Text.assemble((\"test\", \"style\"), (<1,), \"style\")\n ^\nSyntaxError: invalid syntax\n", "details": {"stdout": "", "stderr": " File \"<stdin>\", line 3\n Text.assemble((\"test\", \"style\"), (<1,), \"style\")\n ^\nSyntaxError: invalid syntax\n", "exit_code": 1}} |
| 14 | run_command | {
"command": "python3 - <<'PY'\nfrom rich.text import Text\ntry:\n Text.assemble((\"test\", \"style\"), (1, \"style\"))\nexcept Exception as e:\n print(e)\nPY\n"
} | {"status": "ok", "stdout": "Only str or Text can be appended to Text\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "Only str or Text can be appended to Text\n", "stderr": "", "exit_code": 0} |
| 15 | run_command | {
"command": "sed -n '100,110p' rich/_inspect.py"
} | {"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or gefull{"status": "ok", "stdout": " callable_name.stylize(f\"link file://{source_filename}\")\n signature_text = self.highlighter(_signature)\n\n qualname = name or getattr(obj, \"__qualname__\", name)\n\n # If obj is a module, there may be classes (which are callable) to display\n if inspect.isclass(obj):\n prefix = \"class\"\n elif inspect.iscoroutinefunction(obj):\n prefix = \"async def\"\n else:\n", "stderr": "", "exit_code": 0} |
| 16 | run_command | {
"command": "python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"rich/_inspect.py\")\ns = p.read_text()\nold = ' qualname = name or getattr(obj, \"__qualname__\", name)'\nnew = \"\"\" qualname = name or getattr(obj, \"__qualname__\", name)\n if not isinstance(qualname, str):\n qualname = getattr(obj, \"__name__\", name)\n if not isinstance(qualname, str):\n qualname = str(qualname)\"\"\"\nassert s.count(old) == 1\nt = s.replace(old, new)\np.write_text(t)\nPY\n"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 17 | run_command | {
"command": "python3 -m py_compile rich/_inspect.py"
} | {"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0} |
| 18 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nif __name__ == \"__main__\":\n try:\n inspect(Klaas.__qualname__)\n inspect(Klaas, all=True)\n print(\"Success\")\n except Exception as e:\n print(f\"Failed: {e}\")\nPY\n"
} | {"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \full{"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e \u2502\n\u2502 \u2502 <member '__qualname__' of 'Klaas' objects> \u2502 \u2502\n\u2502 \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f \u2502\n\u2502 \u2502\n\u2502 30 attribute(s) not shown. Run inspect(inspect) for options. \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <functools._lru_cache_wrapper object at 0x1063517a0> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 def Klaas(): \u2502\n\u2502 |
| 19 | run_command | {
"command": "git diff"
} | {"status": "ok", "stdout": "diff --git a/rich/_inspect.py b/rich/_inspect.py\nindex 27d65cec..cace272d 100644\n--- a/rich/_inspect.py\n+++ b/rich/_inspect.py\n@full{"status": "ok", "stdout": "diff --git a/rich/_inspect.py b/rich/_inspect.py\nindex 27d65cec..cace272d 100644\n--- a/rich/_inspect.py\n+++ b/rich/_inspect.py\n@@ -101,6 +101,10 @@ class Inspect(JupyterMixin):\n signature_text = self.highlighter(_signature)\n \n qualname = name or getattr(obj, \"__qualname__\", name)\n+ if not isinstance(qualname, str):\n+ qualname = getattr(obj, \"__name__\", name)\n+ if not isinstance(qualname, str):\n+ qualname = str(qualname)\n \n # If obj is a module, there may be classes (which are callable) to display\n if inspect.isclass(obj):\n", "stderr": "", "exit_code": 0} |
| 20 | run_command | {
"command": "python3 - <<'PY'\nfrom functools import lru_cache\nfrom rich import inspect\n\n@lru_cache\nclass Klaas:\n __slots__ = (\"__qualname__\",)\n\nif __name__ == \"__main__\":\n try:\n inspect(Klaas.__qualname__)\n inspect(Klaas, all=True)\n print(\"Success\")\n except Exception as e:\n print(f\"Failed: {e}\")\nPY\n"
} | {"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \full{"status": "ok", "stdout": "\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <class 'member_descriptor'> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e \u2502\n\u2502 \u2502 <member '__qualname__' of 'Klaas' objects> \u2502 \u2502\n\u2502 \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f \u2502\n\u2502 \u2502\n\u2502 30 attribute(s) not shown. Run inspect(inspect) for options. \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 <functools._lru_cache_wrapper object at 0x1055fd7a0> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 def Klaas(): \u2502\n\u2502 |
| 21 | run_command | {
"command": "submit_patch"
} | {"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/basfull{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bash: submit_patch: command not found\n", "exit_code": 127}} |
| 22 | submit_patch | {} | {"status": "ok", "patch_size": 604, "files_changed": 1}full{"status": "ok", "patch_size": 604, "files_changed": 1} |
diff --git a/rich/_inspect.py b/rich/_inspect.py
index 27d65cec..cace272d 100644
--- a/rich/_inspect.py
+++ b/rich/_inspect.py
@@ -101,6 +101,10 @@ class Inspect(JupyterMixin):
signature_text = self.highlighter(_signature)
qualname = name or getattr(obj, "__qualname__", name)
+ if not isinstance(qualname, str):
+ qualname = getattr(obj, "__name__", name)
+ if not isinstance(qualname, str):
+ qualname = str(qualname)
# If obj is a module, there may be classes (which are callable) to display
if inspect.isclass(obj):
...s....sss................................... [100%] 42 passed, 4 skipped in 0.13s