##// END OF EJS Templates
Merge pull request #6932 from takluyver/help-signatures...
Thomas Kluyver -
r18873:cf19c83f merge
parent child Browse files
Show More
@@ -1,918 +1,919 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.text import indent
40 40 from IPython.utils.wildcard import list_namespace
41 41 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
42 42 from IPython.utils.py3compat import cast_unicode, string_types, PY3
43 from IPython.utils.signatures import signature
43 44
44 45 # builtin docstrings to ignore
45 46 _func_call_docstring = types.FunctionType.__call__.__doc__
46 47 _object_init_docstring = object.__init__.__doc__
47 48 _builtin_type_docstrings = {
48 49 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
49 50 types.FunctionType, property)
50 51 }
51 52
52 53 _builtin_func_type = type(all)
53 54 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
54 55 #****************************************************************************
55 56 # Builtin color schemes
56 57
57 58 Colors = TermColors # just a shorthand
58 59
59 60 # Build a few color schemes
60 61 NoColor = ColorScheme(
61 62 'NoColor',{
62 63 'header' : Colors.NoColor,
63 64 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
64 65 } )
65 66
66 67 LinuxColors = ColorScheme(
67 68 'Linux',{
68 69 'header' : Colors.LightRed,
69 70 'normal' : Colors.Normal # color off (usu. Colors.Normal)
70 71 } )
71 72
72 73 LightBGColors = ColorScheme(
73 74 'LightBG',{
74 75 'header' : Colors.Red,
75 76 'normal' : Colors.Normal # color off (usu. Colors.Normal)
76 77 } )
77 78
78 79 # Build table of color schemes (needed by the parser)
79 80 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
80 81 'Linux')
81 82
82 83 #****************************************************************************
83 84 # Auxiliary functions and objects
84 85
85 86 # See the messaging spec for the definition of all these fields. This list
86 87 # effectively defines the order of display
87 88 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
88 89 'length', 'file', 'definition', 'docstring', 'source',
89 90 'init_definition', 'class_docstring', 'init_docstring',
90 91 'call_def', 'call_docstring',
91 92 # These won't be printed but will be used to determine how to
92 93 # format the object
93 94 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
94 95 ]
95 96
96 97
97 98 def object_info(**kw):
98 99 """Make an object info dict with all fields present."""
99 100 infodict = dict(izip_longest(info_fields, [None]))
100 101 infodict.update(kw)
101 102 return infodict
102 103
103 104
104 105 def get_encoding(obj):
105 106 """Get encoding for python source file defining obj
106 107
107 108 Returns None if obj is not defined in a sourcefile.
108 109 """
109 110 ofile = find_file(obj)
110 111 # run contents of file through pager starting at line where the object
111 112 # is defined, as long as the file isn't binary and is actually on the
112 113 # filesystem.
113 114 if ofile is None:
114 115 return None
115 116 elif ofile.endswith(('.so', '.dll', '.pyd')):
116 117 return None
117 118 elif not os.path.isfile(ofile):
118 119 return None
119 120 else:
120 121 # Print only text files, not extension binaries. Note that
121 122 # getsourcelines returns lineno with 1-offset and page() uses
122 123 # 0-offset, so we must adjust.
123 124 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
124 125 encoding, lines = openpy.detect_encoding(buffer.readline)
125 126 return encoding
126 127
127 128 def getdoc(obj):
128 129 """Stable wrapper around inspect.getdoc.
129 130
130 131 This can't crash because of attribute problems.
131 132
132 133 It also attempts to call a getdoc() method on the given object. This
133 134 allows objects which provide their docstrings via non-standard mechanisms
134 135 (like Pyro proxies) to still be inspected by ipython's ? system."""
135 136 # Allow objects to offer customized documentation via a getdoc method:
136 137 try:
137 138 ds = obj.getdoc()
138 139 except Exception:
139 140 pass
140 141 else:
141 142 # if we get extra info, we add it to the normal docstring.
142 143 if isinstance(ds, string_types):
143 144 return inspect.cleandoc(ds)
144 145
145 146 try:
146 147 docstr = inspect.getdoc(obj)
147 148 encoding = get_encoding(obj)
148 149 return py3compat.cast_unicode(docstr, encoding=encoding)
149 150 except Exception:
150 151 # Harden against an inspect failure, which can occur with
151 152 # SWIG-wrapped extensions.
152 153 raise
153 154 return None
154 155
155 156
156 157 def getsource(obj, oname=''):
157 158 """Wrapper around inspect.getsource.
158 159
159 160 This can be modified by other projects to provide customized source
160 161 extraction.
161 162
162 163 Parameters
163 164 ----------
164 165 obj : object
165 166 an object whose source code we will attempt to extract
166 167 oname : str
167 168 (optional) a name under which the object is known
168 169
169 170 Returns
170 171 -------
171 172 src : unicode or None
172 173
173 174 """
174 175
175 176 if isinstance(obj, property):
176 177 sources = []
177 178 for attrname in ['fget', 'fset', 'fdel']:
178 179 fn = getattr(obj, attrname)
179 180 if fn is not None:
180 181 encoding = get_encoding(fn)
181 182 oname_prefix = ('%s.' % oname) if oname else ''
182 183 sources.append(cast_unicode(
183 184 ''.join(('# ', oname_prefix, attrname)),
184 185 encoding=encoding))
185 186 if inspect.isfunction(fn):
186 187 sources.append(dedent(getsource(fn)))
187 188 else:
188 189 # Default str/repr only prints function name,
189 190 # pretty.pretty prints module name too.
190 191 sources.append(cast_unicode(
191 192 '%s%s = %s\n' % (
192 193 oname_prefix, attrname, pretty(fn)),
193 194 encoding=encoding))
194 195 if sources:
195 196 return '\n'.join(sources)
196 197 else:
197 198 return None
198 199
199 200 else:
200 201 # Get source for non-property objects.
201 202
202 203 # '__wrapped__' attribute is used by some decorators (e.g. ones defined
203 204 # functools) to provide access to the decorated function.
204 205 if hasattr(obj, "__wrapped__"):
205 206 obj = obj.__wrapped__
206 207
207 208 try:
208 209 src = inspect.getsource(obj)
209 210 except TypeError:
210 211 # The object itself provided no meaningful source, try looking for
211 212 # its class definition instead.
212 213 if hasattr(obj, '__class__'):
213 214 try:
214 215 src = inspect.getsource(obj.__class__)
215 216 except TypeError:
216 217 return None
217 218
218 219 encoding = get_encoding(obj)
219 220 return cast_unicode(src, encoding=encoding)
220 221
221 222
222 223 def is_simple_callable(obj):
223 224 """True if obj is a function ()"""
224 225 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
225 226 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
226 227
227 228
228 229 def getargspec(obj):
229 230 """Wrapper around :func:`inspect.getfullargspec` on Python 3, and
230 231 :func:inspect.getargspec` on Python 2.
231 232
232 233 In addition to functions and methods, this can also handle objects with a
233 234 ``__call__`` attribute.
234 235 """
235 236 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
236 237 obj = obj.__call__
237 238
238 239 return inspect.getfullargspec(obj) if PY3 else inspect.getargspec(obj)
239 240
240 241
241 242 def format_argspec(argspec):
242 243 """Format argspect, convenience wrapper around inspect's.
243 244
244 245 This takes a dict instead of ordered arguments and calls
245 246 inspect.format_argspec with the arguments in the necessary order.
246 247 """
247 248 return inspect.formatargspec(argspec['args'], argspec['varargs'],
248 249 argspec['varkw'], argspec['defaults'])
249 250
250 251
251 252 def call_tip(oinfo, format_call=True):
252 253 """Extract call tip data from an oinfo dict.
253 254
254 255 Parameters
255 256 ----------
256 257 oinfo : dict
257 258
258 259 format_call : bool, optional
259 260 If True, the call line is formatted and returned as a string. If not, a
260 261 tuple of (name, argspec) is returned.
261 262
262 263 Returns
263 264 -------
264 265 call_info : None, str or (str, dict) tuple.
265 266 When format_call is True, the whole call information is formattted as a
266 267 single string. Otherwise, the object's name and its argspec dict are
267 268 returned. If no call information is available, None is returned.
268 269
269 270 docstring : str or None
270 271 The most relevant docstring for calling purposes is returned, if
271 272 available. The priority is: call docstring for callable instances, then
272 273 constructor docstring for classes, then main object's docstring otherwise
273 274 (regular functions).
274 275 """
275 276 # Get call definition
276 277 argspec = oinfo.get('argspec')
277 278 if argspec is None:
278 279 call_line = None
279 280 else:
280 281 # Callable objects will have 'self' as their first argument, prune
281 282 # it out if it's there for clarity (since users do *not* pass an
282 283 # extra first argument explicitly).
283 284 try:
284 285 has_self = argspec['args'][0] == 'self'
285 286 except (KeyError, IndexError):
286 287 pass
287 288 else:
288 289 if has_self:
289 290 argspec['args'] = argspec['args'][1:]
290 291
291 292 call_line = oinfo['name']+format_argspec(argspec)
292 293
293 294 # Now get docstring.
294 295 # The priority is: call docstring, constructor docstring, main one.
295 296 doc = oinfo.get('call_docstring')
296 297 if doc is None:
297 298 doc = oinfo.get('init_docstring')
298 299 if doc is None:
299 300 doc = oinfo.get('docstring','')
300 301
301 302 return call_line, doc
302 303
303 304
304 305 def find_file(obj):
305 306 """Find the absolute path to the file where an object was defined.
306 307
307 308 This is essentially a robust wrapper around `inspect.getabsfile`.
308 309
309 310 Returns None if no file can be found.
310 311
311 312 Parameters
312 313 ----------
313 314 obj : any Python object
314 315
315 316 Returns
316 317 -------
317 318 fname : str
318 319 The absolute path to the file where the object was defined.
319 320 """
320 321 # get source if obj was decorated with @decorator
321 322 if safe_hasattr(obj, '__wrapped__'):
322 323 obj = obj.__wrapped__
323 324
324 325 fname = None
325 326 try:
326 327 fname = inspect.getabsfile(obj)
327 328 except TypeError:
328 329 # For an instance, the file that matters is where its class was
329 330 # declared.
330 331 if hasattr(obj, '__class__'):
331 332 try:
332 333 fname = inspect.getabsfile(obj.__class__)
333 334 except TypeError:
334 335 # Can happen for builtins
335 336 pass
336 337 except:
337 338 pass
338 339 return cast_unicode(fname)
339 340
340 341
341 342 def find_source_lines(obj):
342 343 """Find the line number in a file where an object was defined.
343 344
344 345 This is essentially a robust wrapper around `inspect.getsourcelines`.
345 346
346 347 Returns None if no file can be found.
347 348
348 349 Parameters
349 350 ----------
350 351 obj : any Python object
351 352
352 353 Returns
353 354 -------
354 355 lineno : int
355 356 The line number where the object definition starts.
356 357 """
357 358 # get source if obj was decorated with @decorator
358 359 if safe_hasattr(obj, '__wrapped__'):
359 360 obj = obj.__wrapped__
360 361
361 362 try:
362 363 try:
363 364 lineno = inspect.getsourcelines(obj)[1]
364 365 except TypeError:
365 366 # For instances, try the class object like getsource() does
366 367 if hasattr(obj, '__class__'):
367 368 lineno = inspect.getsourcelines(obj.__class__)[1]
368 369 else:
369 370 lineno = None
370 371 except:
371 372 return None
372 373
373 374 return lineno
374 375
375 376
376 377 class Inspector:
377 378 def __init__(self, color_table=InspectColors,
378 379 code_color_table=PyColorize.ANSICodeColors,
379 380 scheme='NoColor',
380 381 str_detail_level=0):
381 382 self.color_table = color_table
382 383 self.parser = PyColorize.Parser(code_color_table,out='str')
383 384 self.format = self.parser.format
384 385 self.str_detail_level = str_detail_level
385 386 self.set_active_scheme(scheme)
386 387
387 388 def _getdef(self,obj,oname=''):
388 389 """Return the call signature for any callable object.
389 390
390 391 If any exception is generated, None is returned instead and the
391 392 exception is suppressed."""
392 393 try:
393 hdef = oname + inspect.formatargspec(*getargspec(obj))
394 hdef = oname + str(signature(obj))
394 395 return cast_unicode(hdef)
395 396 except:
396 397 return None
397 398
398 399 def __head(self,h):
399 400 """Return a header string with proper colors."""
400 401 return '%s%s%s' % (self.color_table.active_colors.header,h,
401 402 self.color_table.active_colors.normal)
402 403
403 404 def set_active_scheme(self, scheme):
404 405 self.color_table.set_active_scheme(scheme)
405 406 self.parser.color_table.set_active_scheme(scheme)
406 407
407 408 def noinfo(self, msg, oname):
408 409 """Generic message when no information is found."""
409 410 print('No %s found' % msg, end=' ')
410 411 if oname:
411 412 print('for %s' % oname)
412 413 else:
413 414 print()
414 415
415 416 def pdef(self, obj, oname=''):
416 417 """Print the call signature for any callable object.
417 418
418 419 If the object is a class, print the constructor information."""
419 420
420 421 if not callable(obj):
421 422 print('Object is not callable.')
422 423 return
423 424
424 425 header = ''
425 426
426 427 if inspect.isclass(obj):
427 428 header = self.__head('Class constructor information:\n')
428 429 obj = obj.__init__
429 430 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
430 431 obj = obj.__call__
431 432
432 433 output = self._getdef(obj,oname)
433 434 if output is None:
434 435 self.noinfo('definition header',oname)
435 436 else:
436 437 print(header,self.format(output), end=' ', file=io.stdout)
437 438
438 439 # In Python 3, all classes are new-style, so they all have __init__.
439 440 @skip_doctest_py3
440 441 def pdoc(self,obj,oname='',formatter = None):
441 442 """Print the docstring for any object.
442 443
443 444 Optional:
444 445 -formatter: a function to run the docstring through for specially
445 446 formatted docstrings.
446 447
447 448 Examples
448 449 --------
449 450
450 451 In [1]: class NoInit:
451 452 ...: pass
452 453
453 454 In [2]: class NoDoc:
454 455 ...: def __init__(self):
455 456 ...: pass
456 457
457 458 In [3]: %pdoc NoDoc
458 459 No documentation found for NoDoc
459 460
460 461 In [4]: %pdoc NoInit
461 462 No documentation found for NoInit
462 463
463 464 In [5]: obj = NoInit()
464 465
465 466 In [6]: %pdoc obj
466 467 No documentation found for obj
467 468
468 469 In [5]: obj2 = NoDoc()
469 470
470 471 In [6]: %pdoc obj2
471 472 No documentation found for obj2
472 473 """
473 474
474 475 head = self.__head # For convenience
475 476 lines = []
476 477 ds = getdoc(obj)
477 478 if formatter:
478 479 ds = formatter(ds)
479 480 if ds:
480 481 lines.append(head("Class docstring:"))
481 482 lines.append(indent(ds))
482 483 if inspect.isclass(obj) and hasattr(obj, '__init__'):
483 484 init_ds = getdoc(obj.__init__)
484 485 if init_ds is not None:
485 486 lines.append(head("Init docstring:"))
486 487 lines.append(indent(init_ds))
487 488 elif hasattr(obj,'__call__'):
488 489 call_ds = getdoc(obj.__call__)
489 490 if call_ds:
490 491 lines.append(head("Call docstring:"))
491 492 lines.append(indent(call_ds))
492 493
493 494 if not lines:
494 495 self.noinfo('documentation',oname)
495 496 else:
496 497 page.page('\n'.join(lines))
497 498
498 499 def psource(self, obj, oname=''):
499 500 """Print the source code for an object."""
500 501
501 502 # Flush the source cache because inspect can return out-of-date source
502 503 linecache.checkcache()
503 504 try:
504 505 src = getsource(obj, oname=oname)
505 506 except Exception:
506 507 src = None
507 508
508 509 if src is None:
509 510 self.noinfo('source', oname)
510 511 else:
511 512 page.page(self.format(src))
512 513
513 514 def pfile(self, obj, oname=''):
514 515 """Show the whole file where an object was defined."""
515 516
516 517 lineno = find_source_lines(obj)
517 518 if lineno is None:
518 519 self.noinfo('file', oname)
519 520 return
520 521
521 522 ofile = find_file(obj)
522 523 # run contents of file through pager starting at line where the object
523 524 # is defined, as long as the file isn't binary and is actually on the
524 525 # filesystem.
525 526 if ofile.endswith(('.so', '.dll', '.pyd')):
526 527 print('File %r is binary, not printing.' % ofile)
527 528 elif not os.path.isfile(ofile):
528 529 print('File %r does not exist, not printing.' % ofile)
529 530 else:
530 531 # Print only text files, not extension binaries. Note that
531 532 # getsourcelines returns lineno with 1-offset and page() uses
532 533 # 0-offset, so we must adjust.
533 534 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
534 535
535 536 def _format_fields(self, fields, title_width=0):
536 537 """Formats a list of fields for display.
537 538
538 539 Parameters
539 540 ----------
540 541 fields : list
541 542 A list of 2-tuples: (field_title, field_content)
542 543 title_width : int
543 544 How many characters to pad titles to. Default to longest title.
544 545 """
545 546 out = []
546 547 header = self.__head
547 548 if title_width == 0:
548 549 title_width = max(len(title) + 2 for title, _ in fields)
549 550 for title, content in fields:
550 551 if len(content.splitlines()) > 1:
551 552 title = header(title + ":") + "\n"
552 553 else:
553 554 title = header((title+":").ljust(title_width))
554 555 out.append(cast_unicode(title) + cast_unicode(content))
555 556 return "\n".join(out)
556 557
557 558 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
558 559 pinfo_fields1 = [("Type", "type_name"),
559 560 ]
560 561
561 562 pinfo_fields2 = [("String form", "string_form"),
562 563 ]
563 564
564 565 pinfo_fields3 = [("Length", "length"),
565 566 ("File", "file"),
566 567 ("Definition", "definition"),
567 568 ]
568 569
569 570 pinfo_fields_obj = [("Class docstring", "class_docstring"),
570 571 ("Init docstring", "init_docstring"),
571 572 ("Call def", "call_def"),
572 573 ("Call docstring", "call_docstring")]
573 574
574 575 def _format_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
575 576 """Format an info dict as text"""
576 577 info = self.info(obj, oname=oname, formatter=formatter,
577 578 info=info, detail_level=detail_level)
578 579 displayfields = []
579 580 def add_fields(fields):
580 581 for title, key in fields:
581 582 field = info[key]
582 583 if field is not None:
583 584 displayfields.append((title, field.rstrip()))
584 585
585 586 add_fields(self.pinfo_fields1)
586 587
587 588 # Base class for old-style instances
588 589 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
589 590 displayfields.append(("Base Class", info['base_class'].rstrip()))
590 591
591 592 add_fields(self.pinfo_fields2)
592 593
593 594 # Namespace
594 595 if info['namespace'] != 'Interactive':
595 596 displayfields.append(("Namespace", info['namespace'].rstrip()))
596 597
597 598 add_fields(self.pinfo_fields3)
598 599 if info['isclass'] and info['init_definition']:
599 600 displayfields.append(("Init definition",
600 601 info['init_definition'].rstrip()))
601 602
602 603 # Source or docstring, depending on detail level and whether
603 604 # source found.
604 605 if detail_level > 0 and info['source'] is not None:
605 606 displayfields.append(("Source",
606 607 self.format(cast_unicode(info['source']))))
607 608 elif info['docstring'] is not None:
608 609 displayfields.append(("Docstring", info["docstring"]))
609 610
610 611 # Constructor info for classes
611 612 if info['isclass']:
612 613 if info['init_docstring'] is not None:
613 614 displayfields.append(("Init docstring",
614 615 info['init_docstring']))
615 616
616 617 # Info for objects:
617 618 else:
618 619 add_fields(self.pinfo_fields_obj)
619 620
620 621 if displayfields:
621 622 return self._format_fields(displayfields)
622 623 else:
623 624 return u''
624 625
625 626 def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0):
626 627 """Show detailed information about an object.
627 628
628 629 Optional arguments:
629 630
630 631 - oname: name of the variable pointing to the object.
631 632
632 633 - formatter: special formatter for docstrings (see pdoc)
633 634
634 635 - info: a structure with some information fields which may have been
635 636 precomputed already.
636 637
637 638 - detail_level: if set to 1, more information is given.
638 639 """
639 640 text = self._format_info(obj, oname, formatter, info, detail_level)
640 641 if text:
641 642 page.page(text)
642 643
643 644 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
644 645 """Compute a dict with detailed information about an object.
645 646
646 647 Optional arguments:
647 648
648 649 - oname: name of the variable pointing to the object.
649 650
650 651 - formatter: special formatter for docstrings (see pdoc)
651 652
652 653 - info: a structure with some information fields which may have been
653 654 precomputed already.
654 655
655 656 - detail_level: if set to 1, more information is given.
656 657 """
657 658
658 659 obj_type = type(obj)
659 660
660 661 if info is None:
661 662 ismagic = 0
662 663 isalias = 0
663 664 ospace = ''
664 665 else:
665 666 ismagic = info.ismagic
666 667 isalias = info.isalias
667 668 ospace = info.namespace
668 669
669 670 # Get docstring, special-casing aliases:
670 671 if isalias:
671 672 if not callable(obj):
672 673 try:
673 674 ds = "Alias to the system command:\n %s" % obj[1]
674 675 except:
675 676 ds = "Alias: " + str(obj)
676 677 else:
677 678 ds = "Alias to " + str(obj)
678 679 if obj.__doc__:
679 680 ds += "\nDocstring:\n" + obj.__doc__
680 681 else:
681 682 ds = getdoc(obj)
682 683 if ds is None:
683 684 ds = '<no docstring>'
684 685 if formatter is not None:
685 686 ds = formatter(ds)
686 687
687 688 # store output in a dict, we initialize it here and fill it as we go
688 689 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
689 690
690 691 string_max = 200 # max size of strings to show (snipped if longer)
691 692 shalf = int((string_max -5)/2)
692 693
693 694 if ismagic:
694 695 obj_type_name = 'Magic function'
695 696 elif isalias:
696 697 obj_type_name = 'System alias'
697 698 else:
698 699 obj_type_name = obj_type.__name__
699 700 out['type_name'] = obj_type_name
700 701
701 702 try:
702 703 bclass = obj.__class__
703 704 out['base_class'] = str(bclass)
704 705 except: pass
705 706
706 707 # String form, but snip if too long in ? form (full in ??)
707 708 if detail_level >= self.str_detail_level:
708 709 try:
709 710 ostr = str(obj)
710 711 str_head = 'string_form'
711 712 if not detail_level and len(ostr)>string_max:
712 713 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
713 714 ostr = ("\n" + " " * len(str_head.expandtabs())).\
714 715 join(q.strip() for q in ostr.split("\n"))
715 716 out[str_head] = ostr
716 717 except:
717 718 pass
718 719
719 720 if ospace:
720 721 out['namespace'] = ospace
721 722
722 723 # Length (for strings and lists)
723 724 try:
724 725 out['length'] = str(len(obj))
725 726 except: pass
726 727
727 728 # Filename where object was defined
728 729 binary_file = False
729 730 fname = find_file(obj)
730 731 if fname is None:
731 732 # if anything goes wrong, we don't want to show source, so it's as
732 733 # if the file was binary
733 734 binary_file = True
734 735 else:
735 736 if fname.endswith(('.so', '.dll', '.pyd')):
736 737 binary_file = True
737 738 elif fname.endswith('<string>'):
738 739 fname = 'Dynamically generated function. No source code available.'
739 740 out['file'] = fname
740 741
741 742 # Original source code for a callable, class or property.
742 743 if detail_level:
743 744 # Flush the source cache because inspect can return out-of-date
744 745 # source
745 746 linecache.checkcache()
746 747 try:
747 748 if isinstance(obj, property) or not binary_file:
748 749 src = getsource(obj, oname)
749 750 if src is not None:
750 751 src = src.rstrip()
751 752 out['source'] = src
752 753
753 754 except Exception:
754 755 pass
755 756
756 757 # Add docstring only if no source is to be shown (avoid repetitions).
757 758 if ds and out.get('source', None) is None:
758 759 out['docstring'] = ds
759 760
760 761 # Constructor docstring for classes
761 762 if inspect.isclass(obj):
762 763 out['isclass'] = True
763 764 # reconstruct the function definition and print it:
764 765 try:
765 766 obj_init = obj.__init__
766 767 except AttributeError:
767 768 init_def = init_ds = None
768 769 else:
769 770 init_def = self._getdef(obj_init,oname)
770 771 init_ds = getdoc(obj_init)
771 772 # Skip Python's auto-generated docstrings
772 773 if init_ds == _object_init_docstring:
773 774 init_ds = None
774 775
775 776 if init_def or init_ds:
776 777 if init_def:
777 778 out['init_definition'] = self.format(init_def)
778 779 if init_ds:
779 780 out['init_docstring'] = init_ds
780 781
781 782 # and class docstring for instances:
782 783 else:
783 784 # reconstruct the function definition and print it:
784 785 defln = self._getdef(obj, oname)
785 786 if defln:
786 787 out['definition'] = self.format(defln)
787 788
788 789 # First, check whether the instance docstring is identical to the
789 790 # class one, and print it separately if they don't coincide. In
790 791 # most cases they will, but it's nice to print all the info for
791 792 # objects which use instance-customized docstrings.
792 793 if ds:
793 794 try:
794 795 cls = getattr(obj,'__class__')
795 796 except:
796 797 class_ds = None
797 798 else:
798 799 class_ds = getdoc(cls)
799 800 # Skip Python's auto-generated docstrings
800 801 if class_ds in _builtin_type_docstrings:
801 802 class_ds = None
802 803 if class_ds and ds != class_ds:
803 804 out['class_docstring'] = class_ds
804 805
805 806 # Next, try to show constructor docstrings
806 807 try:
807 808 init_ds = getdoc(obj.__init__)
808 809 # Skip Python's auto-generated docstrings
809 810 if init_ds == _object_init_docstring:
810 811 init_ds = None
811 812 except AttributeError:
812 813 init_ds = None
813 814 if init_ds:
814 815 out['init_docstring'] = init_ds
815 816
816 817 # Call form docstring for callable instances
817 818 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
818 819 call_def = self._getdef(obj.__call__, oname)
819 820 if call_def:
820 821 call_def = self.format(call_def)
821 822 # it may never be the case that call def and definition differ,
822 823 # but don't include the same signature twice
823 824 if call_def != out.get('definition'):
824 825 out['call_def'] = call_def
825 826 call_ds = getdoc(obj.__call__)
826 827 # Skip Python's auto-generated docstrings
827 828 if call_ds == _func_call_docstring:
828 829 call_ds = None
829 830 if call_ds:
830 831 out['call_docstring'] = call_ds
831 832
832 833 # Compute the object's argspec as a callable. The key is to decide
833 834 # whether to pull it from the object itself, from its __init__ or
834 835 # from its __call__ method.
835 836
836 837 if inspect.isclass(obj):
837 838 # Old-style classes need not have an __init__
838 839 callable_obj = getattr(obj, "__init__", None)
839 840 elif callable(obj):
840 841 callable_obj = obj
841 842 else:
842 843 callable_obj = None
843 844
844 845 if callable_obj:
845 846 try:
846 847 argspec = getargspec(callable_obj)
847 848 except (TypeError, AttributeError):
848 849 # For extensions/builtins we can't retrieve the argspec
849 850 pass
850 851 else:
851 852 # named tuples' _asdict() method returns an OrderedDict, but we
852 853 # we want a normal
853 854 out['argspec'] = argspec_dict = dict(argspec._asdict())
854 855 # We called this varkw before argspec became a named tuple.
855 856 # With getfullargspec it's also called varkw.
856 857 if 'varkw' not in argspec_dict:
857 858 argspec_dict['varkw'] = argspec_dict.pop('keywords')
858 859
859 860 return object_info(**out)
860 861
861 862 def psearch(self,pattern,ns_table,ns_search=[],
862 863 ignore_case=False,show_all=False):
863 864 """Search namespaces with wildcards for objects.
864 865
865 866 Arguments:
866 867
867 868 - pattern: string containing shell-like wildcards to use in namespace
868 869 searches and optionally a type specification to narrow the search to
869 870 objects of that type.
870 871
871 872 - ns_table: dict of name->namespaces for search.
872 873
873 874 Optional arguments:
874 875
875 876 - ns_search: list of namespace names to include in search.
876 877
877 878 - ignore_case(False): make the search case-insensitive.
878 879
879 880 - show_all(False): show all names, including those starting with
880 881 underscores.
881 882 """
882 883 #print 'ps pattern:<%r>' % pattern # dbg
883 884
884 885 # defaults
885 886 type_pattern = 'all'
886 887 filter = ''
887 888
888 889 cmds = pattern.split()
889 890 len_cmds = len(cmds)
890 891 if len_cmds == 1:
891 892 # Only filter pattern given
892 893 filter = cmds[0]
893 894 elif len_cmds == 2:
894 895 # Both filter and type specified
895 896 filter,type_pattern = cmds
896 897 else:
897 898 raise ValueError('invalid argument string for psearch: <%s>' %
898 899 pattern)
899 900
900 901 # filter search namespaces
901 902 for name in ns_search:
902 903 if name not in ns_table:
903 904 raise ValueError('invalid namespace <%s>. Valid names: %s' %
904 905 (name,ns_table.keys()))
905 906
906 907 #print 'type_pattern:',type_pattern # dbg
907 908 search_result, namespaces_seen = set(), set()
908 909 for ns_name in ns_search:
909 910 ns = ns_table[ns_name]
910 911 # Normally, locals and globals are the same, so we just check one.
911 912 if id(ns) in namespaces_seen:
912 913 continue
913 914 namespaces_seen.add(id(ns))
914 915 tmp_res = list_namespace(ns, type_pattern, filter,
915 916 ignore_case=ignore_case, show_all=show_all)
916 917 search_result.update(tmp_res)
917 918
918 919 page.page('\n'.join(sorted(search_result)))
@@ -1,813 +1,816 b''
1 1 """Function signature objects for callables.
2 2
3 3 Back port of Python 3.3's function signature tools from the inspect module,
4 4 modified to be compatible with Python 2.7 and 3.2+.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Python 3.3 stdlib inspect.py is public domain
9 9 #
10 10 # Backports Copyright (C) 2013 Aaron Iles
11 11 # Used under Apache License Version 2.0
12 12 #
13 13 # Further Changes are Copyright (C) 2013 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 from __future__ import absolute_import, division, print_function
20 20 import itertools
21 21 import functools
22 22 import re
23 23 import types
24 24
25 25
26 26 # patch for single-file
27 27 # we don't support 2.6, so we can just import OrderedDict
28 28 from collections import OrderedDict
29 29
30 30 __version__ = '0.3'
31 31 # end patch
32 32
33 33 __all__ = ['BoundArguments', 'Parameter', 'Signature', 'signature']
34 34
35 35
36 36 _WrapperDescriptor = type(type.__call__)
37 37 _MethodWrapper = type(all.__call__)
38 38
39 39 _NonUserDefinedCallables = (_WrapperDescriptor,
40 40 _MethodWrapper,
41 41 types.BuiltinFunctionType)
42 42
43 43
44 44 def formatannotation(annotation, base_module=None):
45 45 if isinstance(annotation, type):
46 46 if annotation.__module__ in ('builtins', '__builtin__', base_module):
47 47 return annotation.__name__
48 48 return annotation.__module__+'.'+annotation.__name__
49 49 return repr(annotation)
50 50
51 51
52 52 def _get_user_defined_method(cls, method_name, *nested):
53 53 try:
54 54 if cls is type:
55 55 return
56 56 meth = getattr(cls, method_name)
57 57 for name in nested:
58 58 meth = getattr(meth, name, meth)
59 59 except AttributeError:
60 60 return
61 61 else:
62 62 if not isinstance(meth, _NonUserDefinedCallables):
63 63 # Once '__signature__' will be added to 'C'-level
64 64 # callables, this check won't be necessary
65 65 return meth
66 66
67 67
68 68 def signature(obj):
69 69 '''Get a signature object for the passed callable.'''
70 70
71 71 if not callable(obj):
72 72 raise TypeError('{0!r} is not a callable object'.format(obj))
73 73
74 74 if isinstance(obj, types.MethodType):
75 # In this case we skip the first parameter of the underlying
76 # function (usually `self` or `cls`).
77 sig = signature(obj.__func__)
78 return sig.replace(parameters=tuple(sig.parameters.values())[1:])
75 if obj.__self__ is None:
76 # Unbound method - treat it as a function (no distinction in Py 3)
77 obj = obj.__func__
78 else:
79 # Bound method: trim off the first parameter (typically self or cls)
80 sig = signature(obj.__func__)
81 return sig.replace(parameters=tuple(sig.parameters.values())[1:])
79 82
80 83 try:
81 84 sig = obj.__signature__
82 85 except AttributeError:
83 86 pass
84 87 else:
85 88 if sig is not None:
86 89 return sig
87 90
88 91 try:
89 92 # Was this function wrapped by a decorator?
90 93 wrapped = obj.__wrapped__
91 94 except AttributeError:
92 95 pass
93 96 else:
94 97 return signature(wrapped)
95 98
96 99 if isinstance(obj, types.FunctionType):
97 100 return Signature.from_function(obj)
98 101
99 102 if isinstance(obj, functools.partial):
100 103 sig = signature(obj.func)
101 104
102 105 new_params = OrderedDict(sig.parameters.items())
103 106
104 107 partial_args = obj.args or ()
105 108 partial_keywords = obj.keywords or {}
106 109 try:
107 110 ba = sig.bind_partial(*partial_args, **partial_keywords)
108 111 except TypeError as ex:
109 112 msg = 'partial object {0!r} has incorrect arguments'.format(obj)
110 113 raise ValueError(msg)
111 114
112 115 for arg_name, arg_value in ba.arguments.items():
113 116 param = new_params[arg_name]
114 117 if arg_name in partial_keywords:
115 118 # We set a new default value, because the following code
116 119 # is correct:
117 120 #
118 121 # >>> def foo(a): print(a)
119 122 # >>> print(partial(partial(foo, a=10), a=20)())
120 123 # 20
121 124 # >>> print(partial(partial(foo, a=10), a=20)(a=30))
122 125 # 30
123 126 #
124 127 # So, with 'partial' objects, passing a keyword argument is
125 128 # like setting a new default value for the corresponding
126 129 # parameter
127 130 #
128 131 # We also mark this parameter with '_partial_kwarg'
129 132 # flag. Later, in '_bind', the 'default' value of this
130 133 # parameter will be added to 'kwargs', to simulate
131 134 # the 'functools.partial' real call.
132 135 new_params[arg_name] = param.replace(default=arg_value,
133 136 _partial_kwarg=True)
134 137
135 138 elif (param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and
136 139 not param._partial_kwarg):
137 140 new_params.pop(arg_name)
138 141
139 142 return sig.replace(parameters=new_params.values())
140 143
141 144 sig = None
142 145 if isinstance(obj, type):
143 146 # obj is a class or a metaclass
144 147
145 148 # First, let's see if it has an overloaded __call__ defined
146 149 # in its metaclass
147 150 call = _get_user_defined_method(type(obj), '__call__')
148 151 if call is not None:
149 152 sig = signature(call)
150 153 else:
151 154 # Now we check if the 'obj' class has a '__new__' method
152 155 new = _get_user_defined_method(obj, '__new__')
153 156 if new is not None:
154 157 sig = signature(new)
155 158 else:
156 159 # Finally, we should have at least __init__ implemented
157 160 init = _get_user_defined_method(obj, '__init__')
158 161 if init is not None:
159 162 sig = signature(init)
160 163 elif not isinstance(obj, _NonUserDefinedCallables):
161 164 # An object with __call__
162 165 # We also check that the 'obj' is not an instance of
163 166 # _WrapperDescriptor or _MethodWrapper to avoid
164 167 # infinite recursion (and even potential segfault)
165 168 call = _get_user_defined_method(type(obj), '__call__', 'im_func')
166 169 if call is not None:
167 170 sig = signature(call)
168 171
169 172 if sig is not None:
170 173 return sig
171 174
172 175 if isinstance(obj, types.BuiltinFunctionType):
173 176 # Raise a nicer error message for builtins
174 177 msg = 'no signature found for builtin function {0!r}'.format(obj)
175 178 raise ValueError(msg)
176 179
177 180 raise ValueError('callable {0!r} is not supported by signature'.format(obj))
178 181
179 182
180 183 class _void(object):
181 184 '''A private marker - used in Parameter & Signature'''
182 185
183 186
184 187 class _empty(object):
185 188 pass
186 189
187 190
188 191 class _ParameterKind(int):
189 192 def __new__(self, *args, **kwargs):
190 193 obj = int.__new__(self, *args)
191 194 obj._name = kwargs['name']
192 195 return obj
193 196
194 197 def __str__(self):
195 198 return self._name
196 199
197 200 def __repr__(self):
198 201 return '<_ParameterKind: {0!r}>'.format(self._name)
199 202
200 203
201 204 _POSITIONAL_ONLY = _ParameterKind(0, name='POSITIONAL_ONLY')
202 205 _POSITIONAL_OR_KEYWORD = _ParameterKind(1, name='POSITIONAL_OR_KEYWORD')
203 206 _VAR_POSITIONAL = _ParameterKind(2, name='VAR_POSITIONAL')
204 207 _KEYWORD_ONLY = _ParameterKind(3, name='KEYWORD_ONLY')
205 208 _VAR_KEYWORD = _ParameterKind(4, name='VAR_KEYWORD')
206 209
207 210
208 211 class Parameter(object):
209 212 '''Represents a parameter in a function signature.
210 213
211 214 Has the following public attributes:
212 215
213 216 * name : str
214 217 The name of the parameter as a string.
215 218 * default : object
216 219 The default value for the parameter if specified. If the
217 220 parameter has no default value, this attribute is not set.
218 221 * annotation
219 222 The annotation for the parameter if specified. If the
220 223 parameter has no annotation, this attribute is not set.
221 224 * kind : str
222 225 Describes how argument values are bound to the parameter.
223 226 Possible values: `Parameter.POSITIONAL_ONLY`,
224 227 `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`,
225 228 `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
226 229 '''
227 230
228 231 __slots__ = ('_name', '_kind', '_default', '_annotation', '_partial_kwarg')
229 232
230 233 POSITIONAL_ONLY = _POSITIONAL_ONLY
231 234 POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
232 235 VAR_POSITIONAL = _VAR_POSITIONAL
233 236 KEYWORD_ONLY = _KEYWORD_ONLY
234 237 VAR_KEYWORD = _VAR_KEYWORD
235 238
236 239 empty = _empty
237 240
238 241 def __init__(self, name, kind, default=_empty, annotation=_empty,
239 242 _partial_kwarg=False):
240 243
241 244 if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
242 245 _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
243 246 raise ValueError("invalid value for 'Parameter.kind' attribute")
244 247 self._kind = kind
245 248
246 249 if default is not _empty:
247 250 if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
248 251 msg = '{0} parameters cannot have default values'.format(kind)
249 252 raise ValueError(msg)
250 253 self._default = default
251 254 self._annotation = annotation
252 255
253 256 if name is None:
254 257 if kind != _POSITIONAL_ONLY:
255 258 raise ValueError("None is not a valid name for a "
256 259 "non-positional-only parameter")
257 260 self._name = name
258 261 else:
259 262 name = str(name)
260 263 if kind != _POSITIONAL_ONLY and not re.match(r'[a-z_]\w*$', name, re.I):
261 264 msg = '{0!r} is not a valid parameter name'.format(name)
262 265 raise ValueError(msg)
263 266 self._name = name
264 267
265 268 self._partial_kwarg = _partial_kwarg
266 269
267 270 @property
268 271 def name(self):
269 272 return self._name
270 273
271 274 @property
272 275 def default(self):
273 276 return self._default
274 277
275 278 @property
276 279 def annotation(self):
277 280 return self._annotation
278 281
279 282 @property
280 283 def kind(self):
281 284 return self._kind
282 285
283 286 def replace(self, name=_void, kind=_void, annotation=_void,
284 287 default=_void, _partial_kwarg=_void):
285 288 '''Creates a customized copy of the Parameter.'''
286 289
287 290 if name is _void:
288 291 name = self._name
289 292
290 293 if kind is _void:
291 294 kind = self._kind
292 295
293 296 if annotation is _void:
294 297 annotation = self._annotation
295 298
296 299 if default is _void:
297 300 default = self._default
298 301
299 302 if _partial_kwarg is _void:
300 303 _partial_kwarg = self._partial_kwarg
301 304
302 305 return type(self)(name, kind, default=default, annotation=annotation,
303 306 _partial_kwarg=_partial_kwarg)
304 307
305 308 def __str__(self):
306 309 kind = self.kind
307 310
308 311 formatted = self._name
309 312 if kind == _POSITIONAL_ONLY:
310 313 if formatted is None:
311 314 formatted = ''
312 315 formatted = '<{0}>'.format(formatted)
313 316
314 317 # Add annotation and default value
315 318 if self._annotation is not _empty:
316 319 formatted = '{0}:{1}'.format(formatted,
317 320 formatannotation(self._annotation))
318 321
319 322 if self._default is not _empty:
320 323 formatted = '{0}={1}'.format(formatted, repr(self._default))
321 324
322 325 if kind == _VAR_POSITIONAL:
323 326 formatted = '*' + formatted
324 327 elif kind == _VAR_KEYWORD:
325 328 formatted = '**' + formatted
326 329
327 330 return formatted
328 331
329 332 def __repr__(self):
330 333 return '<{0} at {1:#x} {2!r}>'.format(self.__class__.__name__,
331 334 id(self), self.name)
332 335
333 336 def __hash__(self):
334 337 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
335 338 raise TypeError(msg)
336 339
337 340 def __eq__(self, other):
338 341 return (issubclass(other.__class__, Parameter) and
339 342 self._name == other._name and
340 343 self._kind == other._kind and
341 344 self._default == other._default and
342 345 self._annotation == other._annotation)
343 346
344 347 def __ne__(self, other):
345 348 return not self.__eq__(other)
346 349
347 350
348 351 class BoundArguments(object):
349 352 '''Result of :meth:`Signature.bind` call. Holds the mapping of arguments
350 353 to the function's parameters.
351 354
352 355 Has the following public attributes:
353 356
354 357 arguments : :class:`collections.OrderedDict`
355 358 An ordered mutable mapping of parameters' names to arguments' values.
356 359 Does not contain arguments' default values.
357 360 signature : :class:`Signature`
358 361 The Signature object that created this instance.
359 362 args : tuple
360 363 Tuple of positional arguments values.
361 364 kwargs : dict
362 365 Dict of keyword arguments values.
363 366 '''
364 367
365 368 def __init__(self, signature, arguments):
366 369 self.arguments = arguments
367 370 self._signature = signature
368 371
369 372 @property
370 373 def signature(self):
371 374 return self._signature
372 375
373 376 @property
374 377 def args(self):
375 378 args = []
376 379 for param_name, param in self._signature.parameters.items():
377 380 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
378 381 param._partial_kwarg):
379 382 # Keyword arguments mapped by 'functools.partial'
380 383 # (Parameter._partial_kwarg is True) are mapped
381 384 # in 'BoundArguments.kwargs', along with VAR_KEYWORD &
382 385 # KEYWORD_ONLY
383 386 break
384 387
385 388 try:
386 389 arg = self.arguments[param_name]
387 390 except KeyError:
388 391 # We're done here. Other arguments
389 392 # will be mapped in 'BoundArguments.kwargs'
390 393 break
391 394 else:
392 395 if param.kind == _VAR_POSITIONAL:
393 396 # *args
394 397 args.extend(arg)
395 398 else:
396 399 # plain argument
397 400 args.append(arg)
398 401
399 402 return tuple(args)
400 403
401 404 @property
402 405 def kwargs(self):
403 406 kwargs = {}
404 407 kwargs_started = False
405 408 for param_name, param in self._signature.parameters.items():
406 409 if not kwargs_started:
407 410 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
408 411 param._partial_kwarg):
409 412 kwargs_started = True
410 413 else:
411 414 if param_name not in self.arguments:
412 415 kwargs_started = True
413 416 continue
414 417
415 418 if not kwargs_started:
416 419 continue
417 420
418 421 try:
419 422 arg = self.arguments[param_name]
420 423 except KeyError:
421 424 pass
422 425 else:
423 426 if param.kind == _VAR_KEYWORD:
424 427 # **kwargs
425 428 kwargs.update(arg)
426 429 else:
427 430 # plain keyword argument
428 431 kwargs[param_name] = arg
429 432
430 433 return kwargs
431 434
432 435 def __hash__(self):
433 436 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
434 437 raise TypeError(msg)
435 438
436 439 def __eq__(self, other):
437 440 return (issubclass(other.__class__, BoundArguments) and
438 441 self.signature == other.signature and
439 442 self.arguments == other.arguments)
440 443
441 444 def __ne__(self, other):
442 445 return not self.__eq__(other)
443 446
444 447
445 448 class Signature(object):
446 449 '''A Signature object represents the overall signature of a function.
447 450 It stores a Parameter object for each parameter accepted by the
448 451 function, as well as information specific to the function itself.
449 452
450 453 A Signature object has the following public attributes:
451 454
452 455 parameters : :class:`collections.OrderedDict`
453 456 An ordered mapping of parameters' names to the corresponding
454 457 Parameter objects (keyword-only arguments are in the same order
455 458 as listed in `code.co_varnames`).
456 459 return_annotation
457 460 The annotation for the return type of the function if specified.
458 461 If the function has no annotation for its return type, this
459 462 attribute is not set.
460 463 '''
461 464
462 465 __slots__ = ('_return_annotation', '_parameters')
463 466
464 467 _parameter_cls = Parameter
465 468 _bound_arguments_cls = BoundArguments
466 469
467 470 empty = _empty
468 471
469 472 def __init__(self, parameters=None, return_annotation=_empty,
470 473 __validate_parameters__=True):
471 474 '''Constructs Signature from the given list of Parameter
472 475 objects and 'return_annotation'. All arguments are optional.
473 476 '''
474 477
475 478 if parameters is None:
476 479 params = OrderedDict()
477 480 else:
478 481 if __validate_parameters__:
479 482 params = OrderedDict()
480 483 top_kind = _POSITIONAL_ONLY
481 484
482 485 for idx, param in enumerate(parameters):
483 486 kind = param.kind
484 487 if kind < top_kind:
485 488 msg = 'wrong parameter order: {0} before {1}'
486 489 msg = msg.format(top_kind, param.kind)
487 490 raise ValueError(msg)
488 491 else:
489 492 top_kind = kind
490 493
491 494 name = param.name
492 495 if name is None:
493 496 name = str(idx)
494 497 param = param.replace(name=name)
495 498
496 499 if name in params:
497 500 msg = 'duplicate parameter name: {0!r}'.format(name)
498 501 raise ValueError(msg)
499 502 params[name] = param
500 503 else:
501 504 params = OrderedDict(((param.name, param)
502 505 for param in parameters))
503 506
504 507 self._parameters = params
505 508 self._return_annotation = return_annotation
506 509
507 510 @classmethod
508 511 def from_function(cls, func):
509 512 '''Constructs Signature for the given python function'''
510 513
511 514 if not isinstance(func, types.FunctionType):
512 515 raise TypeError('{0!r} is not a Python function'.format(func))
513 516
514 517 Parameter = cls._parameter_cls
515 518
516 519 # Parameter information.
517 520 func_code = func.__code__
518 521 pos_count = func_code.co_argcount
519 522 arg_names = func_code.co_varnames
520 523 positional = tuple(arg_names[:pos_count])
521 524 keyword_only_count = getattr(func_code, 'co_kwonlyargcount', 0)
522 525 keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
523 526 annotations = getattr(func, '__annotations__', {})
524 527 defaults = func.__defaults__
525 528 kwdefaults = getattr(func, '__kwdefaults__', None)
526 529
527 530 if defaults:
528 531 pos_default_count = len(defaults)
529 532 else:
530 533 pos_default_count = 0
531 534
532 535 parameters = []
533 536
534 537 # Non-keyword-only parameters w/o defaults.
535 538 non_default_count = pos_count - pos_default_count
536 539 for name in positional[:non_default_count]:
537 540 annotation = annotations.get(name, _empty)
538 541 parameters.append(Parameter(name, annotation=annotation,
539 542 kind=_POSITIONAL_OR_KEYWORD))
540 543
541 544 # ... w/ defaults.
542 545 for offset, name in enumerate(positional[non_default_count:]):
543 546 annotation = annotations.get(name, _empty)
544 547 parameters.append(Parameter(name, annotation=annotation,
545 548 kind=_POSITIONAL_OR_KEYWORD,
546 549 default=defaults[offset]))
547 550
548 551 # *args
549 552 if func_code.co_flags & 0x04:
550 553 name = arg_names[pos_count + keyword_only_count]
551 554 annotation = annotations.get(name, _empty)
552 555 parameters.append(Parameter(name, annotation=annotation,
553 556 kind=_VAR_POSITIONAL))
554 557
555 558 # Keyword-only parameters.
556 559 for name in keyword_only:
557 560 default = _empty
558 561 if kwdefaults is not None:
559 562 default = kwdefaults.get(name, _empty)
560 563
561 564 annotation = annotations.get(name, _empty)
562 565 parameters.append(Parameter(name, annotation=annotation,
563 566 kind=_KEYWORD_ONLY,
564 567 default=default))
565 568 # **kwargs
566 569 if func_code.co_flags & 0x08:
567 570 index = pos_count + keyword_only_count
568 571 if func_code.co_flags & 0x04:
569 572 index += 1
570 573
571 574 name = arg_names[index]
572 575 annotation = annotations.get(name, _empty)
573 576 parameters.append(Parameter(name, annotation=annotation,
574 577 kind=_VAR_KEYWORD))
575 578
576 579 return cls(parameters,
577 580 return_annotation=annotations.get('return', _empty),
578 581 __validate_parameters__=False)
579 582
580 583 @property
581 584 def parameters(self):
582 585 try:
583 586 return types.MappingProxyType(self._parameters)
584 587 except AttributeError:
585 588 return OrderedDict(self._parameters.items())
586 589
587 590 @property
588 591 def return_annotation(self):
589 592 return self._return_annotation
590 593
591 594 def replace(self, parameters=_void, return_annotation=_void):
592 595 '''Creates a customized copy of the Signature.
593 596 Pass 'parameters' and/or 'return_annotation' arguments
594 597 to override them in the new copy.
595 598 '''
596 599
597 600 if parameters is _void:
598 601 parameters = self.parameters.values()
599 602
600 603 if return_annotation is _void:
601 604 return_annotation = self._return_annotation
602 605
603 606 return type(self)(parameters,
604 607 return_annotation=return_annotation)
605 608
606 609 def __hash__(self):
607 610 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
608 611 raise TypeError(msg)
609 612
610 613 def __eq__(self, other):
611 614 if (not issubclass(type(other), Signature) or
612 615 self.return_annotation != other.return_annotation or
613 616 len(self.parameters) != len(other.parameters)):
614 617 return False
615 618
616 619 other_positions = dict((param, idx)
617 620 for idx, param in enumerate(other.parameters.keys()))
618 621
619 622 for idx, (param_name, param) in enumerate(self.parameters.items()):
620 623 if param.kind == _KEYWORD_ONLY:
621 624 try:
622 625 other_param = other.parameters[param_name]
623 626 except KeyError:
624 627 return False
625 628 else:
626 629 if param != other_param:
627 630 return False
628 631 else:
629 632 try:
630 633 other_idx = other_positions[param_name]
631 634 except KeyError:
632 635 return False
633 636 else:
634 637 if (idx != other_idx or
635 638 param != other.parameters[param_name]):
636 639 return False
637 640
638 641 return True
639 642
640 643 def __ne__(self, other):
641 644 return not self.__eq__(other)
642 645
643 646 def _bind(self, args, kwargs, partial=False):
644 647 '''Private method. Don't use directly.'''
645 648
646 649 arguments = OrderedDict()
647 650
648 651 parameters = iter(self.parameters.values())
649 652 parameters_ex = ()
650 653 arg_vals = iter(args)
651 654
652 655 if partial:
653 656 # Support for binding arguments to 'functools.partial' objects.
654 657 # See 'functools.partial' case in 'signature()' implementation
655 658 # for details.
656 659 for param_name, param in self.parameters.items():
657 660 if (param._partial_kwarg and param_name not in kwargs):
658 661 # Simulating 'functools.partial' behavior
659 662 kwargs[param_name] = param.default
660 663
661 664 while True:
662 665 # Let's iterate through the positional arguments and corresponding
663 666 # parameters
664 667 try:
665 668 arg_val = next(arg_vals)
666 669 except StopIteration:
667 670 # No more positional arguments
668 671 try:
669 672 param = next(parameters)
670 673 except StopIteration:
671 674 # No more parameters. That's it. Just need to check that
672 675 # we have no `kwargs` after this while loop
673 676 break
674 677 else:
675 678 if param.kind == _VAR_POSITIONAL:
676 679 # That's OK, just empty *args. Let's start parsing
677 680 # kwargs
678 681 break
679 682 elif param.name in kwargs:
680 683 if param.kind == _POSITIONAL_ONLY:
681 684 msg = '{arg!r} parameter is positional only, ' \
682 685 'but was passed as a keyword'
683 686 msg = msg.format(arg=param.name)
684 687 raise TypeError(msg)
685 688 parameters_ex = (param,)
686 689 break
687 690 elif (param.kind == _VAR_KEYWORD or
688 691 param.default is not _empty):
689 692 # That's fine too - we have a default value for this
690 693 # parameter. So, lets start parsing `kwargs`, starting
691 694 # with the current parameter
692 695 parameters_ex = (param,)
693 696 break
694 697 else:
695 698 if partial:
696 699 parameters_ex = (param,)
697 700 break
698 701 else:
699 702 msg = '{arg!r} parameter lacking default value'
700 703 msg = msg.format(arg=param.name)
701 704 raise TypeError(msg)
702 705 else:
703 706 # We have a positional argument to process
704 707 try:
705 708 param = next(parameters)
706 709 except StopIteration:
707 710 raise TypeError('too many positional arguments')
708 711 else:
709 712 if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
710 713 # Looks like we have no parameter for this positional
711 714 # argument
712 715 raise TypeError('too many positional arguments')
713 716
714 717 if param.kind == _VAR_POSITIONAL:
715 718 # We have an '*args'-like argument, let's fill it with
716 719 # all positional arguments we have left and move on to
717 720 # the next phase
718 721 values = [arg_val]
719 722 values.extend(arg_vals)
720 723 arguments[param.name] = tuple(values)
721 724 break
722 725
723 726 if param.name in kwargs:
724 727 raise TypeError('multiple values for argument '
725 728 '{arg!r}'.format(arg=param.name))
726 729
727 730 arguments[param.name] = arg_val
728 731
729 732 # Now, we iterate through the remaining parameters to process
730 733 # keyword arguments
731 734 kwargs_param = None
732 735 for param in itertools.chain(parameters_ex, parameters):
733 736 if param.kind == _POSITIONAL_ONLY:
734 737 # This should never happen in case of a properly built
735 738 # Signature object (but let's have this check here
736 739 # to ensure correct behaviour just in case)
737 740 raise TypeError('{arg!r} parameter is positional only, '
738 741 'but was passed as a keyword'. \
739 742 format(arg=param.name))
740 743
741 744 if param.kind == _VAR_KEYWORD:
742 745 # Memorize that we have a '**kwargs'-like parameter
743 746 kwargs_param = param
744 747 continue
745 748
746 749 param_name = param.name
747 750 try:
748 751 arg_val = kwargs.pop(param_name)
749 752 except KeyError:
750 753 # We have no value for this parameter. It's fine though,
751 754 # if it has a default value, or it is an '*args'-like
752 755 # parameter, left alone by the processing of positional
753 756 # arguments.
754 757 if (not partial and param.kind != _VAR_POSITIONAL and
755 758 param.default is _empty):
756 759 raise TypeError('{arg!r} parameter lacking default value'. \
757 760 format(arg=param_name))
758 761
759 762 else:
760 763 arguments[param_name] = arg_val
761 764
762 765 if kwargs:
763 766 if kwargs_param is not None:
764 767 # Process our '**kwargs'-like parameter
765 768 arguments[kwargs_param.name] = kwargs
766 769 else:
767 770 raise TypeError('too many keyword arguments')
768 771
769 772 return self._bound_arguments_cls(self, arguments)
770 773
771 774 def bind(self, *args, **kwargs):
772 775 '''Get a :class:`BoundArguments` object, that maps the passed `args`
773 776 and `kwargs` to the function's signature. Raises :exc:`TypeError`
774 777 if the passed arguments can not be bound.
775 778 '''
776 779 return self._bind(args, kwargs)
777 780
778 781 def bind_partial(self, *args, **kwargs):
779 782 '''Get a :class:`BoundArguments` object, that partially maps the
780 783 passed `args` and `kwargs` to the function's signature.
781 784 Raises :exc:`TypeError` if the passed arguments can not be bound.
782 785 '''
783 786 return self._bind(args, kwargs, partial=True)
784 787
785 788 def __str__(self):
786 789 result = []
787 790 render_kw_only_separator = True
788 791 for idx, param in enumerate(self.parameters.values()):
789 792 formatted = str(param)
790 793
791 794 kind = param.kind
792 795 if kind == _VAR_POSITIONAL:
793 796 # OK, we have an '*args'-like parameter, so we won't need
794 797 # a '*' to separate keyword-only arguments
795 798 render_kw_only_separator = False
796 799 elif kind == _KEYWORD_ONLY and render_kw_only_separator:
797 800 # We have a keyword-only parameter to render and we haven't
798 801 # rendered an '*args'-like parameter before, so add a '*'
799 802 # separator to the parameters list ("foo(arg1, *, arg2)" case)
800 803 result.append('*')
801 804 # This condition should be only triggered once, so
802 805 # reset the flag
803 806 render_kw_only_separator = False
804 807
805 808 result.append(formatted)
806 809
807 810 rendered = '({0})'.format(', '.join(result))
808 811
809 812 if self.return_annotation is not _empty:
810 813 anno = formatannotation(self.return_annotation)
811 814 rendered += ' -> {0}'.format(anno)
812 815
813 816 return rendered
General Comments 0
You need to be logged in to leave comments. Login now