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