pytermgui.inspector

This module provides introspection utilities.

The inspect method can be used to create an Inspector widget, which can then be used to see what is happening inside any python object. This method is usually preferred for instantiating an Inspector, as it sets up overwriteable default arguments passed to the new widget.

These defaults are meant to hide the non-important information when they are not needed, in order to allow the least amount of code for the most usability. For example, by default, when passed a class, inspect will clip the docstrings to their first lines, but show all methods. When an class' method is given it will hide show the full docstring, and also use the method's fully qualified name.

  1"""This module provides introspection utilities.
  2
  3The `inspect` method can be used to create an `Inspector` widget, which can
  4then be used to see what is happening inside any python object. This method is
  5usually preferred for instantiating an `Inspector`, as it sets up overwriteable default
  6arguments passed to the new widget.
  7
  8These defaults are meant to hide the non-important information when they are not needed,
  9in order to allow the least amount of code for the most usability. For example, by
 10default, when passed a class, `inspect` will clip the docstrings to their first lines,
 11but show all methods. When an class' method is given it will hide show the full
 12docstring, and also use the method's fully qualified name.
 13"""
 14
 15# pylint: disable=too-many-instance-attributes
 16
 17# Note: There are a lot of `type: ignore`-s in this file. These show up in places where
 18#       mypy sees potential for an error, but the possible error is already counteracted.
 19
 20from __future__ import annotations
 21
 22from enum import Enum
 23from enum import auto as _auto
 24from inspect import getdoc, getfile, isbuiltin, isclass, isfunction, ismodule, signature
 25from typing import Any
 26
 27from .highlighters import highlight_python
 28from .markup import tim
 29from .prettifiers import prettify
 30from .regex import RE_MARKUP, real_length
 31from .widgets import Container, Label, Widget, boxes
 32
 33try:
 34    from typing import get_origin  # pylint: disable=ungrouped-imports
 35
 36except NameError:
 37
 38    def get_origin(_: object) -> Any:  # type: ignore
 39        """Spoofs typing.get_origin, which is used to determine type-hints.
 40
 41        Since this function is only available >=3.8, we need to have some
 42        implementation on it for 3.7. The code checks for the origin to be
 43        non-null, as that is the value returned by this method on non-typing
 44        objects.
 45
 46        This will cause annotations to show up on 3.7, but not on 3.8+.
 47        """
 48
 49        return None
 50
 51
 52__all__ = ["Inspector", "inspect"]
 53
 54
 55class ObjectType(Enum):
 56    """All types an object can be."""
 57
 58    LIVE = _auto()
 59    """An instance that does not fit the other types."""
 60
 61    CLASS = _auto()
 62    """A class object."""
 63
 64    MODULE = _auto()
 65    """A module object."""
 66
 67    BUILTIN = _auto()
 68    """Some sort of a builtin object.
 69
 70    As builtins are often implemented in C, a lot of the standard python APIs
 71    won't work on them, so we need to treat them separately."""
 72
 73    FUNCTION = _auto()
 74    """A callable object, that is not a class."""
 75
 76
 77def _is_builtin(target: object) -> bool:
 78    """Determines if the given target is a builtin."""
 79
 80    try:
 81        signature(target)  # type: ignore
 82        return False
 83
 84    except (ValueError, TypeError):
 85        return True
 86
 87
 88def _determine_type(target: object) -> ObjectType:
 89    """Determines the type of an object."""
 90
 91    if ismodule(target):
 92        return ObjectType.MODULE
 93
 94    if _is_builtin(target):
 95        return ObjectType.BUILTIN
 96
 97    if isclass(target):
 98        return ObjectType.CLASS
 99
