Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/oinspect.py: 8%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

540 statements  

1# -*- coding: utf-8 -*- 

2"""Tools for inspecting Python objects. 

3 

4Uses syntax highlighting for presenting the various information elements. 

5 

6Similar in spirit to the inspect module, but all calls take a name argument to 

7reference the name under which an object is being read. 

8""" 

9 

10# Copyright (c) IPython Development Team. 

11# Distributed under the terms of the Modified BSD License. 

12 

13__all__ = ['Inspector','InspectColors'] 

14 

15# stdlib modules 

16from dataclasses import dataclass 

17from inspect import signature 

18from textwrap import dedent 

19import ast 

20import html 

21import inspect 

22import io as stdlib_io 

23import linecache 

24import os 

25import sys 

26import types 

27import warnings 

28 

29from typing import Any, Optional, Dict, Union, List, Tuple 

30 

31if sys.version_info <= (3, 10): 

32 from typing_extensions import TypeAlias 

33else: 

34 from typing import TypeAlias 

35 

36# IPython's own 

37from IPython.core import page 

38from IPython.lib.pretty import pretty 

39from IPython.testing.skipdoctest import skip_doctest 

40from IPython.utils import PyColorize 

41from IPython.utils import openpy 

42from IPython.utils.dir2 import safe_hasattr 

43from IPython.utils.path import compress_user 

44from IPython.utils.text import indent 

45from IPython.utils.wildcard import list_namespace 

46from IPython.utils.wildcard import typestr2type 

47from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable 

48from IPython.utils.py3compat import cast_unicode 

49from IPython.utils.colorable import Colorable 

50from IPython.utils.decorators import undoc 

51 

52from pygments import highlight 

53from pygments.lexers import PythonLexer 

54from pygments.formatters import HtmlFormatter 

55 

56HOOK_NAME = "__custom_documentations__" 

57 

58 

59UnformattedBundle: TypeAlias = Dict[str, List[Tuple[str, str]]] # List of (title, body) 

60Bundle: TypeAlias = Dict[str, str] 

61 

62 

63@dataclass 

64class OInfo: 

65 ismagic: bool 

66 isalias: bool 

67 found: bool 

68 namespace: Optional[str] 

69 parent: Any 

70 obj: Any 

71 

72 def get(self, field): 

73 """Get a field from the object for backward compatibility with before 8.12 

74 

75 see https://github.com/h5py/h5py/issues/2253 

76 """ 

77 # We need to deprecate this at some point, but the warning will show in completion. 

78 # Let's comment this for now and uncomment end of 2023 ish 

79 # warnings.warn( 

80 # f"OInfo dataclass with fields access since IPython 8.12 please use OInfo.{field} instead." 

81 # "OInfo used to be a dict but a dataclass provide static fields verification with mypy." 

82 # "This warning and backward compatibility `get()` method were added in 8.13.", 

83 # DeprecationWarning, 

84 # stacklevel=2, 

85 # ) 

86 return getattr(self, field) 

87 

88 

89def pylight(code): 

90 return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True)) 

91 

92# builtin docstrings to ignore 

93_func_call_docstring = types.FunctionType.__call__.__doc__ 

94_object_init_docstring = object.__init__.__doc__ 

95_builtin_type_docstrings = { 

96 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType, 

97 types.FunctionType, property) 

98} 

99 

100_builtin_func_type = type(all) 

101_builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions 

102#**************************************************************************** 

103# Builtin color schemes 

104 

105Colors = TermColors # just a shorthand 

106 

107InspectColors = PyColorize.ANSICodeColors 

108 

109#**************************************************************************** 

110# Auxiliary functions and objects 

111 

112# See the messaging spec for the definition of all these fields. This list 

113# effectively defines the order of display 

114info_fields = ['type_name', 'base_class', 'string_form', 'namespace', 

115 'length', 'file', 'definition', 'docstring', 'source', 

116 'init_definition', 'class_docstring', 'init_docstring', 

117 'call_def', 'call_docstring', 

118 # These won't be printed but will be used to determine how to 

119 # format the object 

120 'ismagic', 'isalias', 'isclass', 'found', 'name' 

121 ] 

122 

123 

124def object_info(**kw): 

125 """Make an object info dict with all fields present.""" 

126 infodict = {k:None for k in info_fields} 

127 infodict.update(kw) 

128 return infodict 

129 

130 

131def get_encoding(obj): 

132 """Get encoding for python source file defining obj 

133 

134 Returns None if obj is not defined in a sourcefile. 

135 """ 

136 ofile = find_file(obj) 

137 # run contents of file through pager starting at line where the object 

138 # is defined, as long as the file isn't binary and is actually on the 

139 # filesystem. 

140 if ofile is None: 

141 return None 

142 elif ofile.endswith(('.so', '.dll', '.pyd')): 

143 return None 

144 elif not os.path.isfile(ofile): 

145 return None 

146 else: 

147 # Print only text files, not extension binaries. Note that 

