[_inspect] Add docstrings to our generic functions that determine the type of an object

This commit is contained in:
Olivier Philippon 2022-07-01 16:50:59 +01:00
parent 8b17bc61f8
commit 36ffdcf7bd
3 changed files with 41 additions and 32 deletions

View File

@ -2,7 +2,7 @@ from __future__ import absolute_import
import inspect
from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature
from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union, cast
from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union
from .console import Group, RenderableType
from .control import escape_control_codes
@ -235,27 +235,36 @@ class Inspect(JupyterMixin):
return escape_control_codes(docs)
def object_types_tree(obj: Union[object, Type[Any]]) -> Tuple[type, ...]:
obj_type: Type[Any] = type(obj)
if obj_type is type:
# the given object was already a type: let's use this instead:
obj_type = cast(Type[Any], obj) # I wish MyPy could infer this itself :-/
return getattr(obj_type, "__mro__", ())
def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]:
"""Returns the MRO of an object's class, or of the object itself if it's a class."""
if not hasattr(obj, "__mro__"):
# N.B. we cannot use `if type(obj) is type` here because it doesn't work with
# some types of classes, such as the ones that use abc.ABCMeta.
obj = type(obj)
return getattr(obj, "__mro__", ())
def object_types_tree_as_strings(obj: object) -> Tuple[str, ...]:
return tuple(
[
f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}'
for type_ in object_types_tree(obj)
]
)
def get_object_types_mro_as_strings(obj: object) -> Collection[str]:
"""
Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class.
Examples:
`object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']`
"""
return [
f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}'
for type_ in get_object_types_mro(obj)
]
def object_is_one_of_types(
def is_object_one_of_types(
obj: object, fully_qualified_types_names: Collection[str]
) -> bool:
for type_name in object_types_tree_as_strings(obj):
"""
Returns `True` if the given object's class (or the object itself, if it's a class) has one of the
fully qualified names in its MRO.
"""
for type_name in get_object_types_mro_as_strings(obj):
if type_name in fully_qualified_types_names:
return True
return False

View File

@ -123,7 +123,7 @@ def _ipy_display_hook(
expand_all: bool = False,
) -> None:
# needed here to prevent circular import:
from ._inspect import object_is_one_of_types
from ._inspect import is_object_one_of_types
from .console import ConsoleRenderable
# always skip rich generated jupyter renderables or None values
@ -161,7 +161,7 @@ def _ipy_display_hook(
# as they result in the rendering of useless and noisy lines such as `<Figure size 432x288 with 1 Axes>`.
# What does this do?
# --> if the class has "matplotlib.artist.Artist" in its hierarchy for example, we don't render it.
if object_is_one_of_types(value, JUPYTER_CLASSES_TO_NOT_RENDER):
if is_object_one_of_types(value, JUPYTER_CLASSES_TO_NOT_RENDER):
return
# certain renderables should start on a new line

View File

@ -7,9 +7,9 @@ import pytest
from rich import inspect
from rich._inspect import (
object_is_one_of_types,
object_types_tree,
object_types_tree_as_strings,
get_object_types_mro,
get_object_types_mro_as_strings,
is_object_one_of_types,
)
from rich.console import Console
@ -375,25 +375,25 @@ def test_can_handle_special_characters_in_docstrings(
[FooSubclass, (FooSubclass, Foo, object)],
),
)
def test_object_types_tree(obj: object, expected_result: Sequence[Type]):
assert object_types_tree(obj) == expected_result
def test_object_types_mro(obj: object, expected_result: Sequence[Type]):
assert get_object_types_mro(obj) == expected_result
@pytest.mark.parametrize(
"obj,expected_result",
(
# fmt: off
["hi", ("builtins.str", "builtins.object")],
[str, ("builtins.str", "builtins.object")],
[Foo(1), (f"{__name__}.Foo", "builtins.object")],
[Foo, (f"{__name__}.Foo", "builtins.object")],
[FooSubclass(1), (f"{__name__}.FooSubclass", f"{__name__}.Foo", "builtins.object")],
[FooSubclass, (f"{__name__}.FooSubclass", f"{__name__}.Foo", "builtins.object")],
["hi", ["builtins.str", "builtins.object"]],
[str, ["builtins.str", "builtins.object"]],
[Foo(1), [f"{__name__}.Foo", "builtins.object"]],
[Foo, [f"{__name__}.Foo", "builtins.object"]],
[FooSubclass(1), [f"{__name__}.FooSubclass", f"{__name__}.Foo", "builtins.object"]],
[FooSubclass, [f"{__name__}.FooSubclass", f"{__name__}.Foo", "builtins.object"]],
# fmt: on
),
)
def test_object_types_tree_as_strings(obj: object, expected_result: Sequence[str]):
assert object_types_tree_as_strings(obj) == expected_result
def test_object_types_mro_as_strings(obj: object, expected_result: Sequence[str]):
assert get_object_types_mro_as_strings(obj) == expected_result
@pytest.mark.parametrize(
@ -418,4 +418,4 @@ def test_object_types_tree_as_strings(obj: object, expected_result: Sequence[str
def test_object_is_one_of_types(
obj: object, types: Sequence[str], expected_result: bool
):
assert object_is_one_of_types(obj, types) is expected_result
assert is_object_one_of_types(obj, types) is expected_result