import io import types import sys import re import inspect as std_inspect from IPython.core.oinspect import Inspector, OInfo from slic.utils.richcfg import replace_ipython_inspect # A realistic class to inspect class User: """Represents a user in the system.""" role = "admin" def __init__(self, name: str, age: int): self.name = name self.age = age def greet(self): """Returns a welcome message.""" return f"Welcome, {self.name}!" def dict_to_oinfo(info_dict): info_obj = OInfo( ismagic=False, isalias=False, found=True, namespace='', parent=None, obj=None ) # Fill in optional fields from dict for key, value in info_dict.items(): if hasattr(info_obj, key): setattr(info_obj, key, value) return info_obj def strip_ansi(text): ansi_escape = re.compile(r'\x1b\[[0-9;]*m') return ansi_escape.sub('', text) def test_rich_inspector_outputs_more_than_builtin(monkeypatch): # Simulate a fake IPython shell class FakeInspector: def __init__(self): self.pinfo = None class FakeIPython: def __init__(self): self.inspector = FakeInspector() fake_ipy = FakeIPython() monkeypatch.setattr("slic.utils.richcfg.get_ipython", lambda: fake_ipy) # Apply your Rich-based inspector patch replace_ipython_inspect() assert isinstance(fake_ipy.inspector.pinfo, types.FunctionType) # Capture Rich inspector output rich_buf = io.StringIO() monkeypatch.setattr("sys.stdout", rich_buf) user = User("Alice", 30) fake_ipy.inspector.pinfo(user, oname="user", detail_level=1) rich_text = rich_buf.getvalue() # Capture original IPython inspector output builtin_buf = io.StringIO() user = User("Alice", 30) original_stdout = sys.stdout sys.stdout = builtin_buf inspector = Inspector() info_dict = inspector.info(user) info = dict_to_oinfo(info_dict) inspector.pinfo(user, oname="user", info=info, detail_level=1) sys.stdout = original_stdout builtin_text = builtin_buf.getvalue() builtin_text = strip_ansi(builtin_text) print(rich_text, file=sys.__stdout__) print('\n\n\n', file=sys.__stdout__) print(builtin_text, file=sys.__stdout__) # Rich output: shows actual instance content # Rich shows live instance attribute values like: # age = 30 # name = 'Alice' # role = 'admin' assert "age = 30" in rich_text assert "name = 'Alice'" in rich_text # Built-in inspector does NOT show instance values assert "30" not in builtin_text assert "Alice" not in builtin_text # # Built-in inspector only shows the brut init function assert "def __init__(self, name: str, age: int):" in builtin_text assert "self.name = name" in builtin_text assert "self.age = age" in builtin_text # Both outputs include the same method and documentation and gives us the texts written outside functions # Method name is visible in both assert "def greet():" in rich_text assert "def greet(self):" in builtin_text # Method docstring is visible in both assert "Returns a welcome message." in rich_text assert "Returns a welcome message." in builtin_text # Informations outside functions are visible in both assert "role = 'admin'" in rich_text assert 'role = "admin"' in builtin_text # Class docstring is shown in both assert "Represents a user in the system." in rich_text assert "Represents a user in the system." in builtin_text # Structural difference: Rich wraps values in a visual box assert "╭─ user =" and "─╮" and "╰─" and "─╯" in rich_text assert rich_text.count("│") >= 10