Issue 20828: inspect.getargspec() returns wrong answer with datetime.today.call() (original) (raw)

Created on 2014-03-02 15:10 by zzzeek, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (6)

msg212556 - (view)

Author: mike bayer (zzzeek) *

Date: 2014-03-02 15:10

this appears like it may be related to http://bugs.python.org/issue20786, at least in terms of inspect.getargspec() seems to be returning answers in 3.4 where it used to raise TypeError, however like in 20786 it's again returning the wrong answer. I'm a little concerned that some change was made to allow inspection of lots of builtins that wasn't before, but the argument lists haven't been checked.

test case:

import inspect import datetime

try: insp = inspect.getargspec(datetime.datetime.today.call) except TypeError: pass else: print(insp) assert insp == (["self"], "args", "kwargs", None)

# claims to accept "args" and "kwargs", ok let's try...
datetime.datetime.today.__call__(1, 2, foo='bar')

# raises:
# TypeError: today() takes no keyword arguments

msg212562 - (view)

Author: mike bayer (zzzeek) *

Date: 2014-03-02 15:38

I've also worked around this on my end, so if my poking into today.call() isn't really a good idea anyway, I'm not doing that anymore.

msg212563 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2014-03-02 17:12

Why are you trying to get signature of 'datetime.datetime.today.call'?

call in this case is a generic python object -- method-wrapper. It is used to make a classmethod. And the signature that getargspec is returning for it is correct.

msg212568 - (view)

Author: mike bayer (zzzeek) *

Date: 2014-03-02 18:07

we basically need to be able to get the argument signature for anything that passes callable(); function, method, class, object with call. So this is the logic we use:

import inspect

def get_callable_argspec(fn): if inspect.isfunction(fn) or inspect.ismethod(fn): inspectable = fn elif inspect.isclass(fn): inspectable = fn.init elif hasattr(fn, 'call'): inspectable = fn.call else: inspectable = fn

try:
    return inspect.getargspec(inspectable)
except TypeError:
    raise

def callable1(self, x, y): pass

class SomeClass(object): def init(self, x, y): pass

def callable2(self, x, y):
    pass

def __call__(self, x, y):
    pass

callable3 = SomeClass callable4 = SomeClass(2, 3)

for callable_ in (callable1, SomeClass(1, 2).callable2, callable3, callable4): assert callable(callable_) # the interpreter can tell me this

# how can it reliably tell me this? 
assert get_callable_argspec(callable_) == (["self", "x", "y"], None, None, None)

If you pass a builtin like datetime.datetime.today to it, isfunction()/ismethod()/isclass() return false, but it does have a call().

I'm working around this now by just refusing to act on anything that is types.BuiltinMethodType or types.BuiltinFunctionType.

Any guidance on what the proper way is to get the argument signature for any object that returns True for callable() would be very helpful (py2.6-3.x compatible). I'm not sure if there's a stdlib call for this.

msg212591 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2014-03-02 21:17

OK, I see.

I'd recommend you to take a look how inspect.signature is implemented in 3.3 or 3.4 (and maybe backport it to python 2 and use the new API).

To quickly fix your code, I'd suggest the following modifications:

_WrapperDescriptor = type(type.call) _MethodWrapper = type(all.call) _ClassMethodWrapper = type(int.dict['from_bytes'])

def get_callable_argspec(fn): if inspect.isfunction(fn) or inspect.ismethod(fn): inspectable = fn elif inspect.isclass(fn): inspectable = fn.init elif hasattr(fn, 'call'): inspectable = fn.call else: inspectable = fn

if isinstance(fn, (_WrapperDescriptor, _MethodWrapper, _ClassMethodWrapper)):
    raise ValueError('unsupported callable {!r}'.format(fn))

try:
    return inspect.getargspec(inspectable)
except TypeError:
    raise

I'm closing this issue, as there is no real bug or regression in getargspec.

msg212595 - (view)

Author: mike bayer (zzzeek) *

Date: 2014-03-02 22:04

I've got something like that going on right now, but am doing it in the other direction; when I look at call(), I only do anything with it if it passes inspect.ismethod(). Since I only want plain Python call() functions on plain Python objects.

History

Date

User

Action

Args

2022-04-11 14:57:59

admin

set

github: 65027

2014-03-02 22:04:51

zzzeek

set

messages: +

2014-03-02 21:17:12

yselivanov

set

status: open -> closed
resolution: not a bug
messages: +

2014-03-02 19:52:05

Arfrever

set

nosy: + Arfrever

2014-03-02 18:07:40

zzzeek

set

messages: +

2014-03-02 17:13:04

yselivanov

set

nosy: + ncoghlan

2014-03-02 17:12:51

yselivanov

set

nosy: + yselivanov
messages: +

2014-03-02 15:38:39

zzzeek

set

messages: +

2014-03-02 15:19:26

r.david.murray

set

nosy: + larry

2014-03-02 15:10:47

zzzeek

create