11

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"?

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • Maybe this will help: [What are `__signature__` and `__text_signature__` used for in Python 3.4](http://stackoverflow.com/questions/25847035/what-are-signature-and-text-signature-used-for-in-python-3-4) – myaut Apr 06 '15 at 19:39

1 Answers1

3

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}
1.618
  • 1,765
  • 16
  • 26
  • Thanks for the bug link. There's also the issue of subclasses of these types. For example, `inspect.signature` incorrectly returns an empty signature for any subclass of `int` that does not define its own `__init__`. (If the subclass defines its own `__init__` then the correct signature is returned.) @user1941126's (deleted) answer actually suggests a pretty good augmentation to your answer: if the callable has 0 parameters and is a type, then check the signature of `__init__`. If `__init__` has more than 1 parameter then something fishy is going on. – Laurence Gonsalves Apr 21 '15 at 22:20