148 # getsourcelines returns lineno with 1-offset and page() uses 

149 # 0-offset, so we must adjust. 

150 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2 

151 encoding, lines = openpy.detect_encoding(buffer.readline) 

152 return encoding 

153 

154def getdoc(obj) -> Union[str,None]: 

155 """Stable wrapper around inspect.getdoc. 

156 

157 This can't crash because of attribute problems. 

158 

159 It also attempts to call a getdoc() method on the given object. This 

160 allows objects which provide their docstrings via non-standard mechanisms 

161 (like Pyro proxies) to still be inspected by ipython's ? system. 

162 """ 

163 # Allow objects to offer customized documentation via a getdoc method: 

164 try: 

165 ds = obj.getdoc() 

166 except Exception: 

167 pass 

168 else: 

169 if isinstance(ds, str): 

170 return inspect.cleandoc(ds) 

171 docstr = inspect.getdoc(obj) 

172 return docstr 

173 

174 

175def getsource(obj, oname='') -> Union[str,None]: 

176 """Wrapper around inspect.getsource. 

177 

178 This can be modified by other projects to provide customized source 

179 extraction. 

180 

181 Parameters 

182 ---------- 

183 obj : object 

184 an object whose source code we will attempt to extract 

185 oname : str 

186 (optional) a name under which the object is known 

187 

188 Returns 

189 ------- 

190 src : unicode or None 

191 

192 """ 

193 

194 if isinstance(obj, property): 

195 sources = [] 

196 for attrname in ['fget', 'fset', 'fdel']: 

197 fn = getattr(obj, attrname) 

198 if fn is not None: 

199 encoding = get_encoding(fn) 

200 oname_prefix = ('%s.' % oname) if oname else '' 

201 sources.append(''.join(('# ', oname_prefix, attrname))) 

202 if inspect.isfunction(fn): 

203 _src = getsource(fn) 

204 if _src: 

205 # assert _src is not None, "please mypy" 

206 sources.append(dedent(_src)) 

207 else: 

208 # Default str/repr only prints function name, 

209 # pretty.pretty prints module name too. 

210 sources.append( 

211 '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn)) 

212 ) 

213 if sources: 

214 return '\n'.join(sources) 

215 else: 

216 return None 

217 

218 else: 

219 # Get source for non-property objects. 

220 

221 obj = _get_wrapped(obj) 

222 

223 try: 

224 src = inspect.getsource(obj) 

225 except TypeError: 

226 # The object itself provided no meaningful source, try looking for 

227 # its class definition instead. 

228 try: 

229 src = inspect.getsource(obj.__class__) 

230 except (OSError, TypeError): 

231 return None 

232 except OSError: 

233 return None 

234 

235 return src 

236 

237 

238def is_simple_callable(obj): 

239 """True if obj is a function ()""" 

240 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \ 

241 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type)) 

242 

243@undoc 

244def getargspec(obj): 

245 """Wrapper around :func:`inspect.getfullargspec` 

246 

247 In addition to functions and methods, this can also handle objects with a 

248 ``__call__`` attribute. 

249 

250 DEPRECATED: Deprecated since 7.10. Do not use, will be removed. 

251 """ 

252 

253 warnings.warn('`getargspec` function is deprecated as of IPython 7.10' 

254 'and will be removed in future versions.', DeprecationWarning, stacklevel=2) 

255 

256 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj): 

257 obj = obj.__call__ 

258 

259 return inspect.getfullargspec(obj) 

260 

261@undoc 

262def format_argspec(argspec): 

263 """Format argspect, convenience wrapper around inspect's. 

264 

265 This takes a dict instead of ordered arguments and calls 

266 inspect.format_argspec with the arguments in the necessary order. 

267 

268 DEPRECATED (since 7.10): Do not use; will be removed in future versions. 

269 """ 

270 

271 warnings.warn('`format_argspec` function is deprecated as of IPython 7.10' 

272 'and will be removed in future versions.', DeprecationWarning, stacklevel=2) 

273 

274 

275 return inspect.formatargspec(argspec['args'], argspec['varargs'], 

276 argspec['varkw'], argspec['defaults']) 

277 

278@undoc 

279def call_tip(oinfo, format_call=True): 

280 """DEPRECATED since 6.0. Extract call tip data from an oinfo dict.""" 

281 warnings.warn( 

282 "`call_tip` function is deprecated as of IPython 6.0" 

283 "and will be removed in future versions.", 

284 DeprecationWarning, 

285 stacklevel=2, 

286 ) 

287 # Get call definition 

288 argspec = oinfo.get('argspec') 

289 if argspec is None: 

290 call_line = None 

291 else: 

292 # Callable objects will have 'self' as their first argument, prune 

293 # it out if it's there for clarity (since users do *not* pass an 

294 # extra first argument explicitly). 

295 try: 

296 has_self = argspec['args'][0] == 'self' 

297 except (KeyError, IndexError): 

298 pass 

299 else: 

300 if has_self: 

