Private helper to calculate how 'wrapped_sig' signature will look like after applying a 'functools.partial' object (or alike) on it.
(wrapped_sig, partial, extra_args=())
| 1959 | |
| 1960 | |
| 1961 | def _signature_get_partial(wrapped_sig, partial, extra_args=()): |
| 1962 | """Private helper to calculate how 'wrapped_sig' signature will |
| 1963 | look like after applying a 'functools.partial' object (or alike) |
| 1964 | on it. |
| 1965 | """ |
| 1966 | |
| 1967 | old_params = wrapped_sig.parameters |
| 1968 | new_params = OrderedDict(old_params.items()) |
| 1969 | |
| 1970 | partial_args = partial.args or () |
| 1971 | partial_keywords = partial.keywords or {} |
| 1972 | |
| 1973 | if extra_args: |
| 1974 | partial_args = extra_args + partial_args |
| 1975 | |
| 1976 | try: |
| 1977 | ba = wrapped_sig.bind_partial(*partial_args, **partial_keywords) |
| 1978 | except TypeError as ex: |
| 1979 | msg = 'partial object {!r} has incorrect arguments'.format(partial) |
| 1980 | raise ValueError(msg) from ex |
| 1981 | |
| 1982 | |
| 1983 | transform_to_kwonly = False |
| 1984 | for param_name, param in old_params.items(): |
| 1985 | try: |
| 1986 | arg_value = ba.arguments[param_name] |
| 1987 | except KeyError: |
| 1988 | pass |
| 1989 | else: |
| 1990 | if param.kind is _POSITIONAL_ONLY: |
| 1991 | # If positional-only parameter is bound by partial, |
| 1992 | # it effectively disappears from the signature |
| 1993 | new_params.pop(param_name) |
| 1994 | continue |
| 1995 | |
| 1996 | if param.kind is _POSITIONAL_OR_KEYWORD: |
| 1997 | if param_name in partial_keywords: |
| 1998 | # This means that this parameter, and all parameters |
| 1999 | # after it should be keyword-only (and var-positional |
| 2000 | # should be removed). Here's why. Consider the following |
| 2001 | # function: |
| 2002 | # foo(a, b, *args, c): |
| 2003 | # pass |
| 2004 | # |
| 2005 | # "partial(foo, a='spam')" will have the following |
| 2006 | # signature: "(*, a='spam', b, c)". Because attempting |
| 2007 | # to call that partial with "(10, 20)" arguments will |
| 2008 | # raise a TypeError, saying that "a" argument received |
| 2009 | # multiple values. |
| 2010 | transform_to_kwonly = True |
| 2011 | # Set the new default value |
| 2012 | new_params[param_name] = param.replace(default=arg_value) |
| 2013 | else: |
| 2014 | # was passed as a positional argument |
| 2015 | new_params.pop(param.name) |
| 2016 | continue |
| 2017 | |
| 2018 | if param.kind is _KEYWORD_ONLY: |
no test coverage detected