classification
Title: inspect.Signature doesn't support user classes without __init__ or __new__
Type: behavior Stage: needs patch
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: brett.cannon, larry, ncoghlan, python-dev, skrah, yselivanov
Priority: normal Keywords: patch

Created on 2014-01-19 21:52 by larry, last changed 2014-02-03 08:35 by larry. This issue is now closed.

Files
File name Uploaded Description Edit
signature_plain_cls_01.patch yselivanov, 2014-01-19 23:32 review
signature_plain_cls_02.patch yselivanov, 2014-01-19 23:58 review
signature_plain_cls_04.patch yselivanov, 2014-01-27 21:08 review
Messages (17)
msg208502 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-19 21:52
Boy was I surprised by this:

>>> class C: pass
... 
>>> import inspect
>>> inspect.signature(C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/larry/src/python/clinic_c_types_hacking/Lib/inspect.py", line 1557, in signature
    raise ValueError('callable {!r} is not supported by signature'.format(obj))
ValueError: callable <class '__main__.C'> is not supported by signature

Why not?  C is a callable object.  If you can't find a __new__ or an __init__ in the class, its signature should be pretty predictable.

(It returns a signature if you add either a __new__, an __init__, or both.)
msg208504 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 22:06
Well, the current code looks for __init__ or __new__. The only ones it can find is the 'object.__init__' which are blacklisted, because they are in C.

The question is do (or will) 'object.__new__' or '__init__' have a signature defined with the clinic?
msg208507 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-19 22:42
__new__ and __init__ methods are very special.  They can't have signatures, because the mechanism we use to store the signatures won't work.  (We hide them as a special first line of the docstring, and __new__ and __init__ can't have custom docstrings.)

In my next patch for #20189,

inspect.signature(object)

will return a valid signature object.  But

class C: pass
inspect.signature(C)

still fails in that branch.
msg208508 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 22:48
In this case it would probably be best to just special case classes that don't have __init__ or __new__ defined to return an empty signature without parameters.

I can also make a special case for object.__init__ and object.__new__ functions, if someone would want to introspect them directly.
msg208510 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-19 22:57
If we need a special case for user classes without __new__ or __init__, then do that.  But I wouldn't say we should special case object.__new__ and object.__init__.
msg208511 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 23:32
Please take a look at the attached patch (signature_plain_cls_01.patch)

Now, the patch addresses two kind of classes:

class C: pass
and 
class C(type): pass

For metaclasses, signature will return a signature with three positional-only parameters - (name, bases, dct). [let's discuss this]

The patch doesn't address 'object' or 'type' objects directly, though, so 'signature(object)' and 'signature(type)' are still a ValueError.
msg208512 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-19 23:36
inspect.signature(object) works fine in my (not yet posted) latest #20189 patch.

inspect.signature(type) doesn't work, because it's not clear what the signature for type should be.  There's the one-argument and three-argument approaches.  This is technically true:

    "(object_or_name, [bases, dict])"

However,
a) it's painful to look at,
b) I can't communicate the idea of an "optional group" in an inspect.Signature object right now (although I guess we're going to hash that out somewhere, which is good).

If we can agree on a good signature for inspect.signature(type), I can make it happen.
msg208513 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 23:44
Couple of thoughts:

1. "(object_or_name, [bases, dict])" is a signature for the "type" function, and yes, on that we need to agree how it looks like. Maybe exactly as you proposed, as it is what it is after all.

2. For user-defined metaclasses without __init__ or __new__ (such as "class C(type)"), we can just return, IMO, "(name, bases, dict)", as, although, it is possible to call "C" with only one argument, it's hardly a good practice, and I doubt it very much that someone does such things.
msg208514 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-19 23:47
Special cases aren't special enough to break the rules.  If the signature of a metaclass is "(object_or_name, [bases, dict])", then we must not special-case it to pretend that "(object)" works.  I agree it's a bad idea to actually *do* that, but there are best-practice principles at stake here.
msg208515 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 23:50
When in doubt, import this ;)

Agree. So the best course would be: make a patch for plain classes (not metaclasses). Fix signatures for metaclasses without __init__/__new__ when we have groups support for parameters, so that we can have (obj_or_name, [bases, dict]) kind of signatures.
msg208516 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-19 23:58
Attached is a stripped down patch that special-cases classes without __init__/__new__. Not metaclasses, for that we can start a new issue.
msg209481 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2014-01-27 21:08
Larry,

Please take a look at the attached 'signature_plain_cls_04.patch'.
This one supports classes (returns signature of 'object' builtin), and metaclasses (returns signature of 'type' builtin).
msg209489 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-28 00:08
Looks perfect!  Please check it in.
msg209490 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-01-28 00:08
(With a Misc/NEWS entry of course.)
msg209491 - (view) Author: Roundup Robot (python-dev) Date: 2014-01-28 00:31
New changeset b9ca2019bcb9 by Yury Selivanov in branch 'default':
inspect.signature: Support classes without user-defined __init__/__new__ #20308
http://hg.python.org/cpython/rev/b9ca2019bcb9
msg209522 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2014-01-28 09:36
One test fails --without-doc-strings:

http://buildbot.python.org/all/builders/AMD64%20FreeBSD%209.0%203.x/builds/6266/steps/test/logs/stdio
msg210098 - (view) Author: Larry Hastings (larry) * (Python committer) Date: 2014-02-03 08:35
That buildbot is happy now.  Thanks for pointing it out!
History
Date User Action Args
2014-02-03 08:35:36larrysetmessages: + msg210098
2014-01-28 09:36:33skrahsetnosy: + skrah
messages: + msg209522
2014-01-28 00:32:34yselivanovsetstatus: open -> closed
resolution: fixed
2014-01-28 00:31:11python-devsetnosy: + python-dev
messages: + msg209491
2014-01-28 00:08:40larrysetmessages: + msg209490
2014-01-28 00:08:25larrysetmessages: + msg209489
2014-01-27 21:08:35yselivanovsetfiles: + signature_plain_cls_04.patch

messages: + msg209481
2014-01-19 23:58:00yselivanovsetfiles: + signature_plain_cls_02.patch

messages: + msg208516
2014-01-19 23:50:20yselivanovsetmessages: + msg208515
2014-01-19 23:47:22larrysetmessages: + msg208514
2014-01-19 23:44:22yselivanovsetmessages: + msg208513
2014-01-19 23:36:58larrysetmessages: + msg208512
2014-01-19 23:32:07yselivanovsetfiles: + signature_plain_cls_01.patch
keywords: + patch
messages: + msg208511
2014-01-19 22:57:51larrysetmessages: + msg208510
2014-01-19 22:48:26yselivanovsetmessages: + msg208508
2014-01-19 22:42:53larrysetmessages: + msg208507
2014-01-19 22:06:12yselivanovsetmessages: + msg208504
2014-01-19 21:52:39larrycreate