301 argspec['args'] = argspec['args'][1:] 

302 

303 call_line = oinfo['name']+format_argspec(argspec) 

304 

305 # Now get docstring. 

306 # The priority is: call docstring, constructor docstring, main one. 

307 doc = oinfo.get('call_docstring') 

308 if doc is None: 

309 doc = oinfo.get('init_docstring') 

310 if doc is None: 

311 doc = oinfo.get('docstring','') 

312 

313 return call_line, doc 

314 

315 

316def _get_wrapped(obj): 

317 """Get the original object if wrapped in one or more @decorators 

318 

319 Some objects automatically construct similar objects on any unrecognised 

320 attribute access (e.g. unittest.mock.call). To protect against infinite loops, 

321 this will arbitrarily cut off after 100 levels of obj.__wrapped__ 

322 attribute access. --TK, Jan 2016 

323 """ 

324 orig_obj = obj 

325 i = 0 

326 while safe_hasattr(obj, '__wrapped__'): 

327 obj = obj.__wrapped__ 

328 i += 1 

329 if i > 100: 

330 # __wrapped__ is probably a lie, so return the thing we started with 

331 return orig_obj 

332 return obj 

333 

334def find_file(obj) -> str: 

335 """Find the absolute path to the file where an object was defined. 

336 

337 This is essentially a robust wrapper around `inspect.getabsfile`. 

338 

339 Returns None if no file can be found. 

340 

341 Parameters 

342 ---------- 

343 obj : any Python object 

344 

345 Returns 

346 ------- 

347 fname : str 

348 The absolute path to the file where the object was defined. 

349 """ 

350 obj = _get_wrapped(obj) 

351 

352 fname = None 

353 try: 

354 fname = inspect.getabsfile(obj) 

355 except TypeError: 

356 # For an instance, the file that matters is where its class was 

357 # declared. 

358 try: 

359 fname = inspect.getabsfile(obj.__class__) 

360 except (OSError, TypeError): 

361 # Can happen for builtins 

362 pass 

363 except OSError: 

364 pass 

365 

366 return cast_unicode(fname) 

367 

368 

369def find_source_lines(obj): 

370 """Find the line number in a file where an object was defined. 

371 

372 This is essentially a robust wrapper around `inspect.getsourcelines`. 

373 

374 Returns None if no file can be found. 

375 

376 Parameters 

377 ---------- 

378 obj : any Python object 

379 

380 Returns 

381 ------- 

382 lineno : int 

383 The line number where the object definition starts. 

384 """ 

385 obj = _get_wrapped(obj) 

386 

387 try: 

388 lineno = inspect.getsourcelines(obj)[1] 

389 except TypeError: 

390 # For instances, try the class object like getsource() does 

391 try: 

392 lineno = inspect.getsourcelines(obj.__class__)[1] 

393 except (OSError, TypeError): 

394 return None 

395 except OSError: 

396 return None 

397 

398 return lineno 

399 

400class Inspector(Colorable): 

401 

402 def __init__(self, color_table=InspectColors, 

403 code_color_table=PyColorize.ANSICodeColors, 

404 scheme=None, 

405 str_detail_level=0, 

406 parent=None, config=None): 

407 super(Inspector, self).__init__(parent=parent, config=config) 

408 self.color_table = color_table 

409 self.parser = PyColorize.Parser(out='str', parent=self, style=scheme) 

410 self.format = self.parser.format 

411 self.str_detail_level = str_detail_level 

412 self.set_active_scheme(scheme) 

413 

414 def _getdef(self,obj,oname='') -> Union[str,None]: 

415 """Return the call signature for any callable object. 

416 

417 If any exception is generated, None is returned instead and the 

418 exception is suppressed.""" 

419 if not callable(obj): 

420 return None 

421 try: 

422 return _render_signature(signature(obj), oname) 

423 except: 

424 return None 

425 

426 def __head(self,h) -> str: 

427 """Return a header string with proper colors.""" 

428 return '%s%s%s' % (self.color_table.active_colors.header,h, 

429 self.color_table.active_colors.normal) 

430 

431 def set_active_scheme(self, scheme): 

432 if scheme is not None: 

433 self.color_table.set_active_scheme(scheme) 

434 self.parser.color_table.set_active_scheme(scheme) 

435 

436 def noinfo(self, msg, oname): 

437 """Generic message when no information is found.""" 

438 print('No %s found' % msg, end=' ') 

439 if oname: 

440 print('for %s' % oname) 

441 else: 

442 print() 

443 

444 def pdef(self, obj, oname=''): 

445 """Print the call signature for any callable object. 

446 

447 If the object is a class, print the constructor information.""" 

448 

449 if not callable(obj): 

450 print('Object is not callable.') 

451 return 

452 

453 header = '' 

454 

455 if inspect.isclass(obj): 

456 header = self.__head('Class constructor information:\n') 

457 

458 

459 output = self._getdef(obj,oname) 

460 if output is None: 

461 self.noinfo('definition header',oname) 

462 else: 

