##// END OF EJS Templates
Lint + change to f strings
Jason Grout -
Show More
@@ -1,1083 +1,1080 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for inspecting Python objects.
3 3
4 4 Uses syntax highlighting for presenting the various information elements.
5 5
6 6 Similar in spirit to the inspect module, but all calls take a name argument to
7 7 reference the name under which an object is being read.
8 8 """
9 9
10 10 # Copyright (c) IPython Development Team.
11 11 # Distributed under the terms of the Modified BSD License.
12 12
13 13 __all__ = ['Inspector','InspectColors']
14 14
15 15 # stdlib modules
16 16 import ast
17 17 import inspect
18 18 from inspect import signature
19 19 import html
20 20 import linecache
21 21 import warnings
22 22 import os
23 23 from textwrap import dedent
24 24 import types
25 25 import io as stdlib_io
26 26
27 27 from typing import Union
28 28
29 29 # IPython's own
30 30 from IPython.core import page
31 31 from IPython.lib.pretty import pretty
32 32 from IPython.testing.skipdoctest import skip_doctest
33 33 from IPython.utils import PyColorize
34 34 from IPython.utils import openpy
35 35 from IPython.utils.dir2 import safe_hasattr
36 36 from IPython.utils.path import compress_user
37 37 from IPython.utils.text import indent
38 38 from IPython.utils.wildcard import list_namespace
39 39 from IPython.utils.wildcard import typestr2type
40 40 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
41 41 from IPython.utils.py3compat import cast_unicode
42 42 from IPython.utils.colorable import Colorable
43 43 from IPython.utils.decorators import undoc
44 44
45 45 from pygments import highlight
46 46 from pygments.lexers import PythonLexer
47 47 from pygments.formatters import HtmlFormatter
48 48
49 49 def pylight(code):
50 50 return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
51 51
52 52 # builtin docstrings to ignore
53 53 _func_call_docstring = types.FunctionType.__call__.__doc__
54 54 _object_init_docstring = object.__init__.__doc__
55 55 _builtin_type_docstrings = {
56 56 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
57 57 types.FunctionType, property)
58 58 }
59 59
60 60 _builtin_func_type = type(all)
61 61 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
62 62 #****************************************************************************
63 63 # Builtin color schemes
64 64
65 65 Colors = TermColors # just a shorthand
66 66
67 67 InspectColors = PyColorize.ANSICodeColors
68 68
69 69 #****************************************************************************
70 70 # Auxiliary functions and objects
71 71
72 72 # See the messaging spec for the definition of all these fields. This list
73 73 # effectively defines the order of display
74 74 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
75 75 'length', 'file', 'definition', 'docstring', 'source',
76 76 'init_definition', 'class_docstring', 'init_docstring',
77 77 'call_def', 'call_docstring',
78 78 # These won't be printed but will be used to determine how to
79 79 # format the object
80 80 'ismagic', 'isalias', 'isclass', 'found', 'name'
81 81 ]
82 82
83 83
84 84 def object_info(**kw):
85 85 """Make an object info dict with all fields present."""
86 86 infodict = {k:None for k in info_fields}
87 87 infodict.update(kw)
88 88 return infodict
89 89
90 90
91 91 def get_encoding(obj):
92 92 """Get encoding for python source file defining obj
93 93
94 94 Returns None if obj is not defined in a sourcefile.
95 95 """
96 96 ofile = find_file(obj)
97 97 # run contents of file through pager starting at line where the object
98 98 # is defined, as long as the file isn't binary and is actually on the
99 99 # filesystem.
100 100 if ofile is None:
101 101 return None
102 102 elif ofile.endswith(('.so', '.dll', '.pyd')):
103 103 return None
104 104 elif not os.path.isfile(ofile):
105 105 return None
106 106 else:
107 107 # Print only text files, not extension binaries. Note that
108 108 # getsourcelines returns lineno with 1-offset and page() uses
109 109 # 0-offset, so we must adjust.
110 110 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
111 111 encoding, lines = openpy.detect_encoding(buffer.readline)
112 112 return encoding
113 113
114 114 def getdoc(obj) -> Union[str,None]:
115 115 """Stable wrapper around inspect.getdoc.
116 116
117 117 This can't crash because of attribute problems.
118 118
119 119 It also attempts to call a getdoc() method on the given object. This
120 120 allows objects which provide their docstrings via non-standard mechanisms
121 121 (like Pyro proxies) to still be inspected by ipython's ? system.
122 122 """
123 123 # Allow objects to offer customized documentation via a getdoc method:
124 124 try:
125 125 ds = obj.getdoc()
126 126 except Exception:
127 127 pass
128 128 else:
129 129 if isinstance(ds, str):
130 130 return inspect.cleandoc(ds)
131 131 docstr = inspect.getdoc(obj)
132 132 return docstr
133 133
134 134
135 135 def getsource(obj, oname='') -> Union[str,None]:
136 136 """Wrapper around inspect.getsource.
137 137
138 138 This can be modified by other projects to provide customized source
139 139 extraction.
140 140
141 141 Parameters
142 142 ----------
143 143 obj : object
144 144 an object whose source code we will attempt to extract
145 145 oname : str
146 146 (optional) a name under which the object is known
147 147
148 148 Returns
149 149 -------
150 150 src : unicode or None
151 151
152 152 """
153 153
154 154 if isinstance(obj, property):
155 155 sources = []
156 156 for attrname in ['fget', 'fset', 'fdel']:
157 157 fn = getattr(obj, attrname)
158 158 if fn is not None:
159 159 encoding = get_encoding(fn)
160 160 oname_prefix = ('%s.' % oname) if oname else ''
161 161 sources.append(''.join(('# ', oname_prefix, attrname)))
162 162 if inspect.isfunction(fn):
163 163 sources.append(dedent(getsource(fn)))
164 164 else:
165 165 # Default str/repr only prints function name,
166 166 # pretty.pretty prints module name too.
167 167 sources.append(
168 168 '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
169 169 )
170 170 if sources:
171 171 return '\n'.join(sources)
172 172 else:
173 173 return None
174 174
175 175 else:
176 176 # Get source for non-property objects.
177 177
178 178 obj = _get_wrapped(obj)
179 179
180 180 try:
181 181 src = inspect.getsource(obj)
182 182 except TypeError:
183 183 # The object itself provided no meaningful source, try looking for
184 184 # its class definition instead.
185 185 try:
186 186 src = inspect.getsource(obj.__class__)
187 187 except (OSError, TypeError):
188 188 return None
189 189 except OSError:
190 190 return None
191 191
192 192 return src
193 193
194 194
195 195 def is_simple_callable(obj):
196 196 """True if obj is a function ()"""
197 197 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
198 198 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
199 199
200 200 @undoc
201 201 def getargspec(obj):
202 202 """Wrapper around :func:`inspect.getfullargspec`
203 203
204 204 In addition to functions and methods, this can also handle objects with a
205 205 ``__call__`` attribute.
206 206
207 207 DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
208 208 """
209 209
210 210 warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
211 211 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
212 212
213 213 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
214 214 obj = obj.__call__
215 215
216 216 return inspect.getfullargspec(obj)
217 217
218 218 @undoc
219 219 def format_argspec(argspec):
220 220 """Format argspect, convenience wrapper around inspect's.
221 221
222 222 This takes a dict instead of ordered arguments and calls
223 223 inspect.format_argspec with the arguments in the necessary order.
224 224
225 225 DEPRECATED (since 7.10): Do not use; will be removed in future versions.
226 226 """
227 227
228 228 warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
229 229 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
230 230
231 231
232 232 return inspect.formatargspec(argspec['args'], argspec['varargs'],
233 233 argspec['varkw'], argspec['defaults'])
234 234
235 235 @undoc
236 236 def call_tip(oinfo, format_call=True):
237 237 """DEPRECATED since 6.0. Extract call tip data from an oinfo dict."""
238 238 warnings.warn(
239 239 "`call_tip` function is deprecated as of IPython 6.0"
240 240 "and will be removed in future versions.",
241 241 DeprecationWarning,
242 242 stacklevel=2,
243 243 )
244 244 # Get call definition
245 245 argspec = oinfo.get('argspec')
246 246 if argspec is None:
247 247 call_line = None
248 248 else:
249 249 # Callable objects will have 'self' as their first argument, prune
250 250 # it out if it's there for clarity (since users do *not* pass an
251 251 # extra first argument explicitly).
252 252 try:
253 253 has_self = argspec['args'][0] == 'self'
254 254 except (KeyError, IndexError):
255 255 pass
256 256 else:
257 257 if has_self:
258 258 argspec['args'] = argspec['args'][1:]
259 259
260 260 call_line = oinfo['name']+format_argspec(argspec)
261 261
262 262 # Now get docstring.
263 263 # The priority is: call docstring, constructor docstring, main one.
264 264 doc = oinfo.get('call_docstring')
265 265 if doc is None:
266 266 doc = oinfo.get('init_docstring')
267 267 if doc is None:
268 268 doc = oinfo.get('docstring','')
269 269
270 270 return call_line, doc
271 271
272 272
273 273 def _get_wrapped(obj):
274 274 """Get the original object if wrapped in one or more @decorators
275 275
276 276 Some objects automatically construct similar objects on any unrecognised
277 277 attribute access (e.g. unittest.mock.call). To protect against infinite loops,
278 278 this will arbitrarily cut off after 100 levels of obj.__wrapped__
279 279 attribute access. --TK, Jan 2016
280 280 """
281 281 orig_obj = obj
282 282 i = 0
283 283 while safe_hasattr(obj, '__wrapped__'):
284 284 obj = obj.__wrapped__
285 285 i += 1
286 286 if i > 100:
287 287 # __wrapped__ is probably a lie, so return the thing we started with
288 288 return orig_obj
289 289 return obj
290 290
291 291 def find_file(obj) -> str:
292 292 """Find the absolute path to the file where an object was defined.
293 293
294 294 This is essentially a robust wrapper around `inspect.getabsfile`.
295 295
296 296 Returns None if no file can be found.
297 297
298 298 Parameters
299 299 ----------
300 300 obj : any Python object
301 301
302 302 Returns
303 303 -------
304 304 fname : str
305 305 The absolute path to the file where the object was defined.
306 306 """
307 307 obj = _get_wrapped(obj)
308 308
309 309 fname = None
310 310 try:
311 311 fname = inspect.getabsfile(obj)
312 312 except TypeError:
313 313 # For an instance, the file that matters is where its class was
314 314 # declared.
315 315 try:
316 316 fname = inspect.getabsfile(obj.__class__)
317 317 except (OSError, TypeError):
318 318 # Can happen for builtins
319 319 pass
320 320 except OSError:
321 321 pass
322 322
323 323 return cast_unicode(fname)
324 324
325 325
326 326 def find_source_lines(obj):
327 327 """Find the line number in a file where an object was defined.
328 328
329 329 This is essentially a robust wrapper around `inspect.getsourcelines`.
330 330
331 331 Returns None if no file can be found.
332 332
333 333 Parameters
334 334 ----------
335 335 obj : any Python object
336 336
337 337 Returns
338 338 -------
339 339 lineno : int
340 340 The line number where the object definition starts.
341 341 """
342 342 obj = _get_wrapped(obj)
343 343
344 344 try:
345 345 lineno = inspect.getsourcelines(obj)[1]
346 346 except TypeError:
347 347 # For instances, try the class object like getsource() does
348 348 try:
349 349 lineno = inspect.getsourcelines(obj.__class__)[1]
350 350 except (OSError, TypeError):
351 351 return None
352 352 except OSError:
353 353 return None
354 354
355 355 return lineno
356 356
357 357 class Inspector(Colorable):
358 358
359 359 def __init__(self, color_table=InspectColors,
360 360 code_color_table=PyColorize.ANSICodeColors,
361 361 scheme=None,
362 362 str_detail_level=0,
363 363 parent=None, config=None):
364 364 super(Inspector, self).__init__(parent=parent, config=config)
365 365 self.color_table = color_table
366 366 self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
367 367 self.format = self.parser.format
368 368 self.str_detail_level = str_detail_level
369 369 self.set_active_scheme(scheme)
370 370
371 371 def _getdef(self,obj,oname='') -> Union[str,None]:
372 372 """Return the call signature for any callable object.
373 373
374 374 If any exception is generated, None is returned instead and the
375 375 exception is suppressed."""
376 376 try:
377 377 return _render_signature(signature(obj), oname)
378 378 except:
379 379 return None
380 380
381 381 def __head(self,h) -> str:
382 382 """Return a header string with proper colors."""
383 383 return '%s%s%s' % (self.color_table.active_colors.header,h,
384 384 self.color_table.active_colors.normal)
385 385
386 386 def set_active_scheme(self, scheme):
387 387 if scheme is not None:
388 388 self.color_table.set_active_scheme(scheme)
389 389 self.parser.color_table.set_active_scheme(scheme)
390 390
391 391 def noinfo(self, msg, oname):
392 392 """Generic message when no information is found."""
393 393 print('No %s found' % msg, end=' ')
394 394 if oname:
395 395 print('for %s' % oname)
396 396 else:
397 397 print()
398 398
399 399 def pdef(self, obj, oname=''):
400 400 """Print the call signature for any callable object.
401 401
402 402 If the object is a class, print the constructor information."""
403 403
404 404 if not callable(obj):
405 405 print('Object is not callable.')
406 406 return
407 407
408 408 header = ''
409 409
410 410 if inspect.isclass(obj):
411 411 header = self.__head('Class constructor information:\n')
412 412
413 413
414 414 output = self._getdef(obj,oname)
415 415 if output is None:
416 416 self.noinfo('definition header',oname)
417 417 else:
418 418 print(header,self.format(output), end=' ')
419 419
420 420 # In Python 3, all classes are new-style, so they all have __init__.
421 421 @skip_doctest
422 422 def pdoc(self, obj, oname='', formatter=None):
423 423 """Print the docstring for any object.
424 424
425 425 Optional:
426 426 -formatter: a function to run the docstring through for specially
427 427 formatted docstrings.
428 428
429 429 Examples
430 430 --------
431 431 In [1]: class NoInit:
432 432 ...: pass
433 433
434 434 In [2]: class NoDoc:
435 435 ...: def __init__(self):
436 436 ...: pass
437 437
438 438 In [3]: %pdoc NoDoc
439 439 No documentation found for NoDoc
440 440
441 441 In [4]: %pdoc NoInit
442 442 No documentation found for NoInit
443 443
444 444 In [5]: obj = NoInit()
445 445
446 446 In [6]: %pdoc obj
447 447 No documentation found for obj
448 448
449 449 In [5]: obj2 = NoDoc()
450 450
451 451 In [6]: %pdoc obj2
452 452 No documentation found for obj2
453 453 """
454 454
455 455 head = self.__head # For convenience
456 456 lines = []
457 457 ds = getdoc(obj)
458 458 if formatter:
459 459 ds = formatter(ds).get('plain/text', ds)
460 460 if ds:
461 461 lines.append(head("Class docstring:"))
462 462 lines.append(indent(ds))
463 463 if inspect.isclass(obj) and hasattr(obj, '__init__'):
464 464 init_ds = getdoc(obj.__init__)
465 465 if init_ds is not None:
466 466 lines.append(head("Init docstring:"))
467 467 lines.append(indent(init_ds))
468 468 elif hasattr(obj,'__call__'):
469 469 call_ds = getdoc(obj.__call__)
470 470 if call_ds:
471 471 lines.append(head("Call docstring:"))
472 472 lines.append(indent(call_ds))
473 473
474 474 if not lines:
475 475 self.noinfo('documentation',oname)
476 476 else:
477 477 page.page('\n'.join(lines))
478 478
479 479 def psource(self, obj, oname=''):
480 480 """Print the source code for an object."""
481 481
482 482 # Flush the source cache because inspect can return out-of-date source
483 483 linecache.checkcache()
484 484 try:
485 485 src = getsource(obj, oname=oname)
486 486 except Exception:
487 487 src = None
488 488
489 489 if src is None:
490 490 self.noinfo('source', oname)
491 491 else:
492 492 page.page(self.format(src))
493 493
494 494 def pfile(self, obj, oname=''):
495 495 """Show the whole file where an object was defined."""
496 496
497 497 lineno = find_source_lines(obj)
498 498 if lineno is None:
499 499 self.noinfo('file', oname)
500 500 return
501 501
502 502 ofile = find_file(obj)
503 503 # run contents of file through pager starting at line where the object
504 504 # is defined, as long as the file isn't binary and is actually on the
505 505 # filesystem.
506 506 if ofile.endswith(('.so', '.dll', '.pyd')):
507 507 print('File %r is binary, not printing.' % ofile)
508 508 elif not os.path.isfile(ofile):
509 509 print('File %r does not exist, not printing.' % ofile)
510 510 else:
511 511 # Print only text files, not extension binaries. Note that
512 512 # getsourcelines returns lineno with 1-offset and page() uses
513 513 # 0-offset, so we must adjust.
514 514 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
515 515
516 516
517 517 def _mime_format(self, text:str, formatter=None) -> dict:
518 518 """Return a mime bundle representation of the input text.
519 519
520 520 - if `formatter` is None, the returned mime bundle has
521 521 a ``text/plain`` field, with the input text.
522 522 a ``text/html`` field with a ``<pre>`` tag containing the input text.
523 523
524 524 - if ``formatter`` is not None, it must be a callable transforming the
525 525 input text into a mime bundle. Default values for ``text/plain`` and
526 526 ``text/html`` representations are the ones described above.
527 527
528 528 Note:
529 529
530 530 Formatters returning strings are supported but this behavior is deprecated.
531 531
532 532 """
533 533 defaults = {
534 'text/plain': text,
535 'text/html': '<pre>' + html.escape(text) + '</pre>'
534 "text/plain": text,
535 "text/html": f"<pre>{html.escape(text)}</pre>",
536 536 }
537 537
538 538 if formatter is None:
539 539 return defaults
540 540 else:
541 541 formatted = formatter(text)
542 542
543 543 if not isinstance(formatted, dict):
544 544 # Handle the deprecated behavior of a formatter returning
545 545 # a string instead of a mime bundle.
546 return {
547 'text/plain': formatted,
548 'text/html': '<pre>' + formatted + '</pre>'
549 }
546 return {"text/plain": formatted, "text/html": f"<pre>{formatted}</pre>"}
550 547
551 548 else:
552 549 return dict(defaults, **formatted)
553 550
554 551
555 552 def format_mime(self, bundle):
556 553 """Format a mimebundle being created by _make_info_unformatted into a real mimebundle"""
557 554 # Format text/plain mimetype
558 555 if isinstance(bundle["text/plain"], (list, tuple)):
559 556 # bundle['text/plain'] is a list of (head, formatted body) pairs
560 557 lines = []
561 558 _len = max(len(h) for h, _ in bundle["text/plain"])
562 559
563 560 for head, body in bundle["text/plain"]:
564 561 body = body.strip("\n")
565 562 delim = "\n" if "\n" in body else " "
566 563 lines.append(
567 564 f"{self.__head(head+':')}{(_len - len(head))*' '}{delim}{body}"
568 565 )
569 566
570 567 bundle["text/plain"] = "\n".join(lines)
571 568
572 569 # Format the text/html mimetype
573 570 if isinstance(bundle["text/html"], (list, tuple)):
574 571 # bundle['text/html'] is a list of (head, formatted body) pairs
575 572 bundle["text/html"] = "\n".join(
576 573 (f"<h1>{head}</h1>\n{body}" for (head, body) in bundle["text/html"])
577 574 )
578 575 return bundle
579 576
580 577 def _append_info_field(
581 578 self, bundle, title: str, key: str, info, omit_sections, formatter
582 579 ):
583 580 """Append an info value to the unformatted mimebundle being constructed by _make_info_unformatted"""
584 581 if title in omit_sections or key in omit_sections:
585 582 return
586 583 field = info[key]
587 584 if field is not None:
588 585 formatted_field = self._mime_format(field, formatter)
589 586 bundle["text/plain"].append((title, formatted_field["text/plain"]))
590 587 bundle["text/html"].append((title, formatted_field["text/html"]))
591 588
592 589 def _make_info_unformatted(self, obj, info, formatter, detail_level, omit_sections):
593 590 """Assemble the mimebundle as unformatted lists of information"""
594 591 bundle = {
595 592 "text/plain": [],
596 593 "text/html": [],
597 594 }
598 595
599 596 # A convenience function to simplify calls below
600 597 def append_field(bundle, title: str, key: str, formatter=None):
601 598 self._append_info_field(
602 599 bundle,
603 600 title=title,
604 601 key=key,
605 602 info=info,
606 603 omit_sections=omit_sections,
607 604 formatter=formatter,
608 605 )
609 606
610 607 def code_formatter(text):
611 608 return {
612 609 'text/plain': self.format(text),
613 610 'text/html': pylight(text)
614 611 }
615 612
616 613 if info["isalias"]:
617 614 append_field(bundle, "Repr", "string_form")
618 615
619 616 elif info['ismagic']:
620 617 if detail_level > 0:
621 618 append_field(bundle, "Source", "source", code_formatter)
622 619 else:
623 620 append_field(bundle, "Docstring", "docstring", formatter)
624 621 append_field(bundle, "File", "file")
625 622
626 623 elif info['isclass'] or is_simple_callable(obj):
627 624 # Functions, methods, classes
628 625 append_field(bundle, "Signature", "definition", code_formatter)
629 626 append_field(bundle, "Init signature", "init_definition", code_formatter)
630 627 append_field(bundle, "Docstring", "docstring", formatter)
631 628 if detail_level > 0 and info["source"]:
632 629 append_field(bundle, "Source", "source", code_formatter)
633 630 else:
634 631 append_field(bundle, "Init docstring", "init_docstring", formatter)
635 632
636 633 append_field(bundle, "File", "file")
637 634 append_field(bundle, "Type", "type_name")
638 635 append_field(bundle, "Subclasses", "subclasses")
639 636
640 637 else:
641 638 # General Python objects
642 639 append_field(bundle, "Signature", "definition", code_formatter)
643 640 append_field(bundle, "Call signature", "call_def", code_formatter)
644 641 append_field(bundle, "Type", "type_name")
645 642 append_field(bundle, "String form", "string_form")
646 643
647 644 # Namespace
648 645 if info["namespace"] != "Interactive":
649 646 append_field(bundle, "Namespace", "namespace")
650 647
651 648 append_field(bundle, "Length", "length")
652 649 append_field(bundle, "File", "file")
653 650
654 651 # Source or docstring, depending on detail level and whether
655 652 # source found.
656 653 if detail_level > 0 and info["source"]:
657 654 append_field(bundle, "Source", "source", code_formatter)
658 655 else:
659 656 append_field(bundle, "Docstring", "docstring", formatter)
660 657
661 658 append_field(bundle, "Class docstring", "class_docstring", formatter)
662 659 append_field(bundle, "Init docstring", "init_docstring", formatter)
663 660 append_field(bundle, "Call docstring", "call_docstring", formatter)
664 661 return bundle
665 662
666 663
667 664 def _get_info(
668 665 self, obj, oname="", formatter=None, info=None, detail_level=0, omit_sections=()
669 666 ):
670 667 """Retrieve an info dict and format it.
671 668
672 669 Parameters
673 670 ----------
674 671 obj : any
675 672 Object to inspect and return info from
676 673 oname : str (default: ''):
677 674 Name of the variable pointing to `obj`.
678 675 formatter : callable
679 676 info
680 677 already computed information
681 678 detail_level : integer
682 679 Granularity of detail level, if set to 1, give more information.
683 680 omit_sections : container[str]
684 681 Titles or keys to omit from output (can be set, tuple, etc., anything supporting `in`)
685 682 """
686 683
687 684 info = self.info(obj, oname=oname, info=info, detail_level=detail_level)
688 685 bundle = self._make_info_unformatted(
689 686 obj, info, formatter, detail_level=detail_level, omit_sections=omit_sections
690 687 )
691 688 return self.format_mime(bundle)
692 689
693 690 def pinfo(
694 691 self,
695 692 obj,
696 693 oname="",
697 694 formatter=None,
698 695 info=None,
699 696 detail_level=0,
700 697 enable_html_pager=True,
701 698 omit_sections=(),
702 699 ):
703 700 """Show detailed information about an object.
704 701
705 702 Optional arguments:
706 703
707 704 - oname: name of the variable pointing to the object.
708 705
709 706 - formatter: callable (optional)
710 707 A special formatter for docstrings.
711 708
712 709 The formatter is a callable that takes a string as an input
713 710 and returns either a formatted string or a mime type bundle
714 711 in the form of a dictionary.
715 712
716 713 Although the support of custom formatter returning a string
717 714 instead of a mime type bundle is deprecated.
718 715
719 716 - info: a structure with some information fields which may have been
720 717 precomputed already.
721 718
722 719 - detail_level: if set to 1, more information is given.
723 720
724 721 - omit_sections: set of section keys and titles to omit
725 722 """
726 723 info = self._get_info(
727 724 obj, oname, formatter, info, detail_level, omit_sections=omit_sections
728 725 )
729 726 if not enable_html_pager:
730 727 del info['text/html']
731 728 page.page(info)
732 729
733 730 def _info(self, obj, oname="", info=None, detail_level=0):
734 731 """
735 732 Inspector.info() was likely improperly marked as deprecated
736 733 while only a parameter was deprecated. We "un-deprecate" it.
737 734 """
738 735
739 736 warnings.warn(
740 737 "The `Inspector.info()` method has been un-deprecated as of 8.0 "
741 738 "and the `formatter=` keyword removed. `Inspector._info` is now "
742 739 "an alias, and you can just call `.info()` directly.",
743 740 DeprecationWarning,
744 741 stacklevel=2,
745 742 )
746 743 return self.info(obj, oname=oname, info=info, detail_level=detail_level)
747 744
748 745 def info(self, obj, oname="", info=None, detail_level=0) -> dict:
749 746 """Compute a dict with detailed information about an object.
750 747
751 748 Parameters
752 749 ----------
753 750 obj : any
754 751 An object to find information about
755 752 oname : str (default: '')
756 753 Name of the variable pointing to `obj`.
757 754 info : (default: None)
758 755 A struct (dict like with attr access) with some information fields
759 756 which may have been precomputed already.
760 757 detail_level : int (default:0)
761 758 If set to 1, more information is given.
762 759
763 760 Returns
764 761 -------
765 762 An object info dict with known fields from `info_fields`. Keys are
766 763 strings, values are string or None.
767 764 """
768 765
769 766 if info is None:
770 767 ismagic = False
771 768 isalias = False
772 769 ospace = ''
773 770 else:
774 771 ismagic = info.ismagic
775 772 isalias = info.isalias
776 773 ospace = info.namespace
777 774
778 775 # Get docstring, special-casing aliases:
779 776 if isalias:
780 777 if not callable(obj):
781 778 try:
782 779 ds = "Alias to the system command:\n %s" % obj[1]
783 780 except:
784 781 ds = "Alias: " + str(obj)
785 782 else:
786 783 ds = "Alias to " + str(obj)
787 784 if obj.__doc__:
788 785 ds += "\nDocstring:\n" + obj.__doc__
789 786 else:
790 787 ds = getdoc(obj)
791 788 if ds is None:
792 789 ds = '<no docstring>'
793 790
794 791 # store output in a dict, we initialize it here and fill it as we go
795 792 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None)
796 793
797 794 string_max = 200 # max size of strings to show (snipped if longer)
798 795 shalf = int((string_max - 5) / 2)
799 796
800 797 if ismagic:
801 798 out['type_name'] = 'Magic function'
802 799 elif isalias:
803 800 out['type_name'] = 'System alias'
804 801 else:
805 802 out['type_name'] = type(obj).__name__
806 803
807 804 try:
808 805 bclass = obj.__class__
809 806 out['base_class'] = str(bclass)
810 807 except:
811 808 pass
812 809
813 810 # String form, but snip if too long in ? form (full in ??)
814 811 if detail_level >= self.str_detail_level:
815 812 try:
816 813 ostr = str(obj)
817 814 str_head = 'string_form'
818 815 if not detail_level and len(ostr)>string_max:
819 816 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
820 817 ostr = ("\n" + " " * len(str_head.expandtabs())).\
821 818 join(q.strip() for q in ostr.split("\n"))
822 819 out[str_head] = ostr
823 820 except:
824 821 pass
825 822
826 823 if ospace:
827 824 out['namespace'] = ospace
828 825
829 826 # Length (for strings and lists)
830 827 try:
831 828 out['length'] = str(len(obj))
832 829 except Exception:
833 830 pass
834 831
835 832 # Filename where object was defined
836 833 binary_file = False
837 834 fname = find_file(obj)
838 835 if fname is None:
839 836 # if anything goes wrong, we don't want to show source, so it's as
840 837 # if the file was binary
841 838 binary_file = True
842 839 else:
843 840 if fname.endswith(('.so', '.dll', '.pyd')):
844 841 binary_file = True
845 842 elif fname.endswith('<string>'):
846 843 fname = 'Dynamically generated function. No source code available.'
847 844 out['file'] = compress_user(fname)
848 845
849 846 # Original source code for a callable, class or property.
850 847 if detail_level:
851 848 # Flush the source cache because inspect can return out-of-date
852 849 # source
853 850 linecache.checkcache()
854 851 try:
855 852 if isinstance(obj, property) or not binary_file:
856 853 src = getsource(obj, oname)
857 854 if src is not None:
858 855 src = src.rstrip()
859 856 out['source'] = src
860 857
861 858 except Exception:
862 859 pass
863 860
864 861 # Add docstring only if no source is to be shown (avoid repetitions).
865 862 if ds and not self._source_contains_docstring(out.get('source'), ds):
866 863 out['docstring'] = ds
867 864
868 865 # Constructor docstring for classes
869 866 if inspect.isclass(obj):
870 867 out['isclass'] = True
871 868
872 869 # get the init signature:
873 870 try:
874 871 init_def = self._getdef(obj, oname)
875 872 except AttributeError:
876 873 init_def = None
877 874
878 875 # get the __init__ docstring
879 876 try:
880 877 obj_init = obj.__init__
881 878 except AttributeError:
882 879 init_ds = None
883 880 else:
884 881 if init_def is None:
885 882 # Get signature from init if top-level sig failed.
886 883 # Can happen for built-in types (list, etc.).
887 884 try:
888 885 init_def = self._getdef(obj_init, oname)
889 886 except AttributeError:
890 887 pass
891 888 init_ds = getdoc(obj_init)
892 889 # Skip Python's auto-generated docstrings
893 890 if init_ds == _object_init_docstring:
894 891 init_ds = None
895 892
896 893 if init_def:
897 894 out['init_definition'] = init_def
898 895
899 896 if init_ds:
900 897 out['init_docstring'] = init_ds
901 898
902 899 names = [sub.__name__ for sub in type.__subclasses__(obj)]
903 900 if len(names) < 10:
904 901 all_names = ', '.join(names)
905 902 else:
906 903 all_names = ', '.join(names[:10]+['...'])
907 904 out['subclasses'] = all_names
908 905 # and class docstring for instances:
909 906 else:
910 907 # reconstruct the function definition and print it:
911 908 defln = self._getdef(obj, oname)
912 909 if defln:
913 910 out['definition'] = defln
914 911
915 912 # First, check whether the instance docstring is identical to the
916 913 # class one, and print it separately if they don't coincide. In
917 914 # most cases they will, but it's nice to print all the info for
918 915 # objects which use instance-customized docstrings.
919 916 if ds:
920 917 try:
921 918 cls = getattr(obj,'__class__')
922 919 except:
923 920 class_ds = None
924 921 else:
925 922 class_ds = getdoc(cls)
926 923 # Skip Python's auto-generated docstrings
927 924 if class_ds in _builtin_type_docstrings:
928 925 class_ds = None
929 926 if class_ds and ds != class_ds:
930 927 out['class_docstring'] = class_ds
931 928
932 929 # Next, try to show constructor docstrings
933 930 try:
934 931 init_ds = getdoc(obj.__init__)
935 932 # Skip Python's auto-generated docstrings
936 933 if init_ds == _object_init_docstring:
937 934 init_ds = None
938 935 except AttributeError:
939 936 init_ds = None
940 937 if init_ds:
941 938 out['init_docstring'] = init_ds
942 939
943 940 # Call form docstring for callable instances
944 941 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
945 942 call_def = self._getdef(obj.__call__, oname)
946 943 if call_def and (call_def != out.get('definition')):
947 944 # it may never be the case that call def and definition differ,
948 945 # but don't include the same signature twice
949 946 out['call_def'] = call_def
950 947 call_ds = getdoc(obj.__call__)
951 948 # Skip Python's auto-generated docstrings
952 949 if call_ds == _func_call_docstring:
953 950 call_ds = None
954 951 if call_ds:
955 952 out['call_docstring'] = call_ds
956 953
957 954 return object_info(**out)
958 955
959 956 @staticmethod
960 957 def _source_contains_docstring(src, doc):
961 958 """
962 959 Check whether the source *src* contains the docstring *doc*.
963 960
964 961 This is is helper function to skip displaying the docstring if the
965 962 source already contains it, avoiding repetition of information.
966 963 """
967 964 try:
968 965 def_node, = ast.parse(dedent(src)).body
969 966 return ast.get_docstring(def_node) == doc
970 967 except Exception:
971 968 # The source can become invalid or even non-existent (because it
972 969 # is re-fetched from the source file) so the above code fail in
973 970 # arbitrary ways.
974 971 return False
975 972
976 973 def psearch(self,pattern,ns_table,ns_search=[],
977 974 ignore_case=False,show_all=False, *, list_types=False):
978 975 """Search namespaces with wildcards for objects.
979 976
980 977 Arguments:
981 978
982 979 - pattern: string containing shell-like wildcards to use in namespace
983 980 searches and optionally a type specification to narrow the search to
984 981 objects of that type.
985 982
986 983 - ns_table: dict of name->namespaces for search.
987 984
988 985 Optional arguments:
989 986
990 987 - ns_search: list of namespace names to include in search.
991 988
992 989 - ignore_case(False): make the search case-insensitive.
993 990
994 991 - show_all(False): show all names, including those starting with
995 992 underscores.
996 993
997 994 - list_types(False): list all available object types for object matching.
998 995 """
999 996 #print 'ps pattern:<%r>' % pattern # dbg
1000 997
1001 998 # defaults
1002 999 type_pattern = 'all'
1003 1000 filter = ''
1004 1001
1005 1002 # list all object types
1006 1003 if list_types:
1007 1004 page.page('\n'.join(sorted(typestr2type)))
1008 1005 return
1009 1006
1010 1007 cmds = pattern.split()
1011 1008 len_cmds = len(cmds)
1012 1009 if len_cmds == 1:
1013 1010 # Only filter pattern given
1014 1011 filter = cmds[0]
1015 1012 elif len_cmds == 2:
1016 1013 # Both filter and type specified
1017 1014 filter,type_pattern = cmds
1018 1015 else:
1019 1016 raise ValueError('invalid argument string for psearch: <%s>' %
1020 1017 pattern)
1021 1018
1022 1019 # filter search namespaces
1023 1020 for name in ns_search:
1024 1021 if name not in ns_table:
1025 1022 raise ValueError('invalid namespace <%s>. Valid names: %s' %
1026 1023 (name,ns_table.keys()))
1027 1024
1028 1025 #print 'type_pattern:',type_pattern # dbg
1029 1026 search_result, namespaces_seen = set(), set()
1030 1027 for ns_name in ns_search:
1031 1028 ns = ns_table[ns_name]
1032 1029 # Normally, locals and globals are the same, so we just check one.
1033 1030 if id(ns) in namespaces_seen:
1034 1031 continue
1035 1032 namespaces_seen.add(id(ns))
1036 1033 tmp_res = list_namespace(ns, type_pattern, filter,
1037 1034 ignore_case=ignore_case, show_all=show_all)
1038 1035 search_result.update(tmp_res)
1039 1036
1040 1037 page.page('\n'.join(sorted(search_result)))
1041 1038
1042 1039
1043 1040 def _render_signature(obj_signature, obj_name) -> str:
1044 1041 """
1045 1042 This was mostly taken from inspect.Signature.__str__.
1046 1043 Look there for the comments.
1047 1044 The only change is to add linebreaks when this gets too long.
1048 1045 """
1049 1046 result = []
1050 1047 pos_only = False
1051 1048 kw_only = True
1052 1049 for param in obj_signature.parameters.values():
1053 1050 if param.kind == inspect._POSITIONAL_ONLY:
1054 1051 pos_only = True
1055 1052 elif pos_only:
1056 1053 result.append('/')
1057 1054 pos_only = False
1058 1055
1059 1056 if param.kind == inspect._VAR_POSITIONAL:
1060 1057 kw_only = False
1061 1058 elif param.kind == inspect._KEYWORD_ONLY and kw_only:
1062 1059 result.append('*')
1063 1060 kw_only = False
1064 1061
1065 1062 result.append(str(param))
1066 1063
1067 1064 if pos_only:
1068 1065 result.append('/')
1069 1066
1070 1067 # add up name, parameters, braces (2), and commas
1071 1068 if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
1072 1069 # This doesn’t fit behind “Signature: ” in an inspect window.
1073 1070 rendered = '{}(\n{})'.format(obj_name, ''.join(
1074 1071 ' {},\n'.format(r) for r in result)
1075 1072 )
1076 1073 else:
1077 1074 rendered = '{}({})'.format(obj_name, ', '.join(result))
1078 1075
1079 1076 if obj_signature.return_annotation is not inspect._empty:
1080 1077 anno = inspect.formatannotation(obj_signature.return_annotation)
1081 1078 rendered += ' -> {}'.format(anno)
1082 1079
1083 1080 return rendered
General Comments 0
You need to be logged in to leave comments. Login now