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