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