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