##// END OF EJS Templates
Deduplicate code
Matthias Bussonnier -
Show More
@@ -1,930 +1,909 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 from __future__ import print_function
14 14
15 15 __all__ = ['Inspector','InspectColors']
16 16
17 17 # stdlib modules
18 18 import inspect
19 19 import linecache
20 20 import os
21 21 from textwrap import dedent
22 22 import types
23 23 import io as stdlib_io
24 24
25 25 try:
26 26 from itertools import izip_longest
27 27 except ImportError:
28 28 from itertools import zip_longest as izip_longest
29 29
30 30 # IPython's own
31 31 from IPython.core import page
32 32 from IPython.lib.pretty import pretty
33 33 from IPython.testing.skipdoctest import skip_doctest_py3
34 34 from IPython.utils import PyColorize
35 35 from IPython.utils import io
36 36 from IPython.utils import openpy
37 37 from IPython.utils import py3compat
38 38 from IPython.utils.dir2 import safe_hasattr
39 39 from IPython.utils.path import compress_user
40 40 from IPython.utils.text import indent
41 41 from IPython.utils.wildcard import list_namespace
42 42 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
43 43 from IPython.utils.py3compat import cast_unicode, string_types, PY3
44 44 from IPython.utils.signatures import signature
45 45
46 46 # builtin docstrings to ignore
47 47 _func_call_docstring = types.FunctionType.__call__.__doc__
48 48 _object_init_docstring = object.__init__.__doc__
49 49 _builtin_type_docstrings = {
50 50 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
51 51 types.FunctionType, property)
52 52 }
53 53
54 54 _builtin_func_type = type(all)
55 55 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
56 56 #****************************************************************************
57 57 # Builtin color schemes
58 58
59 59 Colors = TermColors # just a shorthand
60 60
61 # Build a few color schemes
62 NoColor = ColorScheme(
63 'NoColor',{
64 'header' : Colors.NoColor,
65 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
66 } )
67
68 LinuxColors = ColorScheme(
69 'Linux',{
70 'header' : Colors.LightRed,
71 'normal' : Colors.Normal # color off (usu. Colors.Normal)
72 } )
73
74 LightBGColors = ColorScheme(
75 'LightBG',{
76 'header' : Colors.Red,
77 'normal' : Colors.Normal # color off (usu. Colors.Normal)
78 } )
79
80 # Build table of color schemes (needed by the parser)
81 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
82 'Linux')
61 InspectColors = PyColorize.ANSICodeColors
83 62
84 63 #****************************************************************************
85 64 # Auxiliary functions and objects
86 65
87 66 # See the messaging spec for the definition of all these fields. This list
88 67 # effectively defines the order of display
89 68 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
90 69 'length', 'file', 'definition', 'docstring', 'source',
91 70 'init_definition', 'class_docstring', 'init_docstring',
92 71 'call_def', 'call_docstring',
93 72 # These won't be printed but will be used to determine how to
94 73 # format the object
95 74 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
96 75 ]
97 76
98 77
99 78 def object_info(**kw):
100 79 """Make an object info dict with all fields present."""
101 80 infodict = dict(izip_longest(info_fields, [None]))
102 81 infodict.update(kw)
103 82 return infodict
104 83
105 84
106 85 def get_encoding(obj):
107 86 """Get encoding for python source file defining obj
108 87
109 88 Returns None if obj is not defined in a sourcefile.
110 89 """
111 90 ofile = find_file(obj)
112 91 # run contents of file through pager starting at line where the object
113 92 # is defined, as long as the file isn't binary and is actually on the
114 93 # filesystem.
115 94 if ofile is None:
116 95 return None
117 96 elif ofile.endswith(('.so', '.dll', '.pyd')):
118 97 return None
119 98 elif not os.path.isfile(ofile):
120 99 return None
121 100 else:
122 101 # Print only text files, not extension binaries. Note that
123 102 # getsourcelines returns lineno with 1-offset and page() uses
124 103 # 0-offset, so we must adjust.
125 104 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
126 105 encoding, lines = openpy.detect_encoding(buffer.readline)
127 106 return encoding
128 107
129 108 def getdoc(obj):
130 109 """Stable wrapper around inspect.getdoc.
131 110
132 111 This can't crash because of attribute problems.
133 112
134 113 It also attempts to call a getdoc() method on the given object. This
135 114 allows objects which provide their docstrings via non-standard mechanisms
136 115 (like Pyro proxies) to still be inspected by ipython's ? system."""
137 116 # Allow objects to offer customized documentation via a getdoc method:
138 117 try:
139 118 ds = obj.getdoc()
140 119 except Exception:
141 120 pass
142 121 else:
143 122 # if we get extra info, we add it to the normal docstring.
144 123 if isinstance(ds, string_types):
145 124 return inspect.cleandoc(ds)
146 125
147 126 try:
148 127 docstr = inspect.getdoc(obj)
149 128 encoding = get_encoding(obj)
150 129 return py3compat.cast_unicode(docstr, encoding=encoding)
151 130 except Exception:
152 131 # Harden against an inspect failure, which can occur with
153 132 # SWIG-wrapped extensions.
154 133 raise
155 134 return None
156 135
157 136
158 137 def getsource(obj, oname=''):
159 138 """Wrapper around inspect.getsource.
160 139
161 140 This can be modified by other projects to provide customized source
162 141 extraction.
163 142
164 143 Parameters
165 144 ----------
166 145 obj : object
167 146 an object whose source code we will attempt to extract
168 147 oname : str
169 148 (optional) a name under which the object is known
170 149
171 150 Returns
172 151 -------
173 152 src : unicode or None
174 153
175 154 """
176 155
177 156 if isinstance(obj, property):
178 157 sources = []
179 158 for attrname in ['fget', 'fset', 'fdel']:
180 159 fn = getattr(obj, attrname)
181 160 if fn is not None:
182 161 encoding = get_encoding(fn)
183 162 oname_prefix = ('%s.' % oname) if oname else ''
184 163 sources.append(cast_unicode(
185 164 ''.join(('# ', oname_prefix, attrname)),
186 165 encoding=encoding))
187 166 if inspect.isfunction(fn):
188 167 sources.append(dedent(getsource(fn)))
189 168 else:
190 169 # Default str/repr only prints function name,
191 170 # pretty.pretty prints module name too.
192 171 sources.append(cast_unicode(
193 172 '%s%s = %s\n' % (
194 173 oname_prefix, attrname, pretty(fn)),
195 174 encoding=encoding))
196 175 if sources:
197 176 return '\n'.join(sources)
198 177 else:
199 178 return None
200 179
201 180 else:
202 181 # Get source for non-property objects.
203 182
204 183 obj = _get_wrapped(obj)
205 184
206 185 try:
207 186 src = inspect.getsource(obj)
208 187 except TypeError:
209 188 # The object itself provided no meaningful source, try looking for
210 189 # its class definition instead.
211 190 if hasattr(obj, '__class__'):
212 191 try:
213 192 src = inspect.getsource(obj.__class__)
214 193 except TypeError:
215 194 return None
216 195
217 196 encoding = get_encoding(obj)
218 197 return cast_unicode(src, encoding=encoding)
219 198
220 199
221 200 def is_simple_callable(obj):
222 201 """True if obj is a function ()"""
223 202 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
224 203 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
225 204
226 205
227 206 def getargspec(obj):
228 207 """Wrapper around :func:`inspect.getfullargspec` on Python 3, and
229 208 :func:inspect.getargspec` on Python 2.
230 209
231 210 In addition to functions and methods, this can also handle objects with a
232 211 ``__call__`` attribute.
233 212 """
234 213 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
235 214 obj = obj.__call__
236 215
237 216 return inspect.getfullargspec(obj) if PY3 else inspect.getargspec(obj)
238 217
239 218
240 219 def format_argspec(argspec):
241 220 """Format argspect, convenience wrapper around inspect's.
242 221
243 222 This takes a dict instead of ordered arguments and calls
244 223 inspect.format_argspec with the arguments in the necessary order.
245 224 """
246 225 return inspect.formatargspec(argspec['args'], argspec['varargs'],
247 226 argspec['varkw'], argspec['defaults'])
248 227
249 228
250 229 def call_tip(oinfo, format_call=True):
251 230 """Extract call tip data from an oinfo dict.
252 231
253 232 Parameters
254 233 ----------
255 234 oinfo : dict
256 235
257 236 format_call : bool, optional
258 237 If True, the call line is formatted and returned as a string. If not, a
259 238 tuple of (name, argspec) is returned.
260 239
261 240 Returns
262 241 -------
263 242 call_info : None, str or (str, dict) tuple.
264 243 When format_call is True, the whole call information is formattted as a
265 244 single string. Otherwise, the object's name and its argspec dict are
266 245 returned. If no call information is available, None is returned.
267 246
268 247 docstring : str or None
269 248 The most relevant docstring for calling purposes is returned, if
270 249 available. The priority is: call docstring for callable instances, then
271 250 constructor docstring for classes, then main object's docstring otherwise
272 251 (regular functions).
273 252 """
274 253 # Get call definition
275 254 argspec = oinfo.get('argspec')
276 255 if argspec is None:
277 256 call_line = None
278 257 else:
279 258 # Callable objects will have 'self' as their first argument, prune
280 259 # it out if it's there for clarity (since users do *not* pass an
281 260 # extra first argument explicitly).
282 261 try:
283 262 has_self = argspec['args'][0] == 'self'
284 263 except (KeyError, IndexError):
285 264 pass
286 265 else:
287 266 if has_self:
288 267 argspec['args'] = argspec['args'][1:]
289 268
290 269 call_line = oinfo['name']+format_argspec(argspec)
291 270
292 271 # Now get docstring.
293 272 # The priority is: call docstring, constructor docstring, main one.
294 273 doc = oinfo.get('call_docstring')
295 274 if doc is None:
296 275 doc = oinfo.get('init_docstring')
297 276 if doc is None:
298 277 doc = oinfo.get('docstring','')
299 278
300 279 return call_line, doc
301 280
302 281
303 282 def _get_wrapped(obj):
304 283 """Get the original object if wrapped in one or more @decorators"""
305 284 while safe_hasattr(obj, '__wrapped__'):
306 285 obj = obj.__wrapped__
307 286 return obj
308 287
309 288 def find_file(obj):
310 289 """Find the absolute path to the file where an object was defined.
311 290
312 291 This is essentially a robust wrapper around `inspect.getabsfile`.
313 292
314 293 Returns None if no file can be found.
315 294
316 295 Parameters
317 296 ----------
318 297 obj : any Python object
319 298
320 299 Returns
321 300 -------
322 301 fname : str
323 302 The absolute path to the file where the object was defined.
324 303 """
325 304 obj = _get_wrapped(obj)
326 305
327 306 fname = None
328 307 try:
329 308 fname = inspect.getabsfile(obj)
330 309 except TypeError:
331 310 # For an instance, the file that matters is where its class was
332 311 # declared.
333 312 if hasattr(obj, '__class__'):
334 313 try:
335 314 fname = inspect.getabsfile(obj.__class__)
336 315 except TypeError:
337 316 # Can happen for builtins
338 317 pass
339 318 except:
340 319 pass
341 320 return cast_unicode(fname)
342 321
343 322
344 323 def find_source_lines(obj):
345 324 """Find the line number in a file where an object was defined.
346 325
347 326 This is essentially a robust wrapper around `inspect.getsourcelines`.
348 327
349 328 Returns None if no file can be found.
350 329
351 330 Parameters
352 331 ----------
353 332 obj : any Python object
354 333
355 334 Returns
356 335 -------
357 336 lineno : int
358 337 The line number where the object definition starts.
359 338 """
360 339 obj = _get_wrapped(obj)
361 340
362 341 try:
363 342 try:
364 343 lineno = inspect.getsourcelines(obj)[1]
365 344 except TypeError:
366 345 # For instances, try the class object like getsource() does
367 346 if hasattr(obj, '__class__'):
368 347 lineno = inspect.getsourcelines(obj.__class__)[1]
369 348 else:
370 349 lineno = None
371 350 except:
372 351 return None
373 352
374 353 return lineno
375 354
376 355
377 356 class Inspector:
378 357 def __init__(self, color_table=InspectColors,
379 358 code_color_table=PyColorize.ANSICodeColors,
380 359 scheme='NoColor',
381 360 str_detail_level=0):
382 361 self.color_table = color_table
383 362 self.parser = PyColorize.Parser(code_color_table,out='str')
384 363 self.format = self.parser.format
385 364 self.str_detail_level = str_detail_level
386 365 self.set_active_scheme(scheme)
387 366
388 367 def _getdef(self,obj,oname=''):
389 368 """Return the call signature for any callable object.
390 369
391 370 If any exception is generated, None is returned instead and the
392 371 exception is suppressed."""
393 372 try:
394 373 hdef = oname + str(signature(obj))
395 374 return cast_unicode(hdef)
396 375 except:
397 376 return None
398 377
399 378 def __head(self,h):
400 379 """Return a header string with proper colors."""
401 380 return '%s%s%s' % (self.color_table.active_colors.header,h,
402 381 self.color_table.active_colors.normal)
403 382
404 383 def set_active_scheme(self, scheme):
405 384 self.color_table.set_active_scheme(scheme)
406 385 self.parser.color_table.set_active_scheme(scheme)
407 386
408 387 def noinfo(self, msg, oname):
409 388 """Generic message when no information is found."""
410 389 print('No %s found' % msg, end=' ')
411 390 if oname:
412 391 print('for %s' % oname)
413 392 else:
414 393 print()
415 394
416 395 def pdef(self, obj, oname=''):
417 396 """Print the call signature for any callable object.
418 397
419 398 If the object is a class, print the constructor information."""
420 399
421 400 if not callable(obj):
422 401 print('Object is not callable.')
423 402 return
424 403
425 404 header = ''
426 405
427 406 if inspect.isclass(obj):
428 407 header = self.__head('Class constructor information:\n')
429 408 obj = obj.__init__
430 409 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
431 410 obj = obj.__call__
432 411
433 412 output = self._getdef(obj,oname)
434 413 if output is None:
435 414 self.noinfo('definition header',oname)
436 415 else:
437 416 print(header,self.format(output), end=' ', file=io.stdout)
438 417
439 418 # In Python 3, all classes are new-style, so they all have __init__.
440 419 @skip_doctest_py3
441 420 def pdoc(self,obj,oname='',formatter = None):
442 421 """Print the docstring for any object.
443 422
444 423 Optional:
445 424 -formatter: a function to run the docstring through for specially
446 425 formatted docstrings.
447 426
448 427 Examples
449 428 --------
450 429
451 430 In [1]: class NoInit:
452 431 ...: pass
453 432
454 433 In [2]: class NoDoc:
455 434 ...: def __init__(self):
456 435 ...: pass
457 436
458 437 In [3]: %pdoc NoDoc
459 438 No documentation found for NoDoc
460 439
461 440 In [4]: %pdoc NoInit
462 441 No documentation found for NoInit
463 442
464 443 In [5]: obj = NoInit()
465 444
466 445 In [6]: %pdoc obj
467 446 No documentation found for obj
468 447
469 448 In [5]: obj2 = NoDoc()
470 449
471 450 In [6]: %pdoc obj2
472 451 No documentation found for obj2
473 452 """
474 453
475 454 head = self.__head # For convenience
476 455 lines = []
477 456 ds = getdoc(obj)
478 457 if formatter:
479 458 ds = formatter(ds)
480 459 if ds:
481 460 lines.append(head("Class docstring:"))
482 461 lines.append(indent(ds))
483 462 if inspect.isclass(obj) and hasattr(obj, '__init__'):
484 463 init_ds = getdoc(obj.__init__)
485 464 if init_ds is not None:
486 465 lines.append(head("Init docstring:"))
487 466 lines.append(indent(init_ds))
488 467 elif hasattr(obj,'__call__'):
489 468 call_ds = getdoc(obj.__call__)
490 469 if call_ds:
491 470 lines.append(head("Call docstring:"))
492 471 lines.append(indent(call_ds))
493 472
494 473 if not lines:
495 474 self.noinfo('documentation',oname)
496 475 else:
497 476 page.page('\n'.join(lines))
498 477
499 478 def psource(self, obj, oname=''):
500 479 """Print the source code for an object."""
501 480
502 481 # Flush the source cache because inspect can return out-of-date source
503 482 linecache.checkcache()
504 483 try:
505 484 src = getsource(obj, oname=oname)
506 485 except Exception:
507 486 src = None
508 487
509 488 if src is None:
510 489 self.noinfo('source', oname)
511 490 else:
512 491 page.page(self.format(src))
513 492
514 493 def pfile(self, obj, oname=''):
515 494 """Show the whole file where an object was defined."""
516 495
517 496 lineno = find_source_lines(obj)
518 497 if lineno is None:
519 498 self.noinfo('file', oname)
520 499 return
521 500
522 501 ofile = find_file(obj)
523 502 # run contents of file through pager starting at line where the object
524 503 # is defined, as long as the file isn't binary and is actually on the
525 504 # filesystem.
526 505 if ofile.endswith(('.so', '.dll', '.pyd')):
527 506 print('File %r is binary, not printing.' % ofile)
528 507 elif not os.path.isfile(ofile):
529 508 print('File %r does not exist, not printing.' % ofile)
530 509 else:
531 510 # Print only text files, not extension binaries. Note that
532 511 # getsourcelines returns lineno with 1-offset and page() uses
533 512 # 0-offset, so we must adjust.
534 513 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
535 514
536 515 def _format_fields(self, fields, title_width=0):
537 516 """Formats a list of fields for display.
538 517
539 518 Parameters
540 519 ----------
541 520 fields : list
542 521 A list of 2-tuples: (field_title, field_content)
543 522 title_width : int
544 523 How many characters to pad titles to. Default to longest title.
545 524 """
546 525 out = []
547 526 header = self.__head
548 527 if title_width == 0:
549 528 title_width = max(len(title) + 2 for title, _ in fields)
550 529 for title, content in fields:
551 530 if len(content.splitlines()) > 1:
552 531 title = header(title + ":") + "\n"
553 532 else:
554 533 title = header((title+":").ljust(title_width))
555 534 out.append(cast_unicode(title) + cast_unicode(content))
556 535 return "\n".join(out)
557 536
558 537 def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
559 538 """Format an info dict as text"""
560 539 info = self.info(obj, oname=oname, formatter=formatter,
561 540 info=info, detail_level=detail_level)
562 541 displayfields = []
563 542 def add_fields(fields):
564 543 for title, key in fields:
565 544 field = info[key]
566 545 if field is not None:
567 546 if key == "source":
568 547 displayfields.append((title, self.format(cast_unicode(field.rstrip()))))
569 548 else:
570 549 displayfields.append((title, field.rstrip()))
571 550
572 551 if info['isalias']:
573 552 add_fields([('Repr', "string_form")])
574 553
575 554 elif info['ismagic']:
576 555 if detail_level > 0 and info['source'] is not None:
577 556 add_fields([("Source", "source")])
578 557 else:
579 558 add_fields([("Docstring", "docstring")])
580 559
581 560 add_fields([("File", "file"),
582 561 ])
583 562
584 563 elif info['isclass'] or is_simple_callable(obj):
585 564 # Functions, methods, classes
586 565 add_fields([("Signature", "definition"),
587 566 ("Init signature", "init_definition"),
588 567 ])
589 568 if detail_level > 0 and info['source'] is not None:
590 569 add_fields([("Source", "source")])
591 570 else:
592 571 add_fields([("Docstring", "docstring"),
593 572 ("Init docstring", "init_docstring"),
594 573 ])
595 574
596 575 add_fields([('File', 'file'),
597 576 ('Type', 'type_name'),
598 577 ])
599 578
600 579 else:
601 580 # General Python objects
602 581 add_fields([("Type", "type_name")])
603 582
604 583 # Base class for old-style instances
605 584 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
606 585 displayfields.append(("Base Class", info['base_class'].rstrip()))
607 586
608 587 add_fields([("String form", "string_form")])
609 588
610 589 # Namespace
611 590 if info['namespace'] != 'Interactive':
612 591 displayfields.append(("Namespace", info['namespace'].rstrip()))
613 592
614 593 add_fields([("Length", "length"),
615 594 ("File", "file"),
616 595 ("Signature", "definition"),
617 596 ])
618 597
619 598 # Source or docstring, depending on detail level and whether
620 599 # source found.
621 600 if detail_level > 0 and info['source'] is not None:
622 601 displayfields.append(("Source",
623 602 self.format(cast_unicode(info['source']))))
624 603 elif info['docstring'] is not None:
625 604 displayfields.append(("Docstring", info["docstring"]))
626 605
627 606 add_fields([("Class docstring", "class_docstring"),
628 607 ("Init docstring", "init_docstring"),
629 608 ("Call signature", "call_def"),
630 609 ("Call docstring", "call_docstring")])
631 610
632 611 if displayfields:
633 612 return self._format_fields(displayfields)
634 613 else:
635 614 return u''
636 615
637 616 def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0):
638 617 """Show detailed information about an object.
639 618
640 619 Optional arguments:
641 620
642 621 - oname: name of the variable pointing to the object.
643 622
644 623 - formatter: special formatter for docstrings (see pdoc)
645 624
646 625 - info: a structure with some information fields which may have been
647 626 precomputed already.
648 627
649 628 - detail_level: if set to 1, more information is given.
650 629 """
651 630 text = self._format_info(obj, oname, formatter, info, detail_level)
652 631 if text:
653 632 page.page(text)
654 633
655 634 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
656 635 """Compute a dict with detailed information about an object.
657 636
658 637 Optional arguments:
659 638
660 639 - oname: name of the variable pointing to the object.
661 640
662 641 - formatter: special formatter for docstrings (see pdoc)
663 642
664 643 - info: a structure with some information fields which may have been
665 644 precomputed already.
666 645
667 646 - detail_level: if set to 1, more information is given.
668 647 """
669 648
670 649 obj_type = type(obj)
671 650
672 651 if info is None:
673 652 ismagic = 0
674 653 isalias = 0
675 654 ospace = ''
676 655 else:
677 656 ismagic = info.ismagic
678 657 isalias = info.isalias
679 658 ospace = info.namespace
680 659
681 660 # Get docstring, special-casing aliases:
682 661 if isalias:
683 662 if not callable(obj):
684 663 try:
685 664 ds = "Alias to the system command:\n %s" % obj[1]
686 665 except:
687 666 ds = "Alias: " + str(obj)
688 667 else:
689 668 ds = "Alias to " + str(obj)
690 669 if obj.__doc__:
691 670 ds += "\nDocstring:\n" + obj.__doc__
692 671 else:
693 672 ds = getdoc(obj)
694 673 if ds is None:
695 674 ds = '<no docstring>'
696 675 if formatter is not None:
697 676 ds = formatter(ds)
698 677
699 678 # store output in a dict, we initialize it here and fill it as we go
700 679 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
701 680
702 681 string_max = 200 # max size of strings to show (snipped if longer)
703 682 shalf = int((string_max -5)/2)
704 683
705 684 if ismagic:
706 685 obj_type_name = 'Magic function'
707 686 elif isalias:
708 687 obj_type_name = 'System alias'
709 688 else:
710 689 obj_type_name = obj_type.__name__
711 690 out['type_name'] = obj_type_name
712 691
713 692 try:
714 693 bclass = obj.__class__
715 694 out['base_class'] = str(bclass)
716 695 except: pass
717 696
718 697 # String form, but snip if too long in ? form (full in ??)
719 698 if detail_level >= self.str_detail_level:
720 699 try:
721 700 ostr = str(obj)
722 701 str_head = 'string_form'
723 702 if not detail_level and len(ostr)>string_max:
724 703 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
725 704 ostr = ("\n" + " " * len(str_head.expandtabs())).\
726 705 join(q.strip() for q in ostr.split("\n"))
727 706 out[str_head] = ostr
728 707 except:
729 708 pass
730 709
731 710 if ospace:
732 711 out['namespace'] = ospace
733 712
734 713 # Length (for strings and lists)
735 714 try:
736 715 out['length'] = str(len(obj))
737 716 except: pass
738 717
739 718 # Filename where object was defined
740 719 binary_file = False
741 720 fname = find_file(obj)
742 721 if fname is None:
743 722 # if anything goes wrong, we don't want to show source, so it's as
744 723 # if the file was binary
745 724 binary_file = True
746 725 else:
747 726 if fname.endswith(('.so', '.dll', '.pyd')):
748 727 binary_file = True
749 728 elif fname.endswith('<string>'):
750 729 fname = 'Dynamically generated function. No source code available.'
751 730 out['file'] = compress_user(fname)
752 731
753 732 # Original source code for a callable, class or property.
754 733 if detail_level:
755 734 # Flush the source cache because inspect can return out-of-date
756 735 # source
757 736 linecache.checkcache()
758 737 try:
759 738 if isinstance(obj, property) or not binary_file:
760 739 src = getsource(obj, oname)
761 740 if src is not None:
762 741 src = src.rstrip()
763 742 out['source'] = src
764 743
765 744 except Exception:
766 745 pass
767 746
768 747 # Add docstring only if no source is to be shown (avoid repetitions).
769 748 if ds and out.get('source', None) is None:
770 749 out['docstring'] = ds
771 750
772 751 # Constructor docstring for classes
773 752 if inspect.isclass(obj):
774 753 out['isclass'] = True
775 754 # reconstruct the function definition and print it:
776 755 try:
777 756 obj_init = obj.__init__
778 757 except AttributeError:
779 758 init_def = init_ds = None
780 759 else:
781 760 init_def = self._getdef(obj_init,oname)
782 761 init_ds = getdoc(obj_init)
783 762 # Skip Python's auto-generated docstrings
784 763 if init_ds == _object_init_docstring:
785 764 init_ds = None
786 765
787 766 if init_def or init_ds:
788 767 if init_def:
789 768 out['init_definition'] = self.format(init_def)
790 769 if init_ds:
791 770 out['init_docstring'] = init_ds
792 771
793 772 # and class docstring for instances:
794 773 else:
795 774 # reconstruct the function definition and print it:
796 775 defln = self._getdef(obj, oname)
797 776 if defln:
798 777 out['definition'] = self.format(defln)
799 778
800 779 # First, check whether the instance docstring is identical to the
801 780 # class one, and print it separately if they don't coincide. In
802 781 # most cases they will, but it's nice to print all the info for
803 782 # objects which use instance-customized docstrings.
804 783 if ds:
805 784 try:
806 785 cls = getattr(obj,'__class__')
807 786 except:
808 787 class_ds = None
809 788 else:
810 789 class_ds = getdoc(cls)
811 790 # Skip Python's auto-generated docstrings
812 791 if class_ds in _builtin_type_docstrings:
813 792 class_ds = None
814 793 if class_ds and ds != class_ds:
815 794 out['class_docstring'] = class_ds
816 795
817 796 # Next, try to show constructor docstrings
818 797 try:
819 798 init_ds = getdoc(obj.__init__)
820 799 # Skip Python's auto-generated docstrings
821 800 if init_ds == _object_init_docstring:
822 801 init_ds = None
823 802 except AttributeError:
824 803 init_ds = None
825 804 if init_ds:
826 805 out['init_docstring'] = init_ds
827 806
828 807 # Call form docstring for callable instances
829 808 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
830 809 call_def = self._getdef(obj.__call__, oname)
831 810 if call_def:
832 811 call_def = self.format(call_def)
833 812 # it may never be the case that call def and definition differ,
834 813 # but don't include the same signature twice
835 814 if call_def != out.get('definition'):
836 815 out['call_def'] = call_def
837 816 call_ds = getdoc(obj.__call__)
838 817 # Skip Python's auto-generated docstrings
839 818 if call_ds == _func_call_docstring:
840 819 call_ds = None
841 820 if call_ds:
842 821 out['call_docstring'] = call_ds
843 822
844 823 # Compute the object's argspec as a callable. The key is to decide
845 824 # whether to pull it from the object itself, from its __init__ or
846 825 # from its __call__ method.
847 826
848 827 if inspect.isclass(obj):
849 828 # Old-style classes need not have an __init__
850 829 callable_obj = getattr(obj, "__init__", None)
851 830 elif callable(obj):
852 831 callable_obj = obj
853 832 else:
854 833 callable_obj = None
855 834
856 835 if callable_obj:
857 836 try:
858 837 argspec = getargspec(callable_obj)
859 838 except (TypeError, AttributeError):
860 839 # For extensions/builtins we can't retrieve the argspec
861 840 pass
862 841 else:
863 842 # named tuples' _asdict() method returns an OrderedDict, but we
864 843 # we want a normal
865 844 out['argspec'] = argspec_dict = dict(argspec._asdict())
866 845 # We called this varkw before argspec became a named tuple.
867 846 # With getfullargspec it's also called varkw.
868 847 if 'varkw' not in argspec_dict:
869 848 argspec_dict['varkw'] = argspec_dict.pop('keywords')
870 849
871 850 return object_info(**out)
872 851
873 852 def psearch(self,pattern,ns_table,ns_search=[],
874 853 ignore_case=False,show_all=False):
875 854 """Search namespaces with wildcards for objects.
876 855
877 856 Arguments:
878 857
879 858 - pattern: string containing shell-like wildcards to use in namespace
880 859 searches and optionally a type specification to narrow the search to
881 860 objects of that type.
882 861
883 862 - ns_table: dict of name->namespaces for search.
884 863
885 864 Optional arguments:
886 865
887 866 - ns_search: list of namespace names to include in search.
888 867
889 868 - ignore_case(False): make the search case-insensitive.
890 869
891 870 - show_all(False): show all names, including those starting with
892 871 underscores.
893 872 """
894 873 #print 'ps pattern:<%r>' % pattern # dbg
895 874
896 875 # defaults
897 876 type_pattern = 'all'
898 877 filter = ''
899 878
900 879 cmds = pattern.split()
901 880 len_cmds = len(cmds)
902 881 if len_cmds == 1:
903 882 # Only filter pattern given
904 883 filter = cmds[0]
905 884 elif len_cmds == 2:
906 885 # Both filter and type specified
907 886 filter,type_pattern = cmds
908 887 else:
909 888 raise ValueError('invalid argument string for psearch: <%s>' %
910 889 pattern)
911 890
912 891 # filter search namespaces
913 892 for name in ns_search:
914 893 if name not in ns_table:
915 894 raise ValueError('invalid namespace <%s>. Valid names: %s' %
916 895 (name,ns_table.keys()))
917 896
918 897 #print 'type_pattern:',type_pattern # dbg
919 898 search_result, namespaces_seen = set(), set()
920 899 for ns_name in ns_search:
921 900 ns = ns_table[ns_name]
922 901 # Normally, locals and globals are the same, so we just check one.
923 902 if id(ns) in namespaces_seen:
924 903 continue
925 904 namespaces_seen.add(id(ns))
926 905 tmp_res = list_namespace(ns, type_pattern, filter,
927 906 ignore_case=ignore_case, show_all=show_all)
928 907 search_result.update(tmp_res)
929 908
930 909 page.page('\n'.join(sorted(search_result)))
@@ -1,315 +1,318 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Class and program to colorize python source code for ANSI terminals.
4 4
5 5 Based on an HTML code highlighter by Jurgen Hermann found at:
6 6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
7 7
8 8 Modifications by Fernando Perez (fperez@colorado.edu).
9 9
10 10 Information on the original HTML highlighter follows:
11 11
12 12 MoinMoin - Python Source Parser
13 13
14 14 Title: Colorize Python source using the built-in tokenizer
15 15
16 16 Submitter: Jurgen Hermann
17 17 Last Updated:2001/04/06
18 18
19 19 Version no:1.2
20 20
21 21 Description:
22 22
23 23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
24 24 Python source code to HTML markup, rendering comments, keywords,
25 25 operators, numeric and string literals in different colors.
26 26
27 27 It shows how to use the built-in keyword, token and tokenize modules to
28 28 scan Python source code and re-emit it with no changes to its original
29 29 formatting (which is the hard part).
30 30 """
31 31 from __future__ import print_function
32 32 from __future__ import absolute_import
33 33 from __future__ import unicode_literals
34 34
35 35 __all__ = ['ANSICodeColors','Parser']
36 36
37 37 _scheme_default = 'Linux'
38 38
39 39
40 40 # Imports
41 41 import keyword
42 42 import os
43 43 import sys
44 44 import token
45 45 import tokenize
46 46
47 47 try:
48 48 generate_tokens = tokenize.generate_tokens
49 49 except AttributeError:
50 50 # Python 3. Note that we use the undocumented _tokenize because it expects
51 51 # strings, not bytes. See also Python issue #9969.
52 52 generate_tokens = tokenize._tokenize
53 53
54 from IPython.utils.coloransi import *
54 from IPython.utils.coloransi import TermColors, InputTermColors ,ColorScheme, ColorSchemeTable
55 55 from IPython.utils.py3compat import PY3
56 56
57 57 if PY3:
58 58 from io import StringIO
59 59 else:
60 60 from StringIO import StringIO
61 61
62 62 #############################################################################
63 63 ### Python Source Parser (does Hilighting)
64 64 #############################################################################
65 65
66 66 _KEYWORD = token.NT_OFFSET + 1
67 67 _TEXT = token.NT_OFFSET + 2
68 68
69 69 #****************************************************************************
70 70 # Builtin color schemes
71 71
72 72 Colors = TermColors # just a shorthand
73 73
74 74 # Build a few color schemes
75 75 NoColor = ColorScheme(
76 76 'NoColor',{
77 'header' : Colors.NoColor,
77 78 token.NUMBER : Colors.NoColor,
78 79 token.OP : Colors.NoColor,
79 80 token.STRING : Colors.NoColor,
80 81 tokenize.COMMENT : Colors.NoColor,
81 82 token.NAME : Colors.NoColor,
82 83 token.ERRORTOKEN : Colors.NoColor,
83 84
84 85 _KEYWORD : Colors.NoColor,
85 86 _TEXT : Colors.NoColor,
86 87
87 88 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
88 89 } )
89 90
90 91 LinuxColors = ColorScheme(
91 92 'Linux',{
93 'header' : Colors.LightRed,
92 94 token.NUMBER : Colors.LightCyan,
93 95 token.OP : Colors.Yellow,
94 96 token.STRING : Colors.LightBlue,
95 97 tokenize.COMMENT : Colors.LightRed,
96 98 token.NAME : Colors.Normal,
97 99 token.ERRORTOKEN : Colors.Red,
98 100
99 101 _KEYWORD : Colors.LightGreen,
100 102 _TEXT : Colors.Yellow,
101 103
102 104 'normal' : Colors.Normal # color off (usu. Colors.Normal)
103 105 } )
104 106
105 107 LightBGColors = ColorScheme(
106 108 'LightBG',{
109 'header' : Colors.Red,
107 110 token.NUMBER : Colors.Cyan,
108 111 token.OP : Colors.Blue,
109 112 token.STRING : Colors.Blue,
110 113 tokenize.COMMENT : Colors.Red,
111 114 token.NAME : Colors.Normal,
112 115 token.ERRORTOKEN : Colors.Red,
113 116
114 117 _KEYWORD : Colors.Green,
115 118 _TEXT : Colors.Blue,
116 119
117 120 'normal' : Colors.Normal # color off (usu. Colors.Normal)
118 121 } )
119 122
120 123 # Build table of color schemes (needed by the parser)
121 124 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
122 125 _scheme_default)
123 126
124 127 class Parser:
125 128 """ Format colored Python source.
126 129 """
127 130
128 131 def __init__(self, color_table=None,out = sys.stdout):
129 132 """ Create a parser with a specified color table and output channel.
130 133
131 134 Call format() to process code.
132 135 """
133 136 self.color_table = color_table and color_table or ANSICodeColors
134 137 self.out = out
135 138
136 139 def format(self, raw, out = None, scheme = ''):
137 140 return self.format2(raw, out, scheme)[0]
138 141
139 142 def format2(self, raw, out = None, scheme = ''):
140 143 """ Parse and send the colored source.
141 144
142 145 If out and scheme are not specified, the defaults (given to
143 146 constructor) are used.
144 147
145 148 out should be a file-type object. Optionally, out can be given as the
146 149 string 'str' and the parser will automatically return the output in a
147 150 string."""
148 151
149 152 string_output = 0
150 153 if out == 'str' or self.out == 'str' or \
151 154 isinstance(self.out,StringIO):
152 155 # XXX - I don't really like this state handling logic, but at this
153 156 # point I don't want to make major changes, so adding the
154 157 # isinstance() check is the simplest I can do to ensure correct
155 158 # behavior.
156 159 out_old = self.out
157 160 self.out = StringIO()
158 161 string_output = 1
159 162 elif out is not None:
160 163 self.out = out
161 164
162 165 # Fast return of the unmodified input for NoColor scheme
163 166 if scheme == 'NoColor':
164 167 error = False
165 168 self.out.write(raw)
166 169 if string_output:
167 170 return raw,error
168 171 else:
169 172 return None,error
170 173
171 174 # local shorthands
172 175 colors = self.color_table[scheme].colors
173 176 self.colors = colors # put in object so __call__ sees it
174 177
175 178 # Remove trailing whitespace and normalize tabs
176 179 self.raw = raw.expandtabs().rstrip()
177 180
178 181 # store line offsets in self.lines
179 182 self.lines = [0, 0]
180 183 pos = 0
181 184 raw_find = self.raw.find
182 185 lines_append = self.lines.append
183 186 while 1:
184 187 pos = raw_find('\n', pos) + 1
185 188 if not pos: break
186 189 lines_append(pos)
187 190 lines_append(len(self.raw))
188 191
189 192 # parse the source and write it
190 193 self.pos = 0
191 194 text = StringIO(self.raw)
192 195
193 196 error = False
194 197 try:
195 198 for atoken in generate_tokens(text.readline):
196 199 self(*atoken)
197 200 except tokenize.TokenError as ex:
198 201 msg = ex.args[0]
199 202 line = ex.args[1][0]
200 203 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
201 204 (colors[token.ERRORTOKEN],
202 205 msg, self.raw[self.lines[line]:],
203 206 colors.normal)
204 207 )
205 208 error = True
206 209 self.out.write(colors.normal+'\n')
207 210 if string_output:
208 211 output = self.out.getvalue()
209 212 self.out = out_old
210 213 return (output, error)
211 214 return (None, error)
212 215
213 216 def __call__(self, toktype, toktext, start_pos, end_pos, line):
214 217 """ Token handler, with syntax highlighting."""
215 218 (srow,scol) = start_pos
216 219 (erow,ecol) = end_pos
217 220 colors = self.colors
218 221 owrite = self.out.write
219 222
220 223 # line separator, so this works across platforms
221 224 linesep = os.linesep
222 225
223 226 # calculate new positions
224 227 oldpos = self.pos
225 228 newpos = self.lines[srow] + scol
226 229 self.pos = newpos + len(toktext)
227 230
228 231 # send the original whitespace, if needed
229 232 if newpos > oldpos:
230 233 owrite(self.raw[oldpos:newpos])
231 234
232 235 # skip indenting tokens
233 236 if toktype in [token.INDENT, token.DEDENT]:
234 237 self.pos = newpos
235 238 return
236 239
237 240 # map token type to a color group
238 241 if token.LPAR <= toktype and toktype <= token.OP:
239 242 toktype = token.OP
240 243 elif toktype == token.NAME and keyword.iskeyword(toktext):
241 244 toktype = _KEYWORD
242 245 color = colors.get(toktype, colors[_TEXT])
243 246
244 247 #print '<%s>' % toktext, # dbg
245 248
246 249 # Triple quoted strings must be handled carefully so that backtracking
247 250 # in pagers works correctly. We need color terminators on _each_ line.
248 251 if linesep in toktext:
249 252 toktext = toktext.replace(linesep, '%s%s%s' %
250 253 (colors.normal,linesep,color))
251 254
252 255 # send text
253 256 owrite('%s%s%s' % (color,toktext,colors.normal))
254 257
255 258 def main(argv=None):
256 259 """Run as a command-line script: colorize a python file or stdin using ANSI
257 260 color escapes and print to stdout.
258 261
259 262 Inputs:
260 263
261 264 - argv(None): a list of strings like sys.argv[1:] giving the command-line
262 265 arguments. If None, use sys.argv[1:].
263 266 """
264 267
265 268 usage_msg = """%prog [options] [filename]
266 269
267 270 Colorize a python file or stdin using ANSI color escapes and print to stdout.
268 271 If no filename is given, or if filename is -, read standard input."""
269 272
270 273 import optparse
271 274 parser = optparse.OptionParser(usage=usage_msg)
272 275 newopt = parser.add_option
273 276 newopt('-s','--scheme',metavar='NAME',dest='scheme_name',action='store',
274 277 choices=['Linux','LightBG','NoColor'],default=_scheme_default,
275 278 help="give the color scheme to use. Currently only 'Linux'\
276 279 (default) and 'LightBG' and 'NoColor' are implemented (give without\
277 280 quotes)")
278 281
279 282 opts,args = parser.parse_args(argv)
280 283
281 284 if len(args) > 1:
282 285 parser.error("you must give at most one filename.")
283 286
284 287 if len(args) == 0:
285 288 fname = '-' # no filename given; setup to read from stdin
286 289 else:
287 290 fname = args[0]
288 291
289 292 if fname == '-':
290 293 stream = sys.stdin
291 294 else:
292 295 try:
293 296 stream = open(fname)
294 297 except IOError as msg:
295 298 print(msg, file=sys.stderr)
296 299 sys.exit(1)
297 300
298 301 parser = Parser()
299 302
300 303 # we need nested try blocks because pre-2.5 python doesn't support unified
301 304 # try-except-finally
302 305 try:
303 306 try:
304 307 # write colorized version to stdout
305 308 parser.format(stream.read(),scheme=opts.scheme_name)
306 309 except IOError as msg:
307 310 # if user reads through a pager and quits, don't print traceback
308 311 if msg.args != (32,'Broken pipe'):
309 312 raise
310 313 finally:
311 314 if stream is not sys.stdin:
312 315 stream.close() # in case a non-handled exception happened above
313 316
314 317 if __name__ == "__main__":
315 318 main()
General Comments 0
You need to be logged in to leave comments. Login now