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

Returns identifiable information used in repr.

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