100    if isfunction(target) or callable(target):
101        return ObjectType.FUNCTION
102
103    return ObjectType.LIVE
104
105
106def _is_type_alias(obj: object) -> bool:
107    """Determines whether the given object is (likely) a type alias."""
108
109    return get_origin(obj) is not None
110
111
112INDENTED_EMPTY_BOX = boxes.Box(
113    [
114        "   ",
115        "   x",
116        "",
117    ]
118)
119
120
121def inspect(target: object, **inspector_args) -> Inspector:
122    """Inspects an object.
123
124    Args:
125        obj: The object to inspect.
126        show_private: Whether `_private` attributes should be shown.
127        show_dunder: Whether `__dunder__` attributes should be shown.
128        show_methods: Whether methods should be shown when encountering a class.
129        show_full_doc: If not set, docstrings are cut to only include their first
130            line.
131        show_qualname: Show fully-qualified name, e.g. `module.submodule.name`
132            instead of `name`.
133    """
134
135    def _conditionally_overwrite_kwarg(**kwargs) -> None:
136        for key, value in kwargs.items():
137            if inspector_args.get(key) is None:
138                inspector_args[key] = value
139
140    if ismodule(target):
141        _conditionally_overwrite_kwarg(
142            show_dunder=False,
143            show_private=False,
144            show_full_doc=False,
145            show_methods=True,
146            show_qualname=False,
147        )
148
149    elif isclass(target):
150        _conditionally_overwrite_kwarg(
151            show_dunder=False,
152            show_private=False,
153            show_full_doc=True,
154            show_methods=True,
155            show_qualname=False,
156        )
157
158    elif callable(target) or isbuiltin(target):
159        _conditionally_overwrite_kwarg(
160            show_dunder=False,
161            show_private=False,
162            show_full_doc=True,
163            show_methods=False,
164            show_qualname=True,
165        )
166
167    else:
168        _conditionally_overwrite_kwarg(
169            show_dunder=False,
170            show_private=False,
171            show_full_doc=True,
172            show_methods=True,
173            show_qualname=False,
174        )
175
176    inspector = Inspector(**inspector_args).inspect(target)
177
178    return inspector
179
180
181class Inspector(Container):
182    """A widget to inspect any Python object."""
183
184    def __init__(  # pylint: disable=too-many-arguments
185        self,
186        target: object = None,
187        show_private: bool = False,
188        show_dunder: bool = False,
189        show_methods: bool = False,
190        show_full_doc: bool = False,
191        show_qualname: bool = True,
192        **attrs: Any,
193    ):
194        """Initializes an inspector.
195
196        Note that most of the time, using `inspect` to do this is going to be more
197        useful.
198
199        Some styles of the inspector can be changed using the `code.name`,
200        `code.file` and `code.keyword` markup aliases. The rest of the
201        highlighting is done using `pprint`, with all of its respective colors.
202
203        Args:
204            show_private: Whether `_private` attributes should be shown.
205            show_dunder: Whether `__dunder__` attributes should be shown.
206            show_methods: Whether methods should be shown when encountering a class.
207            show_full_doc: If not set, docstrings are cut to only include their first
208                line.
209            show_qualname: Show fully-qualified name, e.g. `module.submodule.name`
210                instead of `name`.
211        """
212
213        if "box" not in attrs:
214            attrs["box"] = "EMPTY"
215
216        super().__init__(**attrs)
217
218        self.width = self.terminal.width
219
220        self.show_private = show_private
221        self.show_dunder = show_dunder
222        self.show_methods = show_methods
223        self.show_full_doc = show_full_doc
224        self.show_qualname = show_qualname
225
226        # TODO: Fix attr-showing
227        self.show_attrs = False
228
229        self.target: object
230        if target is not None:
231            self.inspect(target)
232            self.target = target
233
234    def _get_header(self) -> Container:
235        """Creates a header containing the name and location of the object."""
236
237        header = Container(box="SINGLE")
238
239        line = "[code.name]"
240        if self.target_type is ObjectType.MODULE:
241            line += self.target.__name__  # type: ignore
242
243        else:
244            cls = (
245                self.target
246                if isclass(self.target) or isfunction(self.target)
247                else self.target.__class__
248            )
249            line += cls.__module__ + "." + cls.__qualname__  # type: ignore
250
251        header += line
252
253        try:
254            file = getfile(self.target)  # type: ignore
255        except TypeError:
256            return header
257
258        header += f"Located in [code.file ~file://{file}]{file}[/]"
259
260        return header
261
262    def _get_definition(self) -> Label:
263        """Returns the definition str of self.target."""
264
265        target = self.target
266
267        if self.show_qualname:
268            name = getattr(target, "__qualname__", type(target).__name__)
269        else:
270            name = getattr(target, "__name__", type(target).__name__)
271
272        if self.target_type == ObjectType.LIVE:
273            target = type(target)
274
275        otype = _determine_type(target)
276
277        keyword = ""
278        if otype == ObjectType.CLASS:
279            keyword = "class "
280
281        elif otype == ObjectType.FUNCTION:
282            keyword = "def "
283
284        try:
285            assert callable(target)
286            definition = self.highlight(keyword + name + str(signature(target)) + ":")
287
288        except (TypeError, ValueError, AssertionError):
289            definition = self.highlight(keyword + name + "(...)")
290
291        return Label(definition, parent_align=0, non_first_padding=4)
292
293    def _get_docs(self, padding: int) -> Label:
294        """Returns a list of Labels of the object's documentation."""
295
296        default = Label("...", style="102")
297        if self.target.__doc__ is None:
298            return default
299
300        doc = getdoc(self.target)
301
302        if doc is None:
303            return default
304
305        lines = doc.splitlines()
306        if not self.show_full_doc and len(lines) > 0:
307            lines = [lines[0]]
308
309        trimmed = "\n".join(lines)
310
311        return Label(
312            trimmed.replace("[", r"\["),
313            style="102",
314            parent_align=0,
315            padding=padding,
316        )
317
318    def _get_keys(self) -> list[str]:
319        """Gets all inspectable keys of an object.
320
321        It first checks for an `__all__` attribute, and substitutes `dir` if not found.
322        Then, if there are too many keys and the given target is a module it tries to
323        list all of the present submodules.
324        """
325
326        keys = getattr(self.target, "__all__", dir(self.target))
327
328        if not self.show_dunder:
329            keys = [key for key in keys if not key.startswith("__")]
330
331        if not self.show_private:
332            keys = [key for key in keys if not (key.startswith("_") and key[1] != "_")]
333
334        if not self.show_methods:
335            keys = [
336                key for key in keys if not callable(getattr(self.target, key, None))
337            ]
338
339        keys.sort(key=lambda item: callable(getattr(self.target, item, None)))
340
341        return keys
342
343    def _get_preview(self) -> Container:
344        """Gets a Container with self.target inside."""
345
346        preview = Container(static_width=self.width // 2, parent_align=0, box="SINGLE")
347
348        if isinstance(self.target, str) and RE_MARKUP.match(self.target) is not None:
349            preview += Label(prettify(self.target, parse=False), parent_align=0)
350            return preview
351
352        for line in prettify(self.target).splitlines():
353
354            if real_length(line) > preview.width - preview.sidelength:
355                preview.width = real_length(line) + preview.sidelength
356
357            preview += Label(tim.get_markup(line), parent_align=0)
358
359        preview.width = min(preview.width, self.terminal.width - preview.sidelength)
360        return preview
361
362    @staticmethod
363    def highlight(text: str) -> str:
364        """Applies highlighting to a given string.
365
366        This highlight includes keywords, builtin types and more.
367
368        Args:
369            text: The string to highlight.
370
371        Returns:
372            Unparsed markup.
373        """
374
375        def _split(text: str, chars: str = " ,:|()[]{}") -> list[tuple[str, str]]:
376            """Splits given text by the given chars.
377
378            Args:
379                text: The text to split.
380                chars: A string of characters we will split by.
381
382            Returns:
383                A tuple of (delimiter, word) tuples. Delimiter is one of the characters
384                of `chars`.
385            """
386
387            last_delim = ""
388            output = []
389            word = ""
390            for char in text:
391                if char in chars:
392                    output.append((last_delim, word))
393                    last_delim = char
394                    word = ""
395                    continue
396
397                word += char
398
399            output.append((last_delim, word))
400            return output
401
402        buff = ""
403        for (delim, word) in _split(text):
404            stripped = word.strip("'")
405            highlighted = highlight_python(stripped)
406
407            if highlighted != stripped:
408                buff += delim + stripped
409                continue
410
411            buff += delim + stripped
412
413        return highlight_python(buff)
414
415    def inspect(self, target: object) -> Inspector:
416        """Inspects a given object, and sets self.target to it.
417
418        Returns:
419            Self, with the new content based on the inspection.
420        """
421
422        self.target = target
423        self.target_type = _determine_type(target)
424
425        # Header
426        if self.box is not INDENTED_EMPTY_BOX:
427            self.lazy_add(self._get_header())
428
429        # Body
430        if self.target_type is not ObjectType.MODULE:
431            self.lazy_add(self._get_definition())
432
433        padding = 0 if self.target_type is ObjectType.MODULE else 4
434
435        self.lazy_add(self._get_docs(padding))
436
437        keys = self._get_keys()
438
439        for key in keys:
440            attr = getattr(target, key, None)
441
442            # Don't show type aliases
443            if _is_type_alias(attr):
444                continue
445
446            # Only show functions if they are not lambdas
447            if (isfunction(attr) or callable(attr)) and (
448                hasattr(attr, "__name__") and not attr.__name__ == "<lambda>"
449            ):
450                self.lazy_add(
451                    Inspector(
452                        box=INDENTED_EMPTY_BOX,
453                        show_dunder=self.show_dunder,
454                        show_private=self.show_private,
455                        show_full_doc=False,
456                        show_qualname=self.show_qualname,
457                    ).inspect(attr)
458                )
459                continue
460
461            if not self.show_attrs:
462                continue
463
464            for i, line in enumerate(prettify(attr, parse=False).splitlines()):
465                if i == 0:
466                    line = f"- {key}: {line}"
467
468                self.lazy_add(Label(line, parent_align=0))
469
470        # Footer
471        if self.target_type in [ObjectType.LIVE, ObjectType.BUILTIN]:
472            self.lazy_add(self._get_preview())
473
474        return self
475
476    def debug(self) -> str:
477        """Returns identifiable information used in repr."""
478
479        if self.terminal.is_interactive and not self.terminal.displayhook_installed:
480            return "\n".join(self.get_lines())
481
482        return Widget.debug(self)
class Inspector(pytermgui.widgets.containers.Container):
182class Inspector(Container):
183    """A widget to inspect any Python object."""
184
185    def __init__(  # pylint: disable=too-many-arguments
186        self,
187        target: object = None,
188        show_private: bool = False,
189        show_dunder: bool = False,
190        show_methods: bool = False,
191        show_full_doc: bool = False,
192        show_qualname: bool = True,
193        **attrs: Any,
194    ):
195        """Initializes an inspector.
196
197        Note that most of the time, using `inspect` to do this is going to be more
198        useful.
199
200        Some styles of the inspector can be changed using the `code.name`,
201        `code.file` and `code.keyword` markup aliases. The rest of the
202        highlighting is done using `pprint`, with all of its respective colors.
203
204        Args:
205            show_private: Whether `_private` attributes should be shown.
206            show_dunder: Whether `__dunder__` attributes should be shown.
207            show_methods: Whether methods should be shown when encountering a class.
208            show_full_doc: If not set, docstrings are cut to only include their first
209                line.
210            show_qualname: Show fully-qualified name, e.g. `module.submodule.name`
211                instead of `name`.
212        """
213
214        if "box" not in attrs:
215            attrs["box"] = "EMPTY"
216
217        super().__init__(**attrs)
218
219        self.width = self.terminal.width
220
221        self.show_private = show_private
222        self.show_dunder = show_dunder
223        self.show_methods = show_methods
224        self.show_full_doc = show_full_doc
225        self.show_qualname = show_qualname
226
227        # TODO: Fix attr-showing
228        self.show_attrs = False
229
230        self.target: object
231        if target is not None:
232            self.inspect(target)
233            self.target = target
234
235    def _get_header(self) -> Container:
236        """Creates a header containing the name and location of the object."""
237
238        header = Container(box="SINGLE")
239
240        line = "[code.name]"
241        if self.target_type is ObjectType.MODULE:
242            line += self.target.__name__  # type: ignore
243
244        else:
245            cls = (
246                self.target
247                if isclass(self.target) or isfunction(self.target)
248                else self.target.__class__
249            )
250            line += cls.__module__ + "." + cls.__qualname__  # type: ignore
251
252        header += line
253
254        try:
255            file = getfile(self.target)  # type: ignore
256        except TypeError:
257            return header
258
259        header += f"Located in [code.file ~file://{file}]{file}[/]"
260
261        return header
262
263    def _get_definition(self) -> Label:
264        """Returns the definition str of self.target."""
265
266        target = self.target
267
268        if self.show_qualname:
269            name = getattr(target, "__qualname__", type(target).__name__)
270        else:
271            name = getattr(target, "__name__", type(target).__name__)
272
273        if self.target_type == ObjectType.LIVE:
274            target = type(target)
275
276        otype = _determine_type(target)
277
278        keyword = ""
279        if otype == ObjectType.CLASS:
280            keyword = "class "
281
282        elif otype == ObjectType.FUNCTION:
283            keyword = "def "
284
285        try:
286            assert callable(target)
287            definition = self.highlight(keyword + name + str(signature(target)) + ":")
288
289        except (TypeError, ValueError, AssertionError):
290            definition = self.highlight(keyword + name + "(...)")
291
292        return Label(definition, parent_align=0, non_first_padding=4)
293
294    def _get_docs(self, padding: int) -> Label:
295        """Returns a list of Labels of the object's documentation."""
296
297        default = Label("...", style="102")
298        if self.target.__doc__ is None:
299            return default
300
301        doc = getdoc(self.target)
302
303        if doc is None:
304            return default
305
306        lines = doc.splitlines()
307        if not self.show_full_doc and len(lines) > 0:
308            lines = [lines[0]]
309
310        trimmed = "\n".join(lines)
311
312        return Label(
313            trimmed.replace("[", r"\["),
314            style="102",
315            parent_align=0,
316            padding=padding,
317        )
318
319    def _get_keys(self) -> list[str]:
320        """Gets all inspectable keys of an object.
321
322        It first checks for an `__all__` attribute, and substitutes `dir` if not found.
323        Then, if there are too many keys and the given target is a module it tries to
324        list all of the present submodules.
325        """
326
327        keys = getattr(self.target, "__all__", dir(self.target))
328
329        if not self.show_dunder:
330            keys = [key for key in keys if not key.startswith("__")]
331
332        if not self.show_private:
333            keys = [key for key in keys if not (key.startswith("_") and key[1] != "_")]
334
335        if not self.show_methods:
336            keys = [
337                key for key in keys if not callable(getattr(self.target, key, None))
338            ]
339
340        keys.sort(key=lambda item: callable(getattr(self.target, item, None)))
341
342        return keys
343
344    def _get_preview(self) -> Container:
345        """Gets a Container with self.target inside."""
346
347        preview = Container(static_width=self.width // 2, parent_align=0, box="SINGLE")
348
349        if isinstance(self.target, str) and RE_MARKUP.match(self.target) is not None:
350            preview += Label(prettify(self.target, parse=False), parent_align=0)
351            return preview
352
353        for line in prettify(self.target).splitlines():
354
355            if real_length(line) > preview.width - preview.sidelength:
356                preview.width = real_length(line) + preview.sidelength
357
358            preview += Label(tim.get_markup(line), parent_align=0)
359
360        preview.width = min(preview.width, self.terminal.width - preview.sidelength)
361        return preview
362
363    @staticmethod
364    def highlight(text: str) -> str:
365        """Applies highlighting to a given string.
366
367        This highlight includes keywords, builtin types and more.
368
369        Args:
370            text: The string to highlight.
371
372        Returns:
373            Unparsed markup.
374        """
375
376        def _split(text: str, chars: str = " ,:|()[]{}") -> list[tuple[str, str]]:
377            """Splits given text by the given chars.
378
379            Args:
380                text: The text to split.
381                chars: A string of characters we will split by.
382
383            Returns:
384                A tuple of (delimiter, word) tuples. Delimiter is one of the characters
385                of `chars`.
386            """
387
388            last_delim = ""
389            output = []
390            word = ""
391            for char in text:
392                if char in chars:
393                    output.append((last_delim, word))
394                    last_delim = char
395                    word = ""
396                    continue
397
398                word += char
399
400            output.append((last_delim, word))
401            return output
402
403        buff = ""
404        for (delim, word) in _split(text):
405            stripped = word.strip("'")
406            highlighted = highlight_python(stripped)
407
408            if highlighted != stripped:
409                buff += delim + stripped
410                continue
411
412            buff += delim + stripped
413
414        return highlight_python(buff)
415
416    def inspect(self, target: object) -> Inspector:
417        """Inspects a given object, and sets self.target to it.
418
419        Returns:
420            Self, with the new content based on the inspection.
421        """
422
423        self.target = target
424        self.target_type = _determine_type(target)
425
426        # Header
427        if self.box is not INDENTED_EMPTY_BOX:
428            self.lazy_add(self._get_header())
429
430        # Body
431        if self.target_type is not ObjectType.MODULE:
432            self.lazy_add(self._get_definition())
433
434        padding = 0 if self.target_type is ObjectType.MODULE else 4
435
436        self.lazy_add(self._get_docs(padding))
437
438        keys = self._get_keys()
439
440        for key in keys:
441            attr = getattr(target, key, None)
442
443            # Don't show type aliases
444            if _is_type_alias(attr):
445                continue
446
447            # Only show functions if they are not lambdas
448            if (isfunction(attr) or callable(attr)) and (
449                hasattr(attr, "__name__") and not attr.__name__ == "<lambda>"
450            ):
451                self.lazy_add(
452                    Inspector(
453                        box=INDENTED_EMPTY_BOX,
454                        show_dunder=self.show_dunder,
455                        show_private=self.show_private,
456                        show_full_doc=False,
457                        show_qualname=self.show_qualname,
458                    ).inspect(attr)
459                )
460                continue
461
462            if not self.show_attrs:
463                continue
464
465            for i, line in enumerate(prettify(attr, parse=False).splitlines()):
466                if i == 0:
467                    line = f"- {key}: {line}"
468
469                self.lazy_add(Label(line, parent_align=0))
470
471        # Footer
472        if self.target_type in [ObjectType.LIVE, ObjectType.BUILTIN]:
473            self.lazy_add(self._get_preview())
474
475        return self
476
477    def debug(self) -> str:
478        """Returns identifiable information used in repr."""
479
480        if self.terminal.is_interactive and not self.terminal.displayhook_installed:
481            return "\n".join(self.get_lines())
482
483        return Widget.debug(self)

A widget to inspect any Python object.

Inspector( target: object = None, show_private: bool = False, show_dunder: bool = False, show_methods: bool = False, show_full_doc: bool = False, show_qualname: bool = True, **attrs: Any)
185    def __init__(  # pylint: disable=too-many-arguments
186        self,
187        target: object = None,
188        show_private: bool = False,
189        show_dunder: bool = False,
190        show_methods: bool = False,
191        show_full_doc: bool = False,
192        show_qualname: bool = True,
193        **attrs: Any,
194    ):
195        """Initializes an inspector.
196
197        Note that most of the time, using `inspect` to do this is going to be more
198        useful.
199
200        Some styles of the inspector can be changed using the `code.name`,
201        `code.file` and `code.keyword` markup aliases. The rest of the
202        highlighting is done using `pprint`, with all of its respective colors.
203
204        Args:
205            show_private: Whether `_private` attributes should be shown.
206            show_dunder: Whether `__dunder__` attributes should be shown.
207            show_methods: Whether methods should be shown when encountering a class.
208            show_full_doc: If not set, docstrings are cut to only include their first
209                line.
210            show_qualname: Show fully-qualified name, e.g. `module.submodule.name`
211                instead of `name`.
212        """
213
214        if "box" not in attrs:
215            attrs["box"] = "EMPTY"
216
217        super().__init__(**attrs)
218
219        self.width = self.terminal.width
220
221        self.show_private = show_private
222        self.show_dunder = show_dunder
223        self.show_methods = show_methods
224        self.show_full_doc = show_full_doc
225        self.show_qualname = show_qualname
226
227        # TODO: Fix attr-showing
228        self.show_attrs = False
229
230        self.target: object
231        if target is not None:
232            self.inspect(target)
233            self.target = target

Initializes an inspector.

Note that most of the time, using inspect to do this is going to be more useful.

Some styles of the inspector can be changed using the code.name, code.file and code.keyword markup aliases. The rest of the highlighting is done using pprint, with all of its respective colors.

Args
  • show_private: Whether _private attributes should be shown.
  • show_dunder: Whether __dunder__ attributes should be shown.
  • show_methods: Whether methods should be shown when encountering a class.
  • show_full_doc: If not set, docstrings are cut to only include their first line.
  • show_qualname: Show fully-qualified name, e.g. module.submodule.name instead of name.
@staticmethod
def highlight(text: str) -> str:
363    @staticmethod
364    def highlight(text: str) -> str:
365        """Applies highlighting to a given string.
366
367        This highlight includes keywords, builtin types and more.
368
369        Args:
370            text: The string to highlight.
371
372        Returns:
373            Unparsed markup.
374        """
375
376        def _split(text: str, chars: str = " ,:|()[]{}") -> list[tuple[str, str]]:
377            """Splits given text by the given chars.
378
379            Args:
380                text: The text to split.
381                chars: A string of characters we will split by.
382
383            Returns:
384                A tuple of (delimiter, word) tuples. Delimiter is one of the characters
385                of `chars`.
386            """
387
388            last_delim = ""
389            output = []
390            word = ""
391            for char in text:
392                if char in chars:
393                    output.append((last_delim, word))
394                    last_delim = char
395                    word = ""
396                    continue
397
398                word += char
399
400            output.append((last_delim, word))
401            return output
402
403        buff = ""
404        for (delim, word) in _split(text):
405            stripped = word.strip("'")
406            highlighted = highlight_python(stripped)
407
408            if highlighted != stripped:
409                buff += delim + stripped
410                continue
411
412            buff += delim + stripped
413
414        return highlight_python(buff)

Applies highlighting to a given string.

This highlight includes keywords, builtin types and more.

Args
  • text: The string to highlight.
Returns

Unparsed markup.

def inspect(self, target: object) -> pytermgui.inspector.Inspector:
416    def inspect(self, target: object) -> Inspector:
417        """Inspects a given object, and sets self.target to it.
418
419        Returns:
420            Self, with the new content based on the inspection.
421        """
422
423        self.target = target
424        self.target_type = _determine_type(target)
425
426        # Header
427        if self.box is not INDENTED_EMPTY_BOX:
428            self.lazy_add(self._get_header())
429
430        # Body
431        if self.target_type is not ObjectType.MODULE:
432            self.lazy_add(self._get_definition())
433
434        padding = 0 if self.target_type is ObjectType.MODULE else 4
435
436        self.lazy_add(self._get_docs(padding))
437
438        keys = self._get_keys()
439
440        for key in keys:
441            attr = getattr(target, key, None)
442
443            # Don't show type aliases
444            if _is_type_alias(attr):
445                continue
446
447            # Only show functions if they are not lambdas
448            if (isfunction(attr) or callable(attr)) and (
449                hasattr(attr, "__name__") and not attr.__name__ == "<lambda>"
450            ):
451                self.lazy_add(
452                    Inspector(
453                        box=INDENTED_EMPTY_BOX,
454                        show_dunder=self.show_dunder,
455                        show_private=self.show_private,
456                        show_full_doc=False,
457                        show_qualname=self.show_qualname,
458                    ).inspect(attr)
459                )
460                continue
461
462            if not self.show_attrs:
463                continue
464
465            for i, line in enumerate(prettify(attr, parse=False).splitlines()):
466                if i == 0:
467                    line = f"- {key}: {line}"
468
469                self.lazy_add(Label(line, parent_align=0))
470
471        # Footer
472        if self.target_type in [ObjectType.LIVE, ObjectType.BUILTIN]:
473            self.lazy_add(self._get_preview())
474
475        return self

Inspects a given object, and sets self.target to it.

Returns

Self, with the new content based on the inspection.

def debug(self) -> str:
477    def debug(self) -> str:
478        """Returns identifiable information used in repr."""
479
480        if self.terminal.is_interactive and not self.terminal.displayhook_installed:
481            return "\n".join(self.get_lines())
482
483        return Widget.debug(self)

Returns identifiable information used in repr.

def inspect(target: object, **inspector_args) -> pytermgui.inspector.Inspector:
122def inspect(target: object, **inspector_args) -> Inspector:
123    """Inspects an object.
124
125    Args:
126        obj: The object to inspect.
127        show_private: Whether `_private` attributes should be shown.
128        show_dunder: Whether `__dunder__` attributes should be shown.
129        show_methods: Whether methods should be shown when encountering a class.
130        show_full_doc: If not set, docstrings are cut to only include their first
131            line.
132        show_qualname: Show fully-qualified name, e.g. `module.submodule.name`
133            instead of `name`.
134    """
135
136    def _conditionally_overwrite_kwarg(**kwargs) -> None:
137        for key, value in kwargs.items():
138            if inspector_args.get(key) is None:
139                inspector_args[key] = value
140
141    if ismodule(target):
142        _conditionally_overwrite_kwarg(
143            show_dunder=False,
144            show_private=False,
145            show_full_doc=False,
146            show_methods=True,
147            show_qualname=False,
148        )
149
150    elif isclass(target):
151        _conditionally_overwrite_kwarg(
152            show_dunder=False,
153            show_private=False,
154            show_full_doc=True,
155            show_methods=True,
156            show_qualname=False,
157        )
158
159    elif callable(target) or isbuiltin(target):
160        _conditionally_overwrite_kwarg(
161            show_dunder=False,
162            show_private=False,
163            show_full_doc=True,
164            show_methods=False,
165            show_qualname=True,
166        )
167
168    else:
169        _conditionally_overwrite_kwarg(
170            show_dunder=False,
171            show_private=False,
172            show_full_doc=True,
173            show_methods=True,
174            show_qualname=False,
175        )
176
177    inspector = Inspector(**inspector_args).inspect(target)
178
179    return inspector

Inspects an object.

Args
  • obj: The object to inspect.
  • show_private: Whether _private attributes should be shown.
  • show_dunder: Whether __dunder__ attributes should be shown.
  • show_methods: Whether methods should be shown when encountering a class.
  • show_full_doc: If not set, docstrings are cut to only include their first line.
  • show_qualname: Show fully-qualified name, e.g. module.submodule.name instead of name.