463 print(header,self.format(output), end=' ') 

464 

465 # In Python 3, all classes are new-style, so they all have __init__. 

466 @skip_doctest 

467 def pdoc(self, obj, oname='', formatter=None): 

468 """Print the docstring for any object. 

469 

470 Optional: 

471 -formatter: a function to run the docstring through for specially 

472 formatted docstrings. 

473 

474 Examples 

475 -------- 

476 In [1]: class NoInit: 

477 ...: pass 

478 

479 In [2]: class NoDoc: 

480 ...: def __init__(self): 

481 ...: pass 

482 

483 In [3]: %pdoc NoDoc 

484 No documentation found for NoDoc 

485 

486 In [4]: %pdoc NoInit 

487 No documentation found for NoInit 

488 

489 In [5]: obj = NoInit() 

490 

491 In [6]: %pdoc obj 

492 No documentation found for obj 

493 

494 In [5]: obj2 = NoDoc() 

495 

496 In [6]: %pdoc obj2 

497 No documentation found for obj2 

498 """ 

499 

500 head = self.__head # For convenience 

501 lines = [] 

502 ds = getdoc(obj) 

503 if formatter: 

504 ds = formatter(ds).get('plain/text', ds) 

505 if ds: 

506 lines.append(head("Class docstring:")) 

507 lines.append(indent(ds)) 

508 if inspect.isclass(obj) and hasattr(obj, '__init__'): 

509 init_ds = getdoc(obj.__init__) 

510 if init_ds is not None: 

511 lines.append(head("Init docstring:")) 

512 lines.append(indent(init_ds)) 

513 elif hasattr(obj,'__call__'): 

514 call_ds = getdoc(obj.__call__) 

515 if call_ds: 

516 lines.append(head("Call docstring:")) 

517 lines.append(indent(call_ds)) 

518 

519 if not lines: 

520 self.noinfo('documentation',oname) 

521 else: 

522 page.page('\n'.join(lines)) 

523 

524 def psource(self, obj, oname=''): 

525 """Print the source code for an object.""" 

526 

527 # Flush the source cache because inspect can return out-of-date source 

528 linecache.checkcache() 

529 try: 

530 src = getsource(obj, oname=oname) 

531 except Exception: 

532 src = None 

533 

534 if src is None: 

535 self.noinfo('source', oname) 

536 else: 

537 page.page(self.format(src)) 

538 

539 def pfile(self, obj, oname=''): 

540 """Show the whole file where an object was defined.""" 

541 

542 lineno = find_source_lines(obj) 

543 if lineno is None: 

544 self.noinfo('file', oname) 

545 return 

546 

547 ofile = find_file(obj) 

548 # run contents of file through pager starting at line where the object 

549 # is defined, as long as the file isn't binary and is actually on the 

550 # filesystem. 

551 if ofile.endswith(('.so', '.dll', '.pyd')): 

552 print('File %r is binary, not printing.' % ofile) 

553 elif not os.path.isfile(ofile): 

554 print('File %r does not exist, not printing.' % ofile) 

555 else: 

556 # Print only text files, not extension binaries. Note that 

557 # getsourcelines returns lineno with 1-offset and page() uses 

558 # 0-offset, so we must adjust. 

559 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1) 

560 

561 

562 def _mime_format(self, text:str, formatter=None) -> dict: 

563 """Return a mime bundle representation of the input text. 

564 

565 - if `formatter` is None, the returned mime bundle has 

566 a ``text/plain`` field, with the input text. 

567 a ``text/html`` field with a ``<pre>`` tag containing the input text. 

568 

569 - if ``formatter`` is not None, it must be a callable transforming the 

570 input text into a mime bundle. Default values for ``text/plain`` and 

571 ``text/html`` representations are the ones described above. 

572 

573 Note: 

574 

575 Formatters returning strings are supported but this behavior is deprecated. 

576 

577 """ 

578 defaults = { 

579 "text/plain": text, 

580 "text/html": f"<pre>{html.escape(text)}</pre>", 

581 } 

582 

583 if formatter is None: 

584 return defaults 

585 else: 

586 formatted = formatter(text) 

587 

588 if not isinstance(formatted, dict): 

589 # Handle the deprecated behavior of a formatter returning 

590 # a string instead of a mime bundle. 

591 return {"text/plain": formatted, "text/html": f"<pre>{formatted}</pre>"} 

592 

593 else: 

594 return dict(defaults, **formatted) 

595 

596 def format_mime(self, bundle: UnformattedBundle) -> Bundle: 

597 """Format a mimebundle being created by _make_info_unformatted into a real mimebundle""" 

598 # Format text/plain mimetype 

599 assert isinstance(bundle["text/plain"], list) 

600 for item in bundle["text/plain"]: 

601 assert isinstance(item, tuple) 

602 

603 new_b: Bundle = {} 

604 lines = [] 

605 _len = max(len(h) for h, _ in bundle["text/plain"]) 

606 

607 for head, body in bundle["text/plain"]: 

