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