Quick poll: should help() show bound arguments?

(Quick, because apparently nobody reads the long ones!) In Python 3.3: >>> class C: ... def foo(self, a): pass ... >>> c = C() >>> help(c.foo) shows you the signature "foo(self, a)". As in, it claims it accepts two parameters. The function actually only accepts one parameter, because "self" has already been bound. inspect.signature gets this right: >>> import inspect >>> str(inspect.signature(c.foo)) '(a)' but inspect.getfullargspec does not: >>> inspect.getfullargspec(c.foo) FullArgSpec(args=['self', 'a'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) help() gets its text from pydoc. pydoc uses inspect.getfullargspec to produce the signature. When I added support for introspection on builtins, I wanted help() to show their signature too. But inspect.getfullargspec doesn't support introspection on builtins. So I had to use inspect.signature. Which means the behavior is inconsistent: help() on a method of an instance of a builtin class *doesn't* show "self". FYI, the relevant issues: help(instance_of_builtin_class.method) does not display self http://bugs.python.org/issue20379 inspect.getfullargspec should use __siganture__ http://bugs.python.org/issue17481 What should it be? A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec. I'll tally the results if there's interest. I'd assume a "vote for A" = +1 on A and -1 on B. You can express your vote numerically if you like. I'm voting for A. And yes, that was short for me, //arry/

On Fri, Jan 24, 2014 at 10:07 PM, Larry Hastings <larry@hastings.org> wrote:
(Quick, because apparently nobody reads the long ones!)
In Python 3.3:
class C: ... def foo(self, a): pass ... c = C() help(c.foo)
shows you the signature "foo(self, a)". As in, it claims it accepts two parameters. The function actually only accepts one parameter, because "self" has already been bound. inspect.signature gets this right:
import inspect str(inspect.signature(c.foo)) '(a)'
but inspect.getfullargspec does not:
inspect.getfullargspec(c.foo) FullArgSpec(args=['self', 'a'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
help() gets its text from pydoc. pydoc uses inspect.getfullargspec to produce the signature.
When I added support for introspection on builtins, I wanted help() to show their signature too. But inspect.getfullargspec doesn't support introspection on builtins. So I had to use inspect.signature. Which means the behavior is inconsistent: help() on a method of an instance of a builtin class *doesn't* show "self".
FYI, the relevant issues:
help(instance_of_builtin_class.method) does not display self
http://bugs.python.org/issue20379
inspect.getfullargspec should use __siganture__
http://bugs.python.org/issue17481
What should it be?
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec.
I'll tally the results if there's interest. I'd assume a "vote for A" = +1 on A and -1 on B. You can express your vote numerically if you like. I'm voting for A.
I vote A: it makes sense (though B does too, to an extent), and the patch to make help() consistent for Python and C implemented methods is simply removing two lines from pydoc. I'm not sure how convoluted it might become to make it work the other way. -- Zach

On Fri, Jan 24, 2014 at 08:07:43PM -0800, Larry Hastings wrote:
(Quick, because apparently nobody reads the long ones!)
In Python 3.3:
>>> class C: ... def foo(self, a): pass ... >>> c = C() >>> help(c.foo)
shows you the signature "foo(self, a)".
That matches the function declaration as defined right there in the class.
As in, it claims it accepts two parameters. The function actually only accepts one parameter, because "self" has already been bound.
No, that's wrong. The *function* C.foo accepts two parameters, self and a, exactly as declared. The *method* c.foo accepts only one.
inspect.signature gets this right:
>>> import inspect >>> str(inspect.signature(c.foo)) '(a)'
but inspect.getfullargspec does not:
>>> inspect.getfullargspec(c.foo) FullArgSpec(args=['self', 'a'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
That's arguable. Should inspect give the argument spec of the method object or the function object? I can see arguments for both. Backwards-compatibility argues against changing either, though. help() is another story. Since help() is documentation aimed at the end-user, we can change it without worrying about backward compatibility. Ideally, help() ought to distingush between the two cases. I see that it already does, but not very well. help(c.foo) in Python 3.3 gives: Help on method foo in module __main__: foo(self, a) method of __main__.C instance while help(C.foo) gives: Help on function foo in module __main__: foo(self, a) I think in the bound method case, it should drop the "self": Help on method foo in module __main__: foo(a) method of __main__.C instance and in the function (previously: unbound method) case, it should show the "self" and the class: Help on function foo in module __main__: foo(self, a) method of class __main__.C although I'm not sure how that second one is possible, now that unbound methods are gone and C.foo returns a plain function. (Hmmm, perhaps getting rid of unbound methods was a mistake...)
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec.
Provided they are described as *methods* rather than *functions*, bound methods should not show self. Likewise for classmethod and cls. That is, I'm voting +1 on A provided help() doesn't confuse methods and functions. -- Steven

On 25 January 2014 14:36, Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, Jan 24, 2014 at 08:07:43PM -0800, Larry Hastings wrote:
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec.
Provided they are described as *methods* rather than *functions*, bound methods should not show self. Likewise for classmethod and cls. That is, I'm voting +1 on A provided help() doesn't confuse methods and functions.
+1 from me for fixing the signature help() and pydoc report for bound methods that are reported as such. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 01/24/2014 11:07 PM, Larry Hastings wrote:
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec.
+1 for A: it is how you would actually call the object on which 'help()' is being called. The fact that self will be passed silently is irrelevant to the caller. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iEYEARECAAYFAlLjU4UACgkQ+gerLs4ltQ48SQCg0zMZseKV3EZ/0pRkc5ngt2tb aFMAn0Vhze2wMEim6Vf7F1fvlh+j3PJ/ =Mx/i -----END PGP SIGNATURE-----

Larry Hastings wrote:
inspect.signature gets this right:
>>> import inspect >>> str(inspect.signature(c.foo)) '(a)'
Not always. : Python 3.4.0b2+ (default:32f9e0ae23f7, Jan 18 2014, 13:56:31) : [GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin : Type "help", "copyright", "credits" or "license" for more information. : >>> import inspect : >>> class C1: : ... def f(*args, **kwargs): pass : ... : >>> c = C1() : >>> c.f() : >>> str(inspect.signature(c.f)) : '(**kwargs)' Not to mention: class C2: def g(**kwargs): pass It doesn't really make sense - calling C2().g is guaranteed to fail - but it's legal Python. I'm not saying you can't special-case a few things and fix this, but still, -1/B. I like explicit self. regards, Anders

On 01/25/2014 03:34 AM, Anders J. Munch wrote:
Larry Hastings wrote:
inspect.signature gets this right:
>>> import inspect >>> str(inspect.signature(c.foo)) '(a)'
Not always.
: Python 3.4.0b2+ (default:32f9e0ae23f7, Jan 18 2014, 13:56:31) : [GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)] on darwin : Type "help", "copyright", "credits" or "license" for more information. : >>> import inspect : >>> class C1: : ... def f(*args, **kwargs): pass : ... : >>> c = C1() : >>> c.f() : >>> str(inspect.signature(c.f)) : '(**kwargs)'
File a bug, if there hasn't already been one filed. //arry/

On Sat, Jan 25, 2014 at 3:07 PM, Larry Hastings <larry@hastings.org> wrote:
What should it be?
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature. B) pydoc and help() should show bound parameters in the signature, like inspect.getfullargspec.
Vote for A. As far as I'm concerned, all these foo are equally callable and equally take one parameter named a: def foo1(a): pass class C: def foo(self, a): pass foo2=C().foo class C: def __call__(self, a): pass foo3=C() def two_arg(b, a): pass foo4=lambda a: two_arg(0, a) If I call them as fooN(), fooN(1), and fooN(1,2), the middle one works and the other two throw exceptions, ergo they are one-argument functions. The fact that two of them happen to be bound methods is an implementation detail; it's just a form of currying, which foo4 happens also to be (in that C.foo takes two args, C().foo takes one). ChrisA

On 01/25/2014 04:34 AM, Chris Angelico wrote:
On Sat, Jan 25, 2014 at 3:07 PM, Larry Hastings <larry@hastings.org> wrote:
What should it be?
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature.
Vote for A. As far as I'm concerned, all these foo are equally callable and equally take one parameter named a:
[snip] To strengthen this argument: --> import inspect --> from functools import partial --> def lots_of_args(a, b, c, d=3, e='wow', f=None): ... print(a, b, c, d, e, f) ... --> str(inspect.signature(lots_of_args)) "(a, b, c, d=3, e='wow', f=None)" --> curried = partial(lots_of_args, 9, f='Some') --> str(inspect.signature(curried)) "(b, c, d=3, e='wow', f='Some')" While I partially agree with Antoine that the whole self thing is confusing, I think it would be more accurate to only give help (and a signature) on parameters that you can actually change; if you are calling a bound method there is no way to pass in something else in place of self. So I vote for A. -- ~Ethan~

On Fri, 24 Jan 2014 20:07:43 -0800 Larry Hastings <larry@hastings.org> wrote:
A) pydoc and help() should not show bound parameters in the signature, like inspect.signature.
-1 from me. The problem is this will make help(c.foo) inconsistent with help(c) and help(C), and is bound to confuse newcomers. Speaking of which, I think asking for votes before all arguments have been made is counter-productive. Regards Antoine.

On Sat, 25 Jan 2014 05:42:58 -0800 Larry Hastings <larry@hastings.org> wrote:
On 01/25/2014 05:37 AM, Antoine Pitrou wrote:
Speaking of which, I think asking for votes before all arguments have been made is counter-productive.
Sorry, I didn't realize there was an established protocol for this.
No, the problem is that people then vote assuming you have done all the research and presented all arguments for and against, which you haven't. Regards Antoine.
participants (9)
-
Anders J. Munch
-
Antoine Pitrou
-
Chris Angelico
-
Ethan Furman
-
Larry Hastings
-
Nick Coghlan
-
Steven D'Aprano
-
Tres Seaver
-
Zachary Ware