608 body = body.strip("\n") 

609 delim = "\n" if "\n" in body else " " 

610 lines.append( 

611 f"{self.__head(head+':')}{(_len - len(head))*' '}{delim}{body}" 

612 ) 

613 

614 new_b["text/plain"] = "\n".join(lines) 

615 

616 if "text/html" in bundle: 

617 assert isinstance(bundle["text/html"], list) 

618 for item in bundle["text/html"]: 

619 assert isinstance(item, tuple) 

620 # Format the text/html mimetype 

621 if isinstance(bundle["text/html"], (list, tuple)): 

622 # bundle['text/html'] is a list of (head, formatted body) pairs 

623 new_b["text/html"] = "\n".join( 

624 (f"<h1>{head}</h1>\n{body}" for (head, body) in bundle["text/html"]) 

625 ) 

626 

627 for k in bundle.keys(): 

628 if k in ("text/html", "text/plain"): 

629 continue 

630 else: 

631 new_b = bundle[k] # type:ignore 

632 return new_b 

633 

634 def _append_info_field( 

635 self, 

636 bundle: UnformattedBundle, 

637 title: str, 

638 key: str, 

639 info, 

640 omit_sections, 

641 formatter, 

642 ): 

643 """Append an info value to the unformatted mimebundle being constructed by _make_info_unformatted""" 

644 if title in omit_sections or key in omit_sections: 

645 return 

646 field = info[key] 

647 if field is not None: 

648 formatted_field = self._mime_format(field, formatter) 

649 bundle["text/plain"].append((title, formatted_field["text/plain"])) 

650 bundle["text/html"].append((title, formatted_field["text/html"])) 

651 

652 def _make_info_unformatted( 

653 self, obj, info, formatter, detail_level, omit_sections 

654 ) -> UnformattedBundle: 

655 """Assemble the mimebundle as unformatted lists of information""" 

656 bundle: UnformattedBundle = { 

657 "text/plain": [], 

658 "text/html": [], 

659 } 

660 

661 # A convenience function to simplify calls below 

662 def append_field( 

663 bundle: UnformattedBundle, title: str, key: str, formatter=None 

664 ): 

665 self._append_info_field( 

666 bundle, 

667 title=title, 

668 key=key, 

669 info=info, 

670 omit_sections=omit_sections, 

671 formatter=formatter, 

672 ) 

673 

674 def code_formatter(text) -> Bundle: 

675 return { 

676 'text/plain': self.format(text), 

677 'text/html': pylight(text) 

678 } 

679 

680 if info["isalias"]: 

681 append_field(bundle, "Repr", "string_form") 

682 

683 elif info['ismagic']: 

684 if detail_level > 0: 

685 append_field(bundle, "Source", "source", code_formatter) 

686 else: 

687 append_field(bundle, "Docstring", "docstring", formatter) 

688 append_field(bundle, "File", "file") 

689 

690 elif info['isclass'] or is_simple_callable(obj): 

691 # Functions, methods, classes 

692 append_field(bundle, "Signature", "definition", code_formatter) 

693 append_field(bundle, "Init signature", "init_definition", code_formatter) 

694 append_field(bundle, "Docstring", "docstring", formatter) 

695 if detail_level > 0 and info["source"]: 

696 append_field(bundle, "Source", "source", code_formatter) 

697 else: 

698 append_field(bundle, "Init docstring", "init_docstring", formatter) 

699 

700 append_field(bundle, "File", "file") 

701 append_field(bundle, "Type", "type_name") 

702 append_field(bundle, "Subclasses", "subclasses") 

703 

704 else: 

705 # General Python objects 

706 append_field(bundle, "Signature", "definition", code_formatter) 

707 append_field(bundle, "Call signature", "call_def", code_formatter) 

708 append_field(bundle, "Type", "type_name") 

709 append_field(bundle, "String form", "string_form") 

710 

711 # Namespace 

712 if info["namespace"] != "Interactive": 

713 append_field(bundle, "Namespace", "namespace") 

714 

715 append_field(bundle, "Length", "length") 

716 append_field(bundle, "File", "file") 

717 

718 # Source or docstring, depending on detail level and whether 

719 # source found. 

720 if detail_level > 0 and info["source"]: 

721 append_field(bundle, "Source", "source", code_formatter) 

722 else: 

723 append_field(bundle, "Docstring", "docstring", formatter) 

724 

725 append_field(bundle, "Class docstring", "class_docstring", formatter) 

726 append_field(bundle, "Init docstring", "init_docstring", formatter) 

727 append_field(bundle, "Call docstring", "call_docstring", formatter) 

728 return bundle 

729 

730 

731 def _get_info( 

732 self, 

733 obj: Any, 

734 oname: str = "", 

735 formatter=None, 

736 info: Optional[OInfo] = None, 

737 detail_level=0, 

738 omit_sections=(), 

739 ) -> Bundle: 

