diff options
Diffstat (limited to 'tests/test_inspect.py')
-rw-r--r-- | tests/test_inspect.py | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/tests/test_inspect.py b/tests/test_inspect.py new file mode 100644 index 0000000..0ffc313 --- /dev/null +++ b/tests/test_inspect.py @@ -0,0 +1,205 @@ +import io +import sys + +import pytest + +from rich import inspect +from rich.console import Console + + +skip_py36 = pytest.mark.skipif( + sys.version_info.minor == 6 and sys.version_info.major == 3, + reason="rendered differently on py3.6", +) + + +skip_py37 = pytest.mark.skipif( + sys.version_info.minor == 7 and sys.version_info.major == 3, + reason="rendered differently on py3.7", +) + + +def render(obj, methods=False, value=False, width=50) -> str: + console = Console(file=io.StringIO(), width=width, legacy_windows=False) + inspect(obj, console=console, methods=methods, value=value) + return console.file.getvalue() + + +class InspectError(Exception): + def __str__(self) -> str: + return "INSPECT ERROR" + + +class Foo: + """Foo test + + Second line + """ + + def __init__(self, foo: int) -> None: + """constructor docs.""" + self.foo = foo + + @property + def broken(self): + raise InspectError() + + def method(self, a, b) -> str: + """Multi line + + docs. + """ + return "test" + + def __dir__(self): + return ["__init__", "broken", "method"] + + +@skip_py36 +def test_render(): + console = Console(width=100, file=io.StringIO(), legacy_windows=False) + + foo = Foo("hello") + inspect(foo, console=console, all=True, value=False) + result = console.file.getvalue() + print(repr(result)) + expected = "╭────────────── <class 'tests.test_inspect.Foo'> ──────────────╮\n│ Foo test │\n│ │\n│ broken = InspectError() │\n│ __init__ = def __init__(foo: int) -> None: constructor docs. │\n│ method = def method(a, b) -> str: Multi line │\n╰──────────────────────────────────────────────────────────────╯\n" + assert expected == result + + +def test_inspect_text(): + + expected = ( + "╭──────────────── <class 'str'> ─────────────────╮\n" + "│ str(object='') -> str │\n" + "│ str(bytes_or_buffer[, encoding[, errors]]) -> │\n" + "│ str │\n" + "│ │\n" + "│ 33 attribute(s) not shown. Run │\n" + "│ inspect(inspect) for options. │\n" + "╰────────────────────────────────────────────────╯\n" + ) + print(repr(expected)) + assert expected == render("Hello") + + +@skip_py36 +@skip_py37 +def test_inspect_empty_dict(): + + expected = ( + "╭──────────────── <class 'dict'> ────────────────╮\n" + "│ dict() -> new empty dictionary │\n" + "│ dict(mapping) -> new dictionary initialized │\n" + "│ from a mapping object's │\n" + "│ (key, value) pairs │\n" + "│ dict(iterable) -> new dictionary initialized │\n" + "│ as if via: │\n" + "│ d = {} │\n" + "│ for k, v in iterable: │\n" + "│ d[k] = v │\n" + "│ dict(**kwargs) -> new dictionary initialized │\n" + "│ with the name=value pairs │\n" + "│ in the keyword argument list. For │\n" + "│ example: dict(one=1, two=2) │\n" + "│ │\n" + ) + assert render({}).startswith(expected) + + +def test_inspect_builtin_function(): + + expected = ( + "╭────────── <built-in function print> ───────────╮\n" + "│ def print(...) │\n" + "│ │\n" + "│ print(value, ..., sep=' ', end='\\n', │\n" + "│ file=sys.stdout, flush=False) │\n" + "│ │\n" + "│ 29 attribute(s) not shown. Run │\n" + "│ inspect(inspect) for options. │\n" + "╰────────────────────────────────────────────────╯\n" + ) + assert expected == render(print) + + +@skip_py36 +def test_inspect_integer(): + + expected = ( + "╭────── <class 'int'> ───────╮\n" + "│ int([x]) -> integer │\n" + "│ int(x, base=10) -> integer │\n" + "│ │\n" + "│ denominator = 1 │\n" + "│ imag = 0 │\n" + "│ numerator = 1 │\n" + "│ real = 1 │\n" + "╰────────────────────────────╯\n" + ) + assert expected == render(1) + + +@skip_py36 +def test_inspect_integer_with_value(): + + expected = "╭────── <class 'int'> ───────╮\n│ int([x]) -> integer │\n│ int(x, base=10) -> integer │\n│ │\n│ ╭────────────────────────╮ │\n│ │ 1 │ │\n│ ╰────────────────────────╯ │\n│ │\n│ denominator = 1 │\n│ imag = 0 │\n│ numerator = 1 │\n│ real = 1 │\n╰────────────────────────────╯\n" + value = render(1, value=True) + print(repr(value)) + assert expected == value + + +@skip_py36 +@skip_py37 +def test_inspect_integer_with_methods(): + + expected = ( + "╭──────────────── <class 'int'> ─────────────────╮\n" + "│ int([x]) -> integer │\n" + "│ int(x, base=10) -> integer │\n" + "│ │\n" + "│ denominator = 1 │\n" + "│ imag = 0 │\n" + "│ numerator = 1 │\n" + "│ real = 1 │\n" + "│ as_integer_ratio = def as_integer_ratio(): │\n" + "│ Return integer ratio. │\n" + "│ bit_length = def bit_length(): Number of │\n" + "│ bits necessary to represent │\n" + "│ self in binary. │\n" + "│ conjugate = def conjugate(...) Returns │\n" + "│ self, the complex conjugate │\n" + "│ of any int. │\n" + "│ from_bytes = def from_bytes(bytes, │\n" + "│ byteorder, *, │\n" + "│ signed=False): Return the │\n" + "│ integer represented by the │\n" + "│ given array of bytes. │\n" + "│ to_bytes = def to_bytes(length, │\n" + "│ byteorder, *, │\n" + "│ signed=False): Return an │\n" + "│ array of bytes representing │\n" + "│ an integer. │\n" + "╰────────────────────────────────────────────────╯\n" + ) + assert expected == render(1, methods=True) + + +@skip_py36 +@skip_py37 +def test_broken_call_attr(): + class NotCallable: + __call__ = 5 # Passes callable() but isn't really callable + + def __repr__(self): + return "NotCallable()" + + class Foo: + foo = NotCallable() + + foo = Foo() + assert callable(foo.foo) + expected = "╭─ <class 'tests.test_inspect.test_broken_call_attr.<locals>.Foo'> ─╮\n│ foo = NotCallable() │\n╰───────────────────────────────────────────────────────────────────╯\n" + result = render(foo, methods=True, width=100) + print(repr(result)) + assert expected == result |