Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the signature parameters of a callable, or reliably determine when this is not possible?

The built-in int takes two parameters:

>>> print(int.__doc__)
int(x=0) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
...

However, (in CPython 3.4.0) inspect.signature shows 0:

>>> len(inspect.signature(int).parameters)
0

in contrast with a user-defined function:

>>> def my_int(x, base=10):
...     return int(x, base)
... 
>>> len(inspect.signature(my_int).parameters)
2

The docs for inspect.signature do say:

Some callables may not be introspectable in certain implementations of Python. For example, in CPython, some built-in functions defined in C provide no metadata about their arguments.

But they also say:

Raises ValueError if no signature can be provided, and TypeError if that type of object is not supported.

So I am surprised that I did not get a ValueError and instead got what appears to be an incorrect signature.

Is there a way to reliably (and programmatically) determine when it is not possible to get the parameters for a callable with inspect? That is, if I am given something like int, is there a way to distinguish between "this thing does not have any parameters" and "it is not possible to determine what parameters this thing has"?

like image 410
Laurence Gonsalves Avatar asked Apr 06 '15 18:04

Laurence Gonsalves


1 Answers

There's an open bug report for this: http://bugs.python.org/issue23934

The problem is that if the obj passed to signature() is a 'type', and it doesn't have a user defined init or new, it just gives up and returns the signature for object, which has 0 parameters.

If the obj is in the builtins module, and it's a 'type', but not an Exception, there's a pretty good chance it's not going to work with signature().

I came up with this workaround to find the problem objs... It's not particularly elegant, but it may be useful to you:

def count_params():
    output = {}
    for funcname in dir(builtins):
        f = getattr( builtins,funcname)
        if isinstance(f, type):
            try:
                paramcount = len(inspect.signature(f).parameters)
                output[funcname] = paramcount
            except:
                pass
                #print("%s is a %s" % (funcname, type(f)))
    return output

Output:

{'__loader__': 0,
 'bool': 0,
 'bytes': 0,
 'complex': 0,
 'enumerate': 0,
 'filter': 0,
 'float': 0,
 'frozenset': 0,
 'int': 0,
 'map': 0,
 'memoryview': 0,
 'object': 0,
 'range': 0,
 'reversed': 0,
 'slice': 0,
 'str': 0,
 'tuple': 0,
 'zip': 0}
like image 187
1.618 Avatar answered Oct 01 '22 22:10

1.618