740 """Retrieve an info dict and format it. 

741 

742 Parameters 

743 ---------- 

744 obj : any 

745 Object to inspect and return info from 

746 oname : str (default: ''): 

747 Name of the variable pointing to `obj`. 

748 formatter : callable 

749 info 

750 already computed information 

751 detail_level : integer 

752 Granularity of detail level, if set to 1, give more information. 

753 omit_sections : container[str] 

754 Titles or keys to omit from output (can be set, tuple, etc., anything supporting `in`) 

755 """ 

756 

757 info_dict = self.info(obj, oname=oname, info=info, detail_level=detail_level) 

758 bundle = self._make_info_unformatted( 

759 obj, 

760 info_dict, 

761 formatter, 

762 detail_level=detail_level, 

763 omit_sections=omit_sections, 

764 ) 

765 return self.format_mime(bundle) 

766 

767 def pinfo( 

768 self, 

769 obj, 

770 oname="", 

771 formatter=None, 

772 info: Optional[OInfo] = None, 

773 detail_level=0, 

774 enable_html_pager=True, 

775 omit_sections=(), 

776 ): 

777 """Show detailed information about an object. 

778 

779 Optional arguments: 

780 

781 - oname: name of the variable pointing to the object. 

782 

783 - formatter: callable (optional) 

784 A special formatter for docstrings. 

785 

786 The formatter is a callable that takes a string as an input 

787 and returns either a formatted string or a mime type bundle 

788 in the form of a dictionary. 

789 

790 Although the support of custom formatter returning a string 

791 instead of a mime type bundle is deprecated. 

792 

793 - info: a structure with some information fields which may have been 

794 precomputed already. 

795 

796 - detail_level: if set to 1, more information is given. 

797 

798 - omit_sections: set of section keys and titles to omit 

799 """ 

800 assert info is not None 

801 info_b: Bundle = self._get_info( 

802 obj, oname, formatter, info, detail_level, omit_sections=omit_sections 

803 ) 

804 if not enable_html_pager: 

805 del info_b["text/html"] 

806 page.page(info_b) 

807 

808 def _info(self, obj, oname="", info=None, detail_level=0): 

809 """ 

810 Inspector.info() was likely improperly marked as deprecated 

811 while only a parameter was deprecated. We "un-deprecate" it. 

812 """ 

813 

814 warnings.warn( 

815 "The `Inspector.info()` method has been un-deprecated as of 8.0 " 

816 "and the `formatter=` keyword removed. `Inspector._info` is now " 

817 "an alias, and you can just call `.info()` directly.", 

818 DeprecationWarning, 

819 stacklevel=2, 

820 ) 

821 return self.info(obj, oname=oname, info=info, detail_level=detail_level) 

822 

823 def info(self, obj, oname="", info=None, detail_level=0) -> Dict[str, Any]: 

824 """Compute a dict with detailed information about an object. 

825 

826 Parameters 

827 ---------- 

828 obj : any 

829 An object to find information about 

830 oname : str (default: '') 

831 Name of the variable pointing to `obj`. 

832 info : (default: None) 

833 A struct (dict like with attr access) with some information fields 

834 which may have been precomputed already. 

835 detail_level : int (default:0) 

836 If set to 1, more information is given. 

837 

838 Returns 

839 ------- 

840 An object info dict with known fields from `info_fields`. Keys are 

841 strings, values are string or None. 

842 """ 

843 

844 if info is None: 

845 ismagic = False 

846 isalias = False 

847 ospace = '' 

848 else: 

849 ismagic = info.ismagic 

850 isalias = info.isalias 

851 ospace = info.namespace 

852 

853 # Get docstring, special-casing aliases: 

854 att_name = oname.split(".")[-1] 

855 parents_docs = None 

856 prelude = "" 

857 if info and info.parent is not None and hasattr(info.parent, HOOK_NAME): 

858 parents_docs_dict = getattr(info.parent, HOOK_NAME) 

859 parents_docs = parents_docs_dict.get(att_name, None) 

860 out = dict( 

861 name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None 

862 ) 

863 

864 if parents_docs: 

865 ds = parents_docs 

866 elif isalias: 

867 if not callable(obj): 

868 try: 

869 ds = "Alias to the system command:\n %s" % obj[1] 

870 except: 

871 ds = "Alias: " + str(obj) 

872 else: 

873 ds = "Alias to " + str(obj) 

874 if obj.__doc__: 

875 ds += "\nDocstring:\n" + obj.__doc__ 

876 else: 

877 ds_or_None = getdoc(obj) 

878 if ds_or_None is None: 

879 ds = '<no docstring>' 

880 else: 

881 ds = ds_or_None 

882 

883 ds = prelude + ds 

884 

885 # store output in a dict, we initialize it here and fill it as we go 

886 

887 string_max = 200 # max size of strings to show (snipped if longer) 

888 shalf = int((string_max - 5) / 2) 

889 

890 if ismagic: 

891 out['type_name'] = 'Magic function' 

892 elif isalias: 

893 out['type_name'] = 'System alias' 

894 else: 

