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