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.

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

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:
View Source
366    @staticmethod
367    def highlight(text: str) -> str:
368        """Applies highlighting to a given string.
369
370        This highlight includes keywords, builtin types and more.
371
372        Args:
373            text: The string to highlight.
374
375        Returns:
376            Unparsed markup.
377        """
378
379        def _split(text: str, chars: str = " ,:|()[]{}") -> list[tuple[str, str]]:
380            """Splits given text by the given chars.
381
382            Args:
383                text: The text to split.
384                chars: A string of characters we will split by.
385
386            Returns:
387                A tuple of (delimiter, word) tuples. Delimiter is one of the characters
388                of `chars`.
389            """
390
391            last_delim = ""
392            output = []
393            word = ""
394            for char in text:
395                if char in chars:
396                    output.append((last_delim, word))
397                    last_delim = char
398                    word = ""
399                    continue
400
401                word += char
402
403            output.append((last_delim, word))
404            return output
405
406        buff = ""
407        for (delim, word) in _split(text):
408            stripped = word.strip("'")
409            highlighted = highlight_python(stripped)
410
411            if highlighted != stripped:
412                buff += delim + stripped
413                continue
414
415            buff += delim + stripped
416
417        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:
View Source
419    def inspect(self, target: object) -> Inspector:
420        """Inspects a given object, and sets self.target to it.
421
422        Returns:
423            Self, with the new content based on the inspection.
424        """
425
426        self.target = target
427        self.target_type = _determine_type(target)
428
429        # Header
430        if self.box is not INDENTED_EMPTY_BOX:
431            self.lazy_add(self._get_header())
432
433        # Body
434        if self.target_type is not ObjectType.MODULE:
435            self.lazy_add(self._get_definition())
436
437        padding = 0 if self.target_type is ObjectType.MODULE else 4
438
439        for item in self._get_docs(padding):
440            self.lazy_add(item)
441
442        keys = self._get_keys()
443
444        for key in keys:
445            attr = getattr(target, key, None)
446
447            # Don't show type aliases
448            if _is_type_alias(attr):
449                continue
450
451            # Only show functions if they are not lambdas
452            if (isfunction(attr) or callable(attr)) and (
453                hasattr(attr, "__name__") and not attr.__name__ == "<lambda>"
454            ):
455                self.lazy_add(
456                    Inspector(
457                        box=INDENTED_EMPTY_BOX,
458                        show_dunder=self.show_dunder,
459                        show_private=self.show_private,
460                        show_full_doc=False,
461                        show_qualname=self.show_qualname,
462                    ).inspect(attr)
463                )
464                continue
465
466            if not self.show_attrs:
467                continue
468
469            for i, line in enumerate(prettify(attr, parse=False).splitlines()):
470                if i == 0:
471                    line = f"- {key}: {line}"
472
473                self.lazy_add(Label(line, parent_align=0))
474
475        # Footer
476        if self.target_type in [ObjectType.LIVE, ObjectType.BUILTIN]:
477            self.lazy_add(self._get_preview())
478
479        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:
View Source
481    def debug(self) -> str:
482        """Returns identifiable information used in repr."""
483
484        if terminal.is_interactive and not terminal.displayhook_installed:
485            return "\n".join(self.get_lines())
486
487        return Widget.debug(self)

Returns identifiable information used in repr.

#   def inspect(target: object, **inspector_args) -> pytermgui.inspector.Inspector:
View Source
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

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.