895 out['type_name'] = type(obj).__name__ 

896 

897 try: 

898 bclass = obj.__class__ 

899 out['base_class'] = str(bclass) 

900 except: 

901 pass 

902 

903 # String form, but snip if too long in ? form (full in ??) 

904 if detail_level >= self.str_detail_level: 

905 try: 

906 ostr = str(obj) 

907 str_head = 'string_form' 

908 if not detail_level and len(ostr)>string_max: 

909 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:] 

910 ostr = ("\n" + " " * len(str_head.expandtabs())).\ 

911 join(q.strip() for q in ostr.split("\n")) 

912 out[str_head] = ostr 

913 except: 

914 pass 

915 

916 if ospace: 

917 out['namespace'] = ospace 

918 

919 # Length (for strings and lists) 

920 try: 

921 out['length'] = str(len(obj)) 

922 except Exception: 

923 pass 

924 

925 # Filename where object was defined 

926 binary_file = False 

927 fname = find_file(obj) 

928 if fname is None: 

929 # if anything goes wrong, we don't want to show source, so it's as 

930 # if the file was binary 

931 binary_file = True 

932 else: 

933 if fname.endswith(('.so', '.dll', '.pyd')): 

934 binary_file = True 

935 elif fname.endswith('<string>'): 

936 fname = 'Dynamically generated function. No source code available.' 

937 out['file'] = compress_user(fname) 

938 

939 # Original source code for a callable, class or property. 

940 if detail_level: 

941 # Flush the source cache because inspect can return out-of-date 

942 # source 

943 linecache.checkcache() 

944 try: 

945 if isinstance(obj, property) or not binary_file: 

946 src = getsource(obj, oname) 

947 if src is not None: 

948 src = src.rstrip() 

949 out['source'] = src 

950 

951 except Exception: 

952 pass 

953 

954 # Add docstring only if no source is to be shown (avoid repetitions). 

955 if ds and not self._source_contains_docstring(out.get('source'), ds): 

956 out['docstring'] = ds 

957 

958 # Constructor docstring for classes 

959 if inspect.isclass(obj): 

960 out['isclass'] = True 

961 

962 # get the init signature: 

963 try: 

964 init_def = self._getdef(obj, oname) 

965 except AttributeError: 

966 init_def = None 

967 

968 # get the __init__ docstring 

969 try: 

970 obj_init = obj.__init__ 

971 except AttributeError: 

972 init_ds = None 

973 else: 

974 if init_def is None: 

975 # Get signature from init if top-level sig failed. 

976 # Can happen for built-in types (list, etc.). 

977 try: 

978 init_def = self._getdef(obj_init, oname) 

979 except AttributeError: 

980 pass 

981 init_ds = getdoc(obj_init) 

982 # Skip Python's auto-generated docstrings 

983 if init_ds == _object_init_docstring: 

984 init_ds = None 

985 

986 if init_def: 

987 out['init_definition'] = init_def 

988 

989 if init_ds: 

990 out['init_docstring'] = init_ds 

991 

992 names = [sub.__name__ for sub in type.__subclasses__(obj)] 

993 if len(names) < 10: 

994 all_names = ', '.join(names) 

995 else: 

996 all_names = ', '.join(names[:10]+['...']) 

997 out['subclasses'] = all_names 

998 # and class docstring for instances: 

999 else: 

1000 # reconstruct the function definition and print it: 

1001 defln = self._getdef(obj, oname) 

1002 if defln: 

1003 out['definition'] = defln 

1004 

1005 # First, check whether the instance docstring is identical to the 

1006 # class one, and print it separately if they don't coincide. In 

1007 # most cases they will, but it's nice to print all the info for 

1008 # objects which use instance-customized docstrings. 

1009 if ds: 

1010 try: 

1011 cls = getattr(obj,'__class__') 

1012 except: 

1013 class_ds = None 

1014 else: 

1015 class_ds = getdoc(cls) 

1016 # Skip Python's auto-generated docstrings 

1017 if class_ds in _builtin_type_docstrings: 

1018 class_ds = None 

1019 if class_ds and ds != class_ds: 

1020 out['class_docstring'] = class_ds 

1021 

1022 # Next, try to show constructor docstrings 

1023 try: 

1024 init_ds = getdoc(obj.__init__) 

1025 # Skip Python's auto-generated docstrings 

1026 if init_ds == _object_init_docstring: 

1027 init_ds = None 

1028 except AttributeError: 

1029 init_ds = None 

1030 if init_ds: 

1031 out['init_docstring'] = init_ds 

1032 

1033 # Call form docstring for callable instances 

1034 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj): 

1035 call_def = self._getdef(obj.__call__, oname) 

1036 if call_def and (call_def != out.get('definition')): 

1037 # it may never be the case that call def and definition differ, 

1038 # but don't include the same signature twice 

1039 out['call_def'] = call_def 

1040 call_ds = getdoc(obj.__call__) 

1041 # Skip Python's auto-generated docstrings 

