##// END OF EJS Templates
Merge pull request #8823 from anntzer/use-stdlib-signature-if-available...
Matthias Bussonnier -
r21666:d77341c5 merge
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (817 lines changed) Show them Hide them
@@ -0,0 +1,817 b''
1 """Function signature objects for callables.
2
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+.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Python 3.3 stdlib inspect.py is public domain
9 #
10 # Backports Copyright (C) 2013 Aaron Iles
11 # Used under Apache License Version 2.0
12 #
13 # Further Changes are Copyright (C) 2013 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 from __future__ import absolute_import, division, print_function
20 import itertools
21 import functools
22 import re
23 import types
24
25
26 # patch for single-file
27 # we don't support 2.6, so we can just import OrderedDict
28 from collections import OrderedDict
29
30 __version__ = '0.3'
31 # end patch
32
33 __all__ = ['BoundArguments', 'Parameter', 'Signature', 'signature']
34
35
36 _WrapperDescriptor = type(type.__call__)
37 _MethodWrapper = type(all.__call__)
38
39 _NonUserDefinedCallables = (_WrapperDescriptor,
40 _MethodWrapper,
41 types.BuiltinFunctionType)
42
43
44 def formatannotation(annotation, base_module=None):
45 if isinstance(annotation, type):
46 if annotation.__module__ in ('builtins', '__builtin__', base_module):
47 return annotation.__name__
48 return annotation.__module__+'.'+annotation.__name__
49 return repr(annotation)
50
51
52 def _get_user_defined_method(cls, method_name, *nested):
53 try:
54 if cls is type:
55 return
56 meth = getattr(cls, method_name)
57 for name in nested:
58 meth = getattr(meth, name, meth)
59 except AttributeError:
60 return
61 else:
62 if not isinstance(meth, _NonUserDefinedCallables):
63 # Once '__signature__' will be added to 'C'-level
64 # callables, this check won't be necessary
65 return meth
66
67
68 def signature(obj):
69 '''Get a signature object for the passed callable.'''
70
71 if not callable(obj):
72 raise TypeError('{0!r} is not a callable object'.format(obj))
73
74 if isinstance(obj, types.MethodType):
75 if obj.__self__ is None:
76 # Unbound method - treat it as a function (no distinction in Py 3)
77 obj = obj.__func__
78 else:
79 # Bound method: trim off the first parameter (typically self or cls)
80 sig = signature(obj.__func__)
81 return sig.replace(parameters=tuple(sig.parameters.values())[1:])
82
83 try:
84 sig = obj.__signature__
85 except AttributeError:
86 pass
87 else:
88 if sig is not None:
89 return sig
90
91 try:
92 # Was this function wrapped by a decorator?
93 wrapped = obj.__wrapped__
94 except AttributeError:
95 pass
96 else:
97 return signature(wrapped)
98
99 if isinstance(obj, types.FunctionType):
100 return Signature.from_function(obj)
101
102 if isinstance(obj, functools.partial):
103 sig = signature(obj.func)
104
105 new_params = OrderedDict(sig.parameters.items())
106
107 partial_args = obj.args or ()
108 partial_keywords = obj.keywords or {}
109 try:
110 ba = sig.bind_partial(*partial_args, **partial_keywords)
111 except TypeError as ex:
112 msg = 'partial object {0!r} has incorrect arguments'.format(obj)
113 raise ValueError(msg)
114
115 for arg_name, arg_value in ba.arguments.items():
116 param = new_params[arg_name]
117 if arg_name in partial_keywords:
118 # We set a new default value, because the following code
119 # is correct:
120 #
121 # >>> def foo(a): print(a)
122 # >>> print(partial(partial(foo, a=10), a=20)())
123 # 20
124 # >>> print(partial(partial(foo, a=10), a=20)(a=30))
125 # 30
126 #
127 # So, with 'partial' objects, passing a keyword argument is
128 # like setting a new default value for the corresponding
129 # parameter
130 #
131 # We also mark this parameter with '_partial_kwarg'
132 # flag. Later, in '_bind', the 'default' value of this
133 # parameter will be added to 'kwargs', to simulate
134 # the 'functools.partial' real call.
135 new_params[arg_name] = param.replace(default=arg_value,
136 _partial_kwarg=True)
137
138 elif (param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and
139 not param._partial_kwarg):
140 new_params.pop(arg_name)
141
142 return sig.replace(parameters=new_params.values())
143
144 sig = None
145 if isinstance(obj, type):
146 # obj is a class or a metaclass
147
148 # First, let's see if it has an overloaded __call__ defined
149 # in its metaclass
150 call = _get_user_defined_method(type(obj), '__call__')
151 if call is not None:
152 sig = signature(call)
153 else:
154 # Now we check if the 'obj' class has a '__new__' method
155 new = _get_user_defined_method(obj, '__new__')
156 if new is not None:
157 sig = signature(new)
158 else:
159 # Finally, we should have at least __init__ implemented
160 init = _get_user_defined_method(obj, '__init__')
161 if init is not None:
162 sig = signature(init)
163 elif not isinstance(obj, _NonUserDefinedCallables):
164 # An object with __call__
165 # We also check that the 'obj' is not an instance of
166 # _WrapperDescriptor or _MethodWrapper to avoid
167 # infinite recursion (and even potential segfault)
168 call = _get_user_defined_method(type(obj), '__call__', 'im_func')
169 if call is not None:
170 sig = signature(call)
171
172 if sig is not None:
173 return sig
174
175 if isinstance(obj, types.BuiltinFunctionType):
176 # Raise a nicer error message for builtins
177 msg = 'no signature found for builtin function {0!r}'.format(obj)
178 raise ValueError(msg)
179
180 raise ValueError('callable {0!r} is not supported by signature'.format(obj))
181
182
183 class _void(object):
184 '''A private marker - used in Parameter & Signature'''
185
186
187 class _empty(object):
188 pass
189
190
191 class _ParameterKind(int):
192 def __new__(self, *args, **kwargs):
193 obj = int.__new__(self, *args)
194 obj._name = kwargs['name']
195 return obj
196
197 def __str__(self):
198 return self._name
199
200 def __repr__(self):
201 return '<_ParameterKind: {0!r}>'.format(self._name)
202
203
204 _POSITIONAL_ONLY = _ParameterKind(0, name='POSITIONAL_ONLY')
205 _POSITIONAL_OR_KEYWORD = _ParameterKind(1, name='POSITIONAL_OR_KEYWORD')
206 _VAR_POSITIONAL = _ParameterKind(2, name='VAR_POSITIONAL')
207 _KEYWORD_ONLY = _ParameterKind(3, name='KEYWORD_ONLY')
208 _VAR_KEYWORD = _ParameterKind(4, name='VAR_KEYWORD')
209
210
211 class Parameter(object):
212 '''Represents a parameter in a function signature.
213
214 Has the following public attributes:
215
216 * name : str
217 The name of the parameter as a string.
218 * default : object
219 The default value for the parameter if specified. If the
220 parameter has no default value, this attribute is not set.
221 * annotation
222 The annotation for the parameter if specified. If the
223 parameter has no annotation, this attribute is not set.
224 * kind : str
225 Describes how argument values are bound to the parameter.
226 Possible values: `Parameter.POSITIONAL_ONLY`,
227 `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`,
228 `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
229 '''
230
231 __slots__ = ('_name', '_kind', '_default', '_annotation', '_partial_kwarg')
232
233 POSITIONAL_ONLY = _POSITIONAL_ONLY
234 POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
235 VAR_POSITIONAL = _VAR_POSITIONAL
236 KEYWORD_ONLY = _KEYWORD_ONLY
237 VAR_KEYWORD = _VAR_KEYWORD
238
239 empty = _empty
240
241 def __init__(self, name, kind, default=_empty, annotation=_empty,
242 _partial_kwarg=False):
243
244 if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
245 _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
246 raise ValueError("invalid value for 'Parameter.kind' attribute")
247 self._kind = kind
248
249 if default is not _empty:
250 if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
251 msg = '{0} parameters cannot have default values'.format(kind)
252 raise ValueError(msg)
253 self._default = default
254 self._annotation = annotation
255
256 if name is None:
257 if kind != _POSITIONAL_ONLY:
258 raise ValueError("None is not a valid name for a "
259 "non-positional-only parameter")
260 self._name = name
261 else:
262 name = str(name)
263 if kind != _POSITIONAL_ONLY and not re.match(r'[a-z_]\w*$', name, re.I):
264 msg = '{0!r} is not a valid parameter name'.format(name)
265 raise ValueError(msg)
266 self._name = name
267
268 self._partial_kwarg = _partial_kwarg
269
270 @property
271 def name(self):
272 return self._name
273
274 @property
275 def default(self):
276 return self._default
277
278 @property
279 def annotation(self):
280 return self._annotation
281
282 @property
283 def kind(self):
284 return self._kind
285
286 def replace(self, name=_void, kind=_void, annotation=_void,
287 default=_void, _partial_kwarg=_void):
288 '''Creates a customized copy of the Parameter.'''
289
290 if name is _void:
291 name = self._name
292
293 if kind is _void:
294 kind = self._kind
295
296 if annotation is _void:
297 annotation = self._annotation
298
299 if default is _void:
300 default = self._default
301
302 if _partial_kwarg is _void:
303 _partial_kwarg = self._partial_kwarg
304
305 return type(self)(name, kind, default=default, annotation=annotation,
306 _partial_kwarg=_partial_kwarg)
307
308 def __str__(self):
309 kind = self.kind
310
311 formatted = self._name
312 if kind == _POSITIONAL_ONLY:
313 if formatted is None:
314 formatted = ''
315 formatted = '<{0}>'.format(formatted)
316
317 # Add annotation and default value
318 if self._annotation is not _empty:
319 formatted = '{0}:{1}'.format(formatted,
320 formatannotation(self._annotation))
321
322 if self._default is not _empty:
323 formatted = '{0}={1}'.format(formatted, repr(self._default))
324
325 if kind == _VAR_POSITIONAL:
326 formatted = '*' + formatted
327 elif kind == _VAR_KEYWORD:
328 formatted = '**' + formatted
329
330 return formatted
331
332 def __repr__(self):
333 return '<{0} at {1:#x} {2!r}>'.format(self.__class__.__name__,
334 id(self), self.name)
335
336 def __hash__(self):
337 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
338 raise TypeError(msg)
339
340 def __eq__(self, other):
341 return (issubclass(other.__class__, Parameter) and
342 self._name == other._name and
343 self._kind == other._kind and
344 self._default == other._default and
345 self._annotation == other._annotation)
346
347 def __ne__(self, other):
348 return not self.__eq__(other)
349
350
351 class BoundArguments(object):
352 '''Result of :meth:`Signature.bind` call. Holds the mapping of arguments
353 to the function's parameters.
354
355 Has the following public attributes:
356
357 arguments : :class:`collections.OrderedDict`
358 An ordered mutable mapping of parameters' names to arguments' values.
359 Does not contain arguments' default values.
360 signature : :class:`Signature`
361 The Signature object that created this instance.
362 args : tuple
363 Tuple of positional arguments values.
364 kwargs : dict
365 Dict of keyword arguments values.
366 '''
367
368 def __init__(self, signature, arguments):
369 self.arguments = arguments
370 self._signature = signature
371
372 @property
373 def signature(self):
374 return self._signature
375
376 @property
377 def args(self):
378 args = []
379 for param_name, param in self._signature.parameters.items():
380 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
381 param._partial_kwarg):
382 # Keyword arguments mapped by 'functools.partial'
383 # (Parameter._partial_kwarg is True) are mapped
384 # in 'BoundArguments.kwargs', along with VAR_KEYWORD &
385 # KEYWORD_ONLY
386 break
387
388 try:
389 arg = self.arguments[param_name]
390 except KeyError:
391 # We're done here. Other arguments
392 # will be mapped in 'BoundArguments.kwargs'
393 break
394 else:
395 if param.kind == _VAR_POSITIONAL:
396 # *args
397 args.extend(arg)
398 else:
399 # plain argument
400 args.append(arg)
401
402 return tuple(args)
403
404 @property
405 def kwargs(self):
406 kwargs = {}
407 kwargs_started = False
408 for param_name, param in self._signature.parameters.items():
409 if not kwargs_started:
410 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
411 param._partial_kwarg):
412 kwargs_started = True
413 else:
414 if param_name not in self.arguments:
415 kwargs_started = True
416 continue
417
418 if not kwargs_started:
419 continue
420
421 try:
422 arg = self.arguments[param_name]
423 except KeyError:
424 pass
425 else:
426 if param.kind == _VAR_KEYWORD:
427 # **kwargs
428 kwargs.update(arg)
429 else:
430 # plain keyword argument
431 kwargs[param_name] = arg
432
433 return kwargs
434
435 def __hash__(self):
436 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
437 raise TypeError(msg)
438
439 def __eq__(self, other):
440 return (issubclass(other.__class__, BoundArguments) and
441 self.signature == other.signature and
442 self.arguments == other.arguments)
443
444 def __ne__(self, other):
445 return not self.__eq__(other)
446
447
448 class Signature(object):
449 '''A Signature object represents the overall signature of a function.
450 It stores a Parameter object for each parameter accepted by the
451 function, as well as information specific to the function itself.
452
453 A Signature object has the following public attributes:
454
455 parameters : :class:`collections.OrderedDict`
456 An ordered mapping of parameters' names to the corresponding
457 Parameter objects (keyword-only arguments are in the same order
458 as listed in `code.co_varnames`).
459 return_annotation
460 The annotation for the return type of the function if specified.
461 If the function has no annotation for its return type, this
462 attribute is not set.
463 '''
464
465 __slots__ = ('_return_annotation', '_parameters')
466
467 _parameter_cls = Parameter
468 _bound_arguments_cls = BoundArguments
469
470 empty = _empty
471
472 def __init__(self, parameters=None, return_annotation=_empty,
473 __validate_parameters__=True):
474 '''Constructs Signature from the given list of Parameter
475 objects and 'return_annotation'. All arguments are optional.
476 '''
477
478 if parameters is None:
479 params = OrderedDict()
480 else:
481 if __validate_parameters__:
482 params = OrderedDict()
483 top_kind = _POSITIONAL_ONLY
484
485 for idx, param in enumerate(parameters):
486 kind = param.kind
487 if kind < top_kind:
488 msg = 'wrong parameter order: {0} before {1}'
489 msg = msg.format(top_kind, param.kind)
490 raise ValueError(msg)
491 else:
492 top_kind = kind
493
494 name = param.name
495 if name is None:
496 name = str(idx)
497 param = param.replace(name=name)
498
499 if name in params:
500 msg = 'duplicate parameter name: {0!r}'.format(name)
501 raise ValueError(msg)
502 params[name] = param
503 else:
504 params = OrderedDict(((param.name, param)
505 for param in parameters))
506
507 self._parameters = params
508 self._return_annotation = return_annotation
509
510 @classmethod
511 def from_function(cls, func):
512 '''Constructs Signature for the given python function'''
513
514 if not isinstance(func, types.FunctionType):
515 raise TypeError('{0!r} is not a Python function'.format(func))
516
517 Parameter = cls._parameter_cls
518
519 # Parameter information.
520 func_code = func.__code__
521 pos_count = func_code.co_argcount
522 arg_names = func_code.co_varnames
523 positional = tuple(arg_names[:pos_count])
524 keyword_only_count = getattr(func_code, 'co_kwonlyargcount', 0)
525 keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
526 annotations = getattr(func, '__annotations__', {})
527 defaults = func.__defaults__
528 kwdefaults = getattr(func, '__kwdefaults__', None)
529
530 if defaults:
531 pos_default_count = len(defaults)
532 else:
533 pos_default_count = 0
534
535 parameters = []
536
537 # Non-keyword-only parameters w/o defaults.
538 non_default_count = pos_count - pos_default_count
539 for name in positional[:non_default_count]:
540 annotation = annotations.get(name, _empty)
541 parameters.append(Parameter(name, annotation=annotation,
542 kind=_POSITIONAL_OR_KEYWORD))
543
544 # ... w/ defaults.
545 for offset, name in enumerate(positional[non_default_count:]):
546 annotation = annotations.get(name, _empty)
547 parameters.append(Parameter(name, annotation=annotation,
548 kind=_POSITIONAL_OR_KEYWORD,
549 default=defaults[offset]))
550
551 # *args
552 if func_code.co_flags & 0x04:
553 name = arg_names[pos_count + keyword_only_count]
554 annotation = annotations.get(name, _empty)
555 parameters.append(Parameter(name, annotation=annotation,
556 kind=_VAR_POSITIONAL))
557
558 # Keyword-only parameters.
559 for name in keyword_only:
560 default = _empty
561 if kwdefaults is not None:
562 default = kwdefaults.get(name, _empty)
563
564 annotation = annotations.get(name, _empty)
565 parameters.append(Parameter(name, annotation=annotation,
566 kind=_KEYWORD_ONLY,
567 default=default))
568 # **kwargs
569 if func_code.co_flags & 0x08:
570 index = pos_count + keyword_only_count
571 if func_code.co_flags & 0x04:
572 index += 1
573
574 name = arg_names[index]
575 annotation = annotations.get(name, _empty)
576 parameters.append(Parameter(name, annotation=annotation,
577 kind=_VAR_KEYWORD))
578
579 return cls(parameters,
580 return_annotation=annotations.get('return', _empty),
581 __validate_parameters__=False)
582
583 @property
584 def parameters(self):
585 try:
586 return types.MappingProxyType(self._parameters)
587 except AttributeError:
588 return OrderedDict(self._parameters.items())
589
590 @property
591 def return_annotation(self):
592 return self._return_annotation
593
594 def replace(self, parameters=_void, return_annotation=_void):
595 '''Creates a customized copy of the Signature.
596 Pass 'parameters' and/or 'return_annotation' arguments
597 to override them in the new copy.
598 '''
599
600 if parameters is _void:
601 parameters = self.parameters.values()
602
603 if return_annotation is _void:
604 return_annotation = self._return_annotation
605
606 return type(self)(parameters,
607 return_annotation=return_annotation)
608
609 def __hash__(self):
610 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
611 raise TypeError(msg)
612
613 def __eq__(self, other):
614 if (not issubclass(type(other), Signature) or
615 self.return_annotation != other.return_annotation or
616 len(self.parameters) != len(other.parameters)):
617 return False
618
619 other_positions = dict((param, idx)
620 for idx, param in enumerate(other.parameters.keys()))
621
622 for idx, (param_name, param) in enumerate(self.parameters.items()):
623 if param.kind == _KEYWORD_ONLY:
624 try:
625 other_param = other.parameters[param_name]
626 except KeyError:
627 return False
628 else:
629 if param != other_param:
630 return False
631 else:
632 try:
633 other_idx = other_positions[param_name]
634 except KeyError:
635 return False
636 else:
637 if (idx != other_idx or
638 param != other.parameters[param_name]):
639 return False
640
641 return True
642
643 def __ne__(self, other):
644 return not self.__eq__(other)
645
646 def _bind(self, args, kwargs, partial=False):
647 '''Private method. Don't use directly.'''
648
649 arguments = OrderedDict()
650
651 parameters = iter(self.parameters.values())
652 parameters_ex = ()
653 arg_vals = iter(args)
654
655 if partial:
656 # Support for binding arguments to 'functools.partial' objects.
657 # See 'functools.partial' case in 'signature()' implementation
658 # for details.
659 for param_name, param in self.parameters.items():
660 if (param._partial_kwarg and param_name not in kwargs):
661 # Simulating 'functools.partial' behavior
662 kwargs[param_name] = param.default
663
664 while True:
665 # Let's iterate through the positional arguments and corresponding
666 # parameters
667 try:
668 arg_val = next(arg_vals)
669 except StopIteration:
670 # No more positional arguments
671 try:
672 param = next(parameters)
673 except StopIteration:
674 # No more parameters. That's it. Just need to check that
675 # we have no `kwargs` after this while loop
676 break
677 else:
678 if param.kind == _VAR_POSITIONAL:
679 # That's OK, just empty *args. Let's start parsing
680 # kwargs
681 break
682 elif param.name in kwargs:
683 if param.kind == _POSITIONAL_ONLY:
684 msg = '{arg!r} parameter is positional only, ' \
685 'but was passed as a keyword'
686 msg = msg.format(arg=param.name)
687 raise TypeError(msg)
688 parameters_ex = (param,)
689 break
690 elif (param.kind == _VAR_KEYWORD or
691 param.default is not _empty):
692 # That's fine too - we have a default value for this
693 # parameter. So, lets start parsing `kwargs`, starting
694 # with the current parameter
695 parameters_ex = (param,)
696 break
697 else:
698 if partial:
699 parameters_ex = (param,)
700 break
701 else:
702 msg = '{arg!r} parameter lacking default value'
703 msg = msg.format(arg=param.name)
704 raise TypeError(msg)
705 else:
706 # We have a positional argument to process
707 try:
708 param = next(parameters)
709 except StopIteration:
710 raise TypeError('too many positional arguments')
711 else:
712 if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
713 # Looks like we have no parameter for this positional
714 # argument
715 raise TypeError('too many positional arguments')
716
717 if param.kind == _VAR_POSITIONAL:
718 # We have an '*args'-like argument, let's fill it with
719 # all positional arguments we have left and move on to
720 # the next phase
721 values = [arg_val]
722 values.extend(arg_vals)
723 arguments[param.name] = tuple(values)
724 break
725
726 if param.name in kwargs:
727 raise TypeError('multiple values for argument '
728 '{arg!r}'.format(arg=param.name))
729
730 arguments[param.name] = arg_val
731
732 # Now, we iterate through the remaining parameters to process
733 # keyword arguments
734 kwargs_param = None
735 for param in itertools.chain(parameters_ex, parameters):
736 if param.kind == _POSITIONAL_ONLY:
737 # This should never happen in case of a properly built
738 # Signature object (but let's have this check here
739 # to ensure correct behaviour just in case)
740 raise TypeError('{arg!r} parameter is positional only, '
741 'but was passed as a keyword'. \
742 format(arg=param.name))
743
744 if param.kind == _VAR_KEYWORD:
745 # Memorize that we have a '**kwargs'-like parameter
746 kwargs_param = param
747 continue
748
749 param_name = param.name
750 try:
751 arg_val = kwargs.pop(param_name)
752 except KeyError:
753 # We have no value for this parameter. It's fine though,
754 # if it has a default value, or it is an '*args'-like
755 # parameter, left alone by the processing of positional
756 # arguments.
757 if (not partial and param.kind != _VAR_POSITIONAL and
758 param.default is _empty):
759 raise TypeError('{arg!r} parameter lacking default value'. \
760 format(arg=param_name))
761
762 else:
763 arguments[param_name] = arg_val
764
765 if kwargs:
766 if kwargs_param is not None:
767 # Process our '**kwargs'-like parameter
768 arguments[kwargs_param.name] = kwargs
769 else:
770 raise TypeError('too many keyword arguments')
771
772 return self._bound_arguments_cls(self, arguments)
773
774 def bind(self, *args, **kwargs):
775 '''Get a :class:`BoundArguments` object, that maps the passed `args`
776 and `kwargs` to the function's signature. Raises :exc:`TypeError`
777 if the passed arguments can not be bound.
778 '''
779 return self._bind(args, kwargs)
780
781 def bind_partial(self, *args, **kwargs):
782 '''Get a :class:`BoundArguments` object, that partially maps the
783 passed `args` and `kwargs` to the function's signature.
784 Raises :exc:`TypeError` if the passed arguments can not be bound.
785 '''
786 return self._bind(args, kwargs, partial=True)
787
788 def __str__(self):
789 result = []
790 render_kw_only_separator = True
791 for idx, param in enumerate(self.parameters.values()):
792 formatted = str(param)
793
794 kind = param.kind
795 if kind == _VAR_POSITIONAL:
796 # OK, we have an '*args'-like parameter, so we won't need
797 # a '*' to separate keyword-only arguments
798 render_kw_only_separator = False
799 elif kind == _KEYWORD_ONLY and render_kw_only_separator:
800 # We have a keyword-only parameter to render and we haven't
801 # rendered an '*args'-like parameter before, so add a '*'
802 # separator to the parameters list ("foo(arg1, *, arg2)" case)
803 result.append('*')
804 # This condition should be only triggered once, so
805 # reset the flag
806 render_kw_only_separator = False
807
808 result.append(formatted)
809
810 rendered = '({0})'.format(', '.join(result))
811
812 if self.return_annotation is not _empty:
813 anno = formatannotation(self.return_annotation)
814 rendered += ' -> {0}'.format(anno)
815
816 return rendered
817
This diff has been collapsed as it changes many lines, (818 lines changed) Show them Hide them
@@ -1,817 +1,11 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 Use the standard library version if available, as it is more up to date.
4 modified to be compatible with Python 2.7 and 3.2+.
4 Fallback on backport otherwise.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
8 # Python 3.3 stdlib inspect.py is public domain
9 #
10 # Backports Copyright (C) 2013 Aaron Iles
11 # Used under Apache License Version 2.0
12 #
13 # Further Changes are Copyright (C) 2013 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 from __future__ import absolute_import, division, print_function
20 import itertools
21 import functools
22 import re
23 import types
24
25
26 # patch for single-file
27 # we don't support 2.6, so we can just import OrderedDict
28 from collections import OrderedDict
29
30 __version__ = '0.3'
31 # end patch
32
33 __all__ = ['BoundArguments', 'Parameter', 'Signature', 'signature']
34
35
36 _WrapperDescriptor = type(type.__call__)
37 _MethodWrapper = type(all.__call__)
38
39 _NonUserDefinedCallables = (_WrapperDescriptor,
40 _MethodWrapper,
41 types.BuiltinFunctionType)
42
43
44 def formatannotation(annotation, base_module=None):
45 if isinstance(annotation, type):
46 if annotation.__module__ in ('builtins', '__builtin__', base_module):
47 return annotation.__name__
48 return annotation.__module__+'.'+annotation.__name__
49 return repr(annotation)
50
51
52 def _get_user_defined_method(cls, method_name, *nested):
53 try:
54 if cls is type:
55 return
56 meth = getattr(cls, method_name)
57 for name in nested:
58 meth = getattr(meth, name, meth)
59 except AttributeError:
60 return
61 else:
62 if not isinstance(meth, _NonUserDefinedCallables):
63 # Once '__signature__' will be added to 'C'-level
64 # callables, this check won't be necessary
65 return meth
66
67
68 def signature(obj):
69 '''Get a signature object for the passed callable.'''
70
71 if not callable(obj):
72 raise TypeError('{0!r} is not a callable object'.format(obj))
73
74 if isinstance(obj, types.MethodType):
75 if obj.__self__ is None:
76 # Unbound method - treat it as a function (no distinction in Py 3)
77 obj = obj.__func__
78 else:
79 # Bound method: trim off the first parameter (typically self or cls)
80 sig = signature(obj.__func__)
81 return sig.replace(parameters=tuple(sig.parameters.values())[1:])
82
83 try:
84 sig = obj.__signature__
85 except AttributeError:
86 pass
87 else:
88 if sig is not None:
89 return sig
90
91 try:
92 # Was this function wrapped by a decorator?
93 wrapped = obj.__wrapped__
94 except AttributeError:
95 pass
96 else:
97 return signature(wrapped)
98
99 if isinstance(obj, types.FunctionType):
100 return Signature.from_function(obj)
101
102 if isinstance(obj, functools.partial):
103 sig = signature(obj.func)
104
105 new_params = OrderedDict(sig.parameters.items())
106
107 partial_args = obj.args or ()
108 partial_keywords = obj.keywords or {}
109 try:
110 ba = sig.bind_partial(*partial_args, **partial_keywords)
111 except TypeError as ex:
112 msg = 'partial object {0!r} has incorrect arguments'.format(obj)
113 raise ValueError(msg)
114
115 for arg_name, arg_value in ba.arguments.items():
116 param = new_params[arg_name]
117 if arg_name in partial_keywords:
118 # We set a new default value, because the following code
119 # is correct:
120 #
121 # >>> def foo(a): print(a)
122 # >>> print(partial(partial(foo, a=10), a=20)())
123 # 20
124 # >>> print(partial(partial(foo, a=10), a=20)(a=30))
125 # 30
126 #
127 # So, with 'partial' objects, passing a keyword argument is
128 # like setting a new default value for the corresponding
129 # parameter
130 #
131 # We also mark this parameter with '_partial_kwarg'
132 # flag. Later, in '_bind', the 'default' value of this
133 # parameter will be added to 'kwargs', to simulate
134 # the 'functools.partial' real call.
135 new_params[arg_name] = param.replace(default=arg_value,
136 _partial_kwarg=True)
137
138 elif (param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and
139 not param._partial_kwarg):
140 new_params.pop(arg_name)
141
142 return sig.replace(parameters=new_params.values())
143
144 sig = None
145 if isinstance(obj, type):
146 # obj is a class or a metaclass
147
148 # First, let's see if it has an overloaded __call__ defined
149 # in its metaclass
150 call = _get_user_defined_method(type(obj), '__call__')
151 if call is not None:
152 sig = signature(call)
153 else:
154 # Now we check if the 'obj' class has a '__new__' method
155 new = _get_user_defined_method(obj, '__new__')
156 if new is not None:
157 sig = signature(new)
158 else:
159 # Finally, we should have at least __init__ implemented
160 init = _get_user_defined_method(obj, '__init__')
161 if init is not None:
162 sig = signature(init)
163 elif not isinstance(obj, _NonUserDefinedCallables):
164 # An object with __call__
165 # We also check that the 'obj' is not an instance of
166 # _WrapperDescriptor or _MethodWrapper to avoid
167 # infinite recursion (and even potential segfault)
168 call = _get_user_defined_method(type(obj), '__call__', 'im_func')
169 if call is not None:
170 sig = signature(call)
171
172 if sig is not None:
173 return sig
174
175 if isinstance(obj, types.BuiltinFunctionType):
176 # Raise a nicer error message for builtins
177 msg = 'no signature found for builtin function {0!r}'.format(obj)
178 raise ValueError(msg)
179
180 raise ValueError('callable {0!r} is not supported by signature'.format(obj))
181
182
183 class _void(object):
184 '''A private marker - used in Parameter & Signature'''
185
186
187 class _empty(object):
188 pass
189
190
191 class _ParameterKind(int):
192 def __new__(self, *args, **kwargs):
193 obj = int.__new__(self, *args)
194 obj._name = kwargs['name']
195 return obj
196
197 def __str__(self):
198 return self._name
199
200 def __repr__(self):
201 return '<_ParameterKind: {0!r}>'.format(self._name)
202
203
204 _POSITIONAL_ONLY = _ParameterKind(0, name='POSITIONAL_ONLY')
205 _POSITIONAL_OR_KEYWORD = _ParameterKind(1, name='POSITIONAL_OR_KEYWORD')
206 _VAR_POSITIONAL = _ParameterKind(2, name='VAR_POSITIONAL')
207 _KEYWORD_ONLY = _ParameterKind(3, name='KEYWORD_ONLY')
208 _VAR_KEYWORD = _ParameterKind(4, name='VAR_KEYWORD')
209
210
211 class Parameter(object):
212 '''Represents a parameter in a function signature.
213
214 Has the following public attributes:
215
216 * name : str
217 The name of the parameter as a string.
218 * default : object
219 The default value for the parameter if specified. If the
220 parameter has no default value, this attribute is not set.
221 * annotation
222 The annotation for the parameter if specified. If the
223 parameter has no annotation, this attribute is not set.
224 * kind : str
225 Describes how argument values are bound to the parameter.
226 Possible values: `Parameter.POSITIONAL_ONLY`,
227 `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`,
228 `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
229 '''
230
231 __slots__ = ('_name', '_kind', '_default', '_annotation', '_partial_kwarg')
232
233 POSITIONAL_ONLY = _POSITIONAL_ONLY
234 POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
235 VAR_POSITIONAL = _VAR_POSITIONAL
236 KEYWORD_ONLY = _KEYWORD_ONLY
237 VAR_KEYWORD = _VAR_KEYWORD
238
239 empty = _empty
240
241 def __init__(self, name, kind, default=_empty, annotation=_empty,
242 _partial_kwarg=False):
243
244 if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
245 _VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
246 raise ValueError("invalid value for 'Parameter.kind' attribute")
247 self._kind = kind
248
249 if default is not _empty:
250 if kind in (_VAR_POSITIONAL, _VAR_KEYWORD):
251 msg = '{0} parameters cannot have default values'.format(kind)
252 raise ValueError(msg)
253 self._default = default
254 self._annotation = annotation
255
256 if name is None:
257 if kind != _POSITIONAL_ONLY:
258 raise ValueError("None is not a valid name for a "
259 "non-positional-only parameter")
260 self._name = name
261 else:
262 name = str(name)
263 if kind != _POSITIONAL_ONLY and not re.match(r'[a-z_]\w*$', name, re.I):
264 msg = '{0!r} is not a valid parameter name'.format(name)
265 raise ValueError(msg)
266 self._name = name
267
268 self._partial_kwarg = _partial_kwarg
269
270 @property
271 def name(self):
272 return self._name
273
274 @property
275 def default(self):
276 return self._default
277
278 @property
279 def annotation(self):
280 return self._annotation
281
282 @property
283 def kind(self):
284 return self._kind
285
286 def replace(self, name=_void, kind=_void, annotation=_void,
287 default=_void, _partial_kwarg=_void):
288 '''Creates a customized copy of the Parameter.'''
289
290 if name is _void:
291 name = self._name
292
293 if kind is _void:
294 kind = self._kind
295
296 if annotation is _void:
297 annotation = self._annotation
298
299 if default is _void:
300 default = self._default
301
302 if _partial_kwarg is _void:
303 _partial_kwarg = self._partial_kwarg
304
305 return type(self)(name, kind, default=default, annotation=annotation,
306 _partial_kwarg=_partial_kwarg)
307
308 def __str__(self):
309 kind = self.kind
310
311 formatted = self._name
312 if kind == _POSITIONAL_ONLY:
313 if formatted is None:
314 formatted = ''
315 formatted = '<{0}>'.format(formatted)
316
317 # Add annotation and default value
318 if self._annotation is not _empty:
319 formatted = '{0}:{1}'.format(formatted,
320 formatannotation(self._annotation))
321
322 if self._default is not _empty:
323 formatted = '{0}={1}'.format(formatted, repr(self._default))
324
325 if kind == _VAR_POSITIONAL:
326 formatted = '*' + formatted
327 elif kind == _VAR_KEYWORD:
328 formatted = '**' + formatted
329
330 return formatted
331
332 def __repr__(self):
333 return '<{0} at {1:#x} {2!r}>'.format(self.__class__.__name__,
334 id(self), self.name)
335
336 def __hash__(self):
337 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
338 raise TypeError(msg)
339
340 def __eq__(self, other):
341 return (issubclass(other.__class__, Parameter) and
342 self._name == other._name and
343 self._kind == other._kind and
344 self._default == other._default and
345 self._annotation == other._annotation)
346
347 def __ne__(self, other):
348 return not self.__eq__(other)
349
350
351 class BoundArguments(object):
352 '''Result of :meth:`Signature.bind` call. Holds the mapping of arguments
353 to the function's parameters.
354
355 Has the following public attributes:
356
357 arguments : :class:`collections.OrderedDict`
358 An ordered mutable mapping of parameters' names to arguments' values.
359 Does not contain arguments' default values.
360 signature : :class:`Signature`
361 The Signature object that created this instance.
362 args : tuple
363 Tuple of positional arguments values.
364 kwargs : dict
365 Dict of keyword arguments values.
366 '''
367
368 def __init__(self, signature, arguments):
369 self.arguments = arguments
370 self._signature = signature
371
372 @property
373 def signature(self):
374 return self._signature
375
376 @property
377 def args(self):
378 args = []
379 for param_name, param in self._signature.parameters.items():
380 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
381 param._partial_kwarg):
382 # Keyword arguments mapped by 'functools.partial'
383 # (Parameter._partial_kwarg is True) are mapped
384 # in 'BoundArguments.kwargs', along with VAR_KEYWORD &
385 # KEYWORD_ONLY
386 break
387
388 try:
389 arg = self.arguments[param_name]
390 except KeyError:
391 # We're done here. Other arguments
392 # will be mapped in 'BoundArguments.kwargs'
393 break
394 else:
395 if param.kind == _VAR_POSITIONAL:
396 # *args
397 args.extend(arg)
398 else:
399 # plain argument
400 args.append(arg)
401
402 return tuple(args)
403
404 @property
405 def kwargs(self):
406 kwargs = {}
407 kwargs_started = False
408 for param_name, param in self._signature.parameters.items():
409 if not kwargs_started:
410 if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
411 param._partial_kwarg):
412 kwargs_started = True
413 else:
414 if param_name not in self.arguments:
415 kwargs_started = True
416 continue
417
418 if not kwargs_started:
419 continue
420
421 try:
422 arg = self.arguments[param_name]
423 except KeyError:
424 pass
425 else:
426 if param.kind == _VAR_KEYWORD:
427 # **kwargs
428 kwargs.update(arg)
429 else:
430 # plain keyword argument
431 kwargs[param_name] = arg
432
433 return kwargs
434
435 def __hash__(self):
436 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
437 raise TypeError(msg)
438
439 def __eq__(self, other):
440 return (issubclass(other.__class__, BoundArguments) and
441 self.signature == other.signature and
442 self.arguments == other.arguments)
443
444 def __ne__(self, other):
445 return not self.__eq__(other)
446
447
448 class Signature(object):
449 '''A Signature object represents the overall signature of a function.
450 It stores a Parameter object for each parameter accepted by the
451 function, as well as information specific to the function itself.
452
453 A Signature object has the following public attributes:
454
455 parameters : :class:`collections.OrderedDict`
456 An ordered mapping of parameters' names to the corresponding
457 Parameter objects (keyword-only arguments are in the same order
458 as listed in `code.co_varnames`).
459 return_annotation
460 The annotation for the return type of the function if specified.
461 If the function has no annotation for its return type, this
462 attribute is not set.
463 '''
464
465 __slots__ = ('_return_annotation', '_parameters')
466
467 _parameter_cls = Parameter
468 _bound_arguments_cls = BoundArguments
469
470 empty = _empty
471
472 def __init__(self, parameters=None, return_annotation=_empty,
473 __validate_parameters__=True):
474 '''Constructs Signature from the given list of Parameter
475 objects and 'return_annotation'. All arguments are optional.
476 '''
477
478 if parameters is None:
479 params = OrderedDict()
480 else:
481 if __validate_parameters__:
482 params = OrderedDict()
483 top_kind = _POSITIONAL_ONLY
484
485 for idx, param in enumerate(parameters):
486 kind = param.kind
487 if kind < top_kind:
488 msg = 'wrong parameter order: {0} before {1}'
489 msg = msg.format(top_kind, param.kind)
490 raise ValueError(msg)
491 else:
492 top_kind = kind
493
494 name = param.name
495 if name is None:
496 name = str(idx)
497 param = param.replace(name=name)
498
499 if name in params:
500 msg = 'duplicate parameter name: {0!r}'.format(name)
501 raise ValueError(msg)
502 params[name] = param
503 else:
504 params = OrderedDict(((param.name, param)
505 for param in parameters))
506
507 self._parameters = params
508 self._return_annotation = return_annotation
509
510 @classmethod
511 def from_function(cls, func):
512 '''Constructs Signature for the given python function'''
513
514 if not isinstance(func, types.FunctionType):
515 raise TypeError('{0!r} is not a Python function'.format(func))
516
517 Parameter = cls._parameter_cls
518
519 # Parameter information.
520 func_code = func.__code__
521 pos_count = func_code.co_argcount
522 arg_names = func_code.co_varnames
523 positional = tuple(arg_names[:pos_count])
524 keyword_only_count = getattr(func_code, 'co_kwonlyargcount', 0)
525 keyword_only = arg_names[pos_count:(pos_count + keyword_only_count)]
526 annotations = getattr(func, '__annotations__', {})
527 defaults = func.__defaults__
528 kwdefaults = getattr(func, '__kwdefaults__', None)
529
530 if defaults:
531 pos_default_count = len(defaults)
532 else:
533 pos_default_count = 0
534
535 parameters = []
536
537 # Non-keyword-only parameters w/o defaults.
538 non_default_count = pos_count - pos_default_count
539 for name in positional[:non_default_count]:
540 annotation = annotations.get(name, _empty)
541 parameters.append(Parameter(name, annotation=annotation,
542 kind=_POSITIONAL_OR_KEYWORD))
543
544 # ... w/ defaults.
545 for offset, name in enumerate(positional[non_default_count:]):
546 annotation = annotations.get(name, _empty)
547 parameters.append(Parameter(name, annotation=annotation,
548 kind=_POSITIONAL_OR_KEYWORD,
549 default=defaults[offset]))
550
551 # *args
552 if func_code.co_flags & 0x04:
553 name = arg_names[pos_count + keyword_only_count]
554 annotation = annotations.get(name, _empty)
555 parameters.append(Parameter(name, annotation=annotation,
556 kind=_VAR_POSITIONAL))
557
558 # Keyword-only parameters.
559 for name in keyword_only:
560 default = _empty
561 if kwdefaults is not None:
562 default = kwdefaults.get(name, _empty)
563
564 annotation = annotations.get(name, _empty)
565 parameters.append(Parameter(name, annotation=annotation,
566 kind=_KEYWORD_ONLY,
567 default=default))
568 # **kwargs
569 if func_code.co_flags & 0x08:
570 index = pos_count + keyword_only_count
571 if func_code.co_flags & 0x04:
572 index += 1
573
574 name = arg_names[index]
575 annotation = annotations.get(name, _empty)
576 parameters.append(Parameter(name, annotation=annotation,
577 kind=_VAR_KEYWORD))
578
579 return cls(parameters,
580 return_annotation=annotations.get('return', _empty),
581 __validate_parameters__=False)
582
583 @property
584 def parameters(self):
585 try:
586 return types.MappingProxyType(self._parameters)
587 except AttributeError:
588 return OrderedDict(self._parameters.items())
589
590 @property
591 def return_annotation(self):
592 return self._return_annotation
593
594 def replace(self, parameters=_void, return_annotation=_void):
595 '''Creates a customized copy of the Signature.
596 Pass 'parameters' and/or 'return_annotation' arguments
597 to override them in the new copy.
598 '''
599
600 if parameters is _void:
601 parameters = self.parameters.values()
602
603 if return_annotation is _void:
604 return_annotation = self._return_annotation
605
606 return type(self)(parameters,
607 return_annotation=return_annotation)
608
609 def __hash__(self):
610 msg = "unhashable type: '{0}'".format(self.__class__.__name__)
611 raise TypeError(msg)
612
613 def __eq__(self, other):
614 if (not issubclass(type(other), Signature) or
615 self.return_annotation != other.return_annotation or
616 len(self.parameters) != len(other.parameters)):
617 return False
618
619 other_positions = dict((param, idx)
620 for idx, param in enumerate(other.parameters.keys()))
621
622 for idx, (param_name, param) in enumerate(self.parameters.items()):
623 if param.kind == _KEYWORD_ONLY:
624 try:
625 other_param = other.parameters[param_name]
626 except KeyError:
627 return False
628 else:
629 if param != other_param:
630 return False
631 else:
632 try:
633 other_idx = other_positions[param_name]
634 except KeyError:
635 return False
636 else:
637 if (idx != other_idx or
638 param != other.parameters[param_name]):
639 return False
640
641 return True
642
643 def __ne__(self, other):
644 return not self.__eq__(other)
645
646 def _bind(self, args, kwargs, partial=False):
647 '''Private method. Don't use directly.'''
648
649 arguments = OrderedDict()
650
651 parameters = iter(self.parameters.values())
652 parameters_ex = ()
653 arg_vals = iter(args)
654
655 if partial:
656 # Support for binding arguments to 'functools.partial' objects.
657 # See 'functools.partial' case in 'signature()' implementation
658 # for details.
659 for param_name, param in self.parameters.items():
660 if (param._partial_kwarg and param_name not in kwargs):
661 # Simulating 'functools.partial' behavior
662 kwargs[param_name] = param.default
663
664 while True:
665 # Let's iterate through the positional arguments and corresponding
666 # parameters
667 try:
668 arg_val = next(arg_vals)
669 except StopIteration:
670 # No more positional arguments
671 try:
672 param = next(parameters)
673 except StopIteration:
674 # No more parameters. That's it. Just need to check that
675 # we have no `kwargs` after this while loop
676 break
677 else:
678 if param.kind == _VAR_POSITIONAL:
679 # That's OK, just empty *args. Let's start parsing
680 # kwargs
681 break
682 elif param.name in kwargs:
683 if param.kind == _POSITIONAL_ONLY:
684 msg = '{arg!r} parameter is positional only, ' \
685 'but was passed as a keyword'
686 msg = msg.format(arg=param.name)
687 raise TypeError(msg)
688 parameters_ex = (param,)
689 break
690 elif (param.kind == _VAR_KEYWORD or
691 param.default is not _empty):
692 # That's fine too - we have a default value for this
693 # parameter. So, lets start parsing `kwargs`, starting
694 # with the current parameter
695 parameters_ex = (param,)
696 break
697 else:
698 if partial:
699 parameters_ex = (param,)
700 break
701 else:
702 msg = '{arg!r} parameter lacking default value'
703 msg = msg.format(arg=param.name)
704 raise TypeError(msg)
705 else:
706 # We have a positional argument to process
707 try:
708 param = next(parameters)
709 except StopIteration:
710 raise TypeError('too many positional arguments')
711 else:
712 if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
713 # Looks like we have no parameter for this positional
714 # argument
715 raise TypeError('too many positional arguments')
716
717 if param.kind == _VAR_POSITIONAL:
718 # We have an '*args'-like argument, let's fill it with
719 # all positional arguments we have left and move on to
720 # the next phase
721 values = [arg_val]
722 values.extend(arg_vals)
723 arguments[param.name] = tuple(values)
724 break
725
726 if param.name in kwargs:
727 raise TypeError('multiple values for argument '
728 '{arg!r}'.format(arg=param.name))
729
730 arguments[param.name] = arg_val
731
732 # Now, we iterate through the remaining parameters to process
733 # keyword arguments
734 kwargs_param = None
735 for param in itertools.chain(parameters_ex, parameters):
736 if param.kind == _POSITIONAL_ONLY:
737 # This should never happen in case of a properly built
738 # Signature object (but let's have this check here
739 # to ensure correct behaviour just in case)
740 raise TypeError('{arg!r} parameter is positional only, '
741 'but was passed as a keyword'. \
742 format(arg=param.name))
743
744 if param.kind == _VAR_KEYWORD:
745 # Memorize that we have a '**kwargs'-like parameter
746 kwargs_param = param
747 continue
748
749 param_name = param.name
750 try:
751 arg_val = kwargs.pop(param_name)
752 except KeyError:
753 # We have no value for this parameter. It's fine though,
754 # if it has a default value, or it is an '*args'-like
755 # parameter, left alone by the processing of positional
756 # arguments.
757 if (not partial and param.kind != _VAR_POSITIONAL and
758 param.default is _empty):
759 raise TypeError('{arg!r} parameter lacking default value'. \
760 format(arg=param_name))
761
762 else:
763 arguments[param_name] = arg_val
764
765 if kwargs:
766 if kwargs_param is not None:
767 # Process our '**kwargs'-like parameter
768 arguments[kwargs_param.name] = kwargs
769 else:
770 raise TypeError('too many keyword arguments')
771
772 return self._bound_arguments_cls(self, arguments)
773
774 def bind(self, *args, **kwargs):
775 '''Get a :class:`BoundArguments` object, that maps the passed `args`
776 and `kwargs` to the function's signature. Raises :exc:`TypeError`
777 if the passed arguments can not be bound.
778 '''
779 return self._bind(args, kwargs)
780
781 def bind_partial(self, *args, **kwargs):
782 '''Get a :class:`BoundArguments` object, that partially maps the
783 passed `args` and `kwargs` to the function's signature.
784 Raises :exc:`TypeError` if the passed arguments can not be bound.
785 '''
786 return self._bind(args, kwargs, partial=True)
787
788 def __str__(self):
789 result = []
790 render_kw_only_separator = True
791 for idx, param in enumerate(self.parameters.values()):
792 formatted = str(param)
793
794 kind = param.kind
795 if kind == _VAR_POSITIONAL:
796 # OK, we have an '*args'-like parameter, so we won't need
797 # a '*' to separate keyword-only arguments
798 render_kw_only_separator = False
799 elif kind == _KEYWORD_ONLY and render_kw_only_separator:
800 # We have a keyword-only parameter to render and we haven't
801 # rendered an '*args'-like parameter before, so add a '*'
802 # separator to the parameters list ("foo(arg1, *, arg2)" case)
803 result.append('*')
804 # This condition should be only triggered once, so
805 # reset the flag
806 render_kw_only_separator = False
807
808 result.append(formatted)
809
810 rendered = '({0})'.format(', '.join(result))
811
812 if self.return_annotation is not _empty:
813 anno = formatannotation(self.return_annotation)
814 rendered += ' -> {0}'.format(anno)
815
816 return rendered
817
7
8 try:
9 from inspect import BoundArguments, Parameter, Signature, signature
10 except ImportError:
11 from ._signatures import BoundArguments, Parameter, Signature, signature
General Comments 0
You need to be logged in to leave comments. Login now