Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/rich/_inspect.py: 4%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import inspect
2from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature
3from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union
5from .console import Group, RenderableType
6from .control import escape_control_codes
7from .highlighter import ReprHighlighter
8from .jupyter import JupyterMixin
9from .panel import Panel
10from .pretty import Pretty
11from .table import Table
12from .text import Text, TextType
15def _first_paragraph(doc: str) -> str:
16 """Get the first paragraph from a docstring."""
17 paragraph, _, _ = doc.partition("\n\n")
18 return paragraph
21class Inspect(JupyterMixin):
22 """A renderable to inspect any Python Object.
24 Args:
25 obj (Any): An object to inspect.
26 title (str, optional): Title to display over inspect result, or None use type. Defaults to None.
27 help (bool, optional): Show full help text rather than just first paragraph. Defaults to False.
28 methods (bool, optional): Enable inspection of callables. Defaults to False.
29 docs (bool, optional): Also render doc strings. Defaults to True.
30 private (bool, optional): Show private attributes (beginning with underscore). Defaults to False.
31 dunder (bool, optional): Show attributes starting with double underscore. Defaults to False.
32 sort (bool, optional): Sort attributes alphabetically. Defaults to True.
33 all (bool, optional): Show all attributes. Defaults to False.
34 value (bool, optional): Pretty print value of object. Defaults to True.
35 """
37 def __init__(
38 self,
39 obj: Any,
40 *,
41 title: Optional[TextType] = None,
42 help: bool = False,
43 methods: bool = False,
44 docs: bool = True,
45 private: bool = False,
46 dunder: bool = False,
47 sort: bool = True,
48 all: bool = True,
49 value: bool = True,
50 ) -> None:
51 self.highlighter = ReprHighlighter()
52 self.obj = obj
53 self.title = title or self._make_title(obj)
54 if all:
55 methods = private = dunder = True
56 self.help = help
57 self.methods = methods
58 self.docs = docs or help
59 self.private = private or dunder
60 self.dunder = dunder
61 self.sort = sort
62 self.value = value
64 def _make_title(self, obj: Any) -> Text:
65 """Make a default title."""
66 title_str = (
67 str(obj)
68 if (isclass(obj) or callable(obj) or ismodule(obj))
69 else str(type(obj))
70 )
71 title_text = self.highlighter(title_str)
72 return title_text
74 def __rich__(self) -> Panel:
75 return Panel.fit(
76 Group(*self._render()),
77 title=self.title,
78 border_style="scope.border",
79 padding=(0, 1),
80 )
82 def _get_signature(self, name: str, obj: Any) -> Optional[Text]:
83 """Get a signature for a callable."""
84 try:
85 _signature = str(signature(obj)) + ":"
86 except ValueError:
87 _signature = "(...)"
88 except TypeError:
89 return None
91 source_filename: Optional[str] = None
92 try:
93 source_filename = getfile(obj)
94 except (OSError, TypeError):
95 # OSError is raised if obj has no source file, e.g. when defined in REPL.
96 pass
98 callable_name = Text(name, style="inspect.callable")
99 if source_filename:
100 callable_name.stylize(f"link file://{source_filename}")
101 signature_text = self.highlighter(_signature)
103 qualname = name or getattr(obj, "__qualname__", name)
105 # If obj is a module, there may be classes (which are callable) to display
106 if inspect.isclass(obj):
107 prefix = "class"
108 elif inspect.iscoroutinefunction(obj):
109 prefix = "async def"
110 else:
111 prefix = "def"
113 qual_signature = Text.assemble(
114 (f"{prefix} ", f"inspect.{prefix.replace(' ', '_')}"),
115 (qualname, "inspect.callable"),
116 signature_text,
117 )
119 return qual_signature
121 def _render(self) -> Iterable[RenderableType]:
122 """Render object."""
124 def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]:
125 key, (_error, value) = item
126 return (callable(value), key.strip("_").lower())
128 def safe_getattr(attr_name: str) -> Tuple[Any, Any]:
129 """Get attribute or any exception."""
130 try:
131 return (None, getattr(obj, attr_name))
132 except Exception as error:
133 return (error, None)
135 obj = self.obj
136 keys = dir(obj)
137 total_items = len(keys)
138 if not self.dunder:
139 keys = [key for key in keys if not key.startswith("__")]
140 if not self.private:
141 keys = [key for key in keys if not key.startswith("_")]
142 not_shown_count = total_items - len(keys)
143 items = [(key, safe_getattr(key)) for key in keys]
144 if self.sort:
145 items.sort(key=sort_items)
147 items_table = Table.grid(padding=(0, 1), expand=False)
148 items_table.add_column(justify="right")
149 add_row = items_table.add_row
150 highlighter = self.highlighter
152 if callable(obj):
153 signature = self._get_signature("", obj)
154 if signature is not None:
155 yield signature
156 yield ""
158 if self.docs:
159 _doc = self._get_formatted_doc(obj)
160 if _doc is not None:
161 doc_text = Text(_doc, style="inspect.help")
162 doc_text = highlighter(doc_text)
163 yield doc_text
164 yield ""
166 if self.value and not (isclass(obj) or callable(obj) or ismodule(obj)):
167 yield Panel(
168 Pretty(obj, indent_guides=True, max_length=10, max_string=60),
169 border_style="inspect.value.border",
170 )
171 yield ""
173 for key, (error, value) in items:
174 key_text = Text.assemble(
175 (
176 key,
177 "inspect.attr.dunder" if key.startswith("__") else "inspect.attr",
178 ),
179 (" =", "inspect.equals"),
180 )
181 if error is not None:
182 warning = key_text.copy()
183 warning.stylize("inspect.error")
184 add_row(warning, highlighter(repr(error)))
185 continue
187 if callable(value):
188 if not self.methods:
189 continue
191 _signature_text = self._get_signature(key, value)
192 if _signature_text is None:
193 add_row(key_text, Pretty(value, highlighter=highlighter))
194 else:
195 if self.docs:
196 docs = self._get_formatted_doc(value)
197 if docs is not None:
198 _signature_text.append("\n" if "\n" in docs else " ")
199 doc = highlighter(docs)
200 doc.stylize("inspect.doc")
201 _signature_text.append(doc)
203 add_row(key_text, _signature_text)
204 else:
205 add_row(key_text, Pretty(value, highlighter=highlighter))
206 if items_table.row_count:
207 yield items_table
208 elif not_shown_count:
209 yield Text.from_markup(
210 f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] "
211 f"Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options."
212 )
214 def _get_formatted_doc(self, object_: Any) -> Optional[str]:
215 """
216 Extract the docstring of an object, process it and returns it.
217 The processing consists in cleaning up the doctring's indentation,
218 taking only its 1st paragraph if `self.help` is not True,
219 and escape its control codes.
221 Args:
222 object_ (Any): the object to get the docstring from.
224 Returns:
225 Optional[str]: the processed docstring, or None if no docstring was found.
226 """
227 docs = getdoc(object_)
228 if docs is None:
229 return None
230 docs = cleandoc(docs).strip()
231 if not self.help:
232 docs = _first_paragraph(docs)
233 return escape_control_codes(docs)
236def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]:
237 """Returns the MRO of an object's class, or of the object itself if it's a class."""
238 if not hasattr(obj, "__mro__"):
239 # N.B. we cannot use `if type(obj) is type` here because it doesn't work with
240 # some types of classes, such as the ones that use abc.ABCMeta.
241 obj = type(obj)
242 return getattr(obj, "__mro__", ())
245def get_object_types_mro_as_strings(obj: object) -> Collection[str]:
246 """
247 Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class.
249 Examples:
250 `object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']`
251 """
252 return [
253 f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}'
254 for type_ in get_object_types_mro(obj)
255 ]
258def is_object_one_of_types(
259 obj: object, fully_qualified_types_names: Collection[str]
260) -> bool:
261 """
262 Returns `True` if the given object's class (or the object itself, if it's a class) has one of the
263 fully qualified names in its MRO.
264 """
265 for type_name in get_object_types_mro_as_strings(obj):
266 if type_name in fully_qualified_types_names:
267 return True
268 return False