1042 if call_ds == _func_call_docstring: 

1043 call_ds = None 

1044 if call_ds: 

1045 out['call_docstring'] = call_ds 

1046 

1047 return object_info(**out) 

1048 

1049 @staticmethod 

1050 def _source_contains_docstring(src, doc): 

1051 """ 

1052 Check whether the source *src* contains the docstring *doc*. 

1053 

1054 This is is helper function to skip displaying the docstring if the 

1055 source already contains it, avoiding repetition of information. 

1056 """ 

1057 try: 

1058 (def_node,) = ast.parse(dedent(src)).body 

1059 return ast.get_docstring(def_node) == doc # type: ignore[arg-type] 

1060 except Exception: 

1061 # The source can become invalid or even non-existent (because it 

1062 # is re-fetched from the source file) so the above code fail in 

1063 # arbitrary ways. 

1064 return False 

1065 

1066 def psearch(self,pattern,ns_table,ns_search=[], 

1067 ignore_case=False,show_all=False, *, list_types=False): 

1068 """Search namespaces with wildcards for objects. 

1069 

1070 Arguments: 

1071 

1072 - pattern: string containing shell-like wildcards to use in namespace 

1073 searches and optionally a type specification to narrow the search to 

1074 objects of that type. 

1075 

1076 - ns_table: dict of name->namespaces for search. 

1077 

1078 Optional arguments: 

1079 

1080 - ns_search: list of namespace names to include in search. 

1081 

1082 - ignore_case(False): make the search case-insensitive. 

1083 

1084 - show_all(False): show all names, including those starting with 

1085 underscores. 

1086 

1087 - list_types(False): list all available object types for object matching. 

1088 """ 

1089 #print 'ps pattern:<%r>' % pattern # dbg 

1090 

1091 # defaults 

1092 type_pattern = 'all' 

1093 filter = '' 

1094 

1095 # list all object types 

1096 if list_types: 

1097 page.page('\n'.join(sorted(typestr2type))) 

1098 return 

1099 

1100 cmds = pattern.split() 

1101 len_cmds = len(cmds) 

1102 if len_cmds == 1: 

1103 # Only filter pattern given 

1104 filter = cmds[0] 

1105 elif len_cmds == 2: 

1106 # Both filter and type specified 

1107 filter,type_pattern = cmds 

1108 else: 

1109 raise ValueError('invalid argument string for psearch: <%s>' % 

1110 pattern) 

1111 

1112 # filter search namespaces 

1113 for name in ns_search: 

1114 if name not in ns_table: 

1115 raise ValueError('invalid namespace <%s>. Valid names: %s' % 

1116 (name,ns_table.keys())) 

1117 

1118 #print 'type_pattern:',type_pattern # dbg 

1119 search_result, namespaces_seen = set(), set() 

1120 for ns_name in ns_search: 

1121 ns = ns_table[ns_name] 

1122 # Normally, locals and globals are the same, so we just check one. 

1123 if id(ns) in namespaces_seen: 

1124 continue 

1125 namespaces_seen.add(id(ns)) 

1126 tmp_res = list_namespace(ns, type_pattern, filter, 

1127 ignore_case=ignore_case, show_all=show_all) 

1128 search_result.update(tmp_res) 

1129 

1130 page.page('\n'.join(sorted(search_result))) 

1131 

1132 

1133def _render_signature(obj_signature, obj_name) -> str: 

1134 """ 

1135 This was mostly taken from inspect.Signature.__str__. 

1136 Look there for the comments. 

1137 The only change is to add linebreaks when this gets too long. 

1138 """ 

1139 result = [] 

1140 pos_only = False 

1141 kw_only = True 

1142 for param in obj_signature.parameters.values(): 

1143 if param.kind == inspect.Parameter.POSITIONAL_ONLY: 

1144 pos_only = True 

1145 elif pos_only: 

1146 result.append('/') 

1147 pos_only = False 

1148 

1149 if param.kind == inspect.Parameter.VAR_POSITIONAL: 

1150 kw_only = False 

1151 elif param.kind == inspect.Parameter.KEYWORD_ONLY and kw_only: 

1152 result.append('*') 

1153 kw_only = False 

1154 

1155 result.append(str(param)) 

1156 

1157 if pos_only: 

1158 result.append('/') 

1159 

1160 # add up name, parameters, braces (2), and commas 

1161 if len(obj_name) + sum(len(r) + 2 for r in result) > 75: 

1162 # This doesn’t fit behind “Signature: ” in an inspect window. 

1163 rendered = '{}(\n{})'.format(obj_name, ''.join( 

1164 ' {},\n'.format(r) for r in result) 

1165 ) 

1166 else: 

1167 rendered = '{}({})'.format(obj_name, ', '.join(result)) 

1168 

1169 if obj_signature.return_annotation is not inspect._empty: 

1170 anno = inspect.formatannotation(obj_signature.return_annotation) 

1171 rendered += ' -> {}'.format(anno) 

1172 

1173 return rendered