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)
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.
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 ofname
.
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.
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.
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.
Inherited Members
- pytermgui.widgets.containers.Container
- styles
- chars
- keys
- serialized
- vertical_align
- allow_fullscreen
- overflow
- sidelength
- content_dimensions
- selectables
- selectables_length
- selected
- box
- get_change
- lazy_add
- get_lines
- set_widgets
- serialize
- pop
- remove
- set_recursive_depth
- select
- center
- handle_mouse
- execute_binding
- handle_key
- wipe
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 ofname
.