Reasons behind misleading TypeError message when passing the wrong number of arguments to a method
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2? --- Giampaolo http://code.google.com/p/pyftpdlib http://code.google.com/p/psutil
On 20/05/2010 00:42, Giampaolo Rodolà wrote:
class A:
... def echo(self, x): ... return x ...
a = A() a.echo()
Traceback (most recent call last): File "<stdin>", line 1, in<module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
+1 - I've seen many newbies confused by this error and I sometimes do a double-take myself. All the best, Michael
--- Giampaolo http://code.google.com/p/pyftpdlib http://code.google.com/p/psutil _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies (”BOGUS AGREEMENTS”) that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
Giampaolo Rodolà wrote:
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
Because you wouldn't want to have A.echo() Say that it takes 1 argument and (-1 given) ? John =:->
John Arbash Meinel wrote:
Because you wouldn't want to have
A.echo()
Say that it takes 1 argument and (-1 given) ?
Something like "1 argument in addition to 'self'" would be reasonably clear and would cover both situations. +1 on fixing this from me, too. Even when you understand exactly what's going on, it's annoying having to make the mental adjustment every time. -- Greg
Greg Ewing
John Arbash Meinel wrote:
Because you wouldn't want to have
A.echo()
Say that it takes 1 argument and (-1 given) ?
Something like "1 argument in addition to 'self'" would be reasonably clear and would cover both situations.
Except that there's nothing special to the syntax or parser about the name ‘self’.
+1 on fixing this from me, too. Even when you understand exactly what's going on, it's annoying having to make the mental adjustment every time.
Would it help if the traceback showed the ‘repr()’ of each of the arguments received? That way it would be much clearer when the instance was received as the first argument. -- \ “Any sufficiently advanced bug is indistinguishable from a | `\ feature.” —Rich Kulawiec | _o__) | Ben Finney
Ben Finney wrote:
Something like "1 argument in addition to 'self'" would be reasonably clear and would cover both situations.
Except that there's nothing special to the syntax or parser about the name ‘self’.
That's true, but the use of the word 'self' here isn't meant to refer to the name of a parameter. The message is aimed at the caller of the function, who doesn't necessarily know or care what the parameter is actually called. The important thing is that it represents the object the method is being called for, and 'self' is the traditional term used when talking about that. I can't think of anything that would be more accurate without being excessively verbose or pedantic. -- Greg
On Fri, 21 May 2010 10:53:16 am Greg Ewing wrote:
Ben Finney wrote:
Something like "1 argument in addition to 'self'" would be reasonably clear and would cover both situations.
Except that there's nothing special to the syntax or parser about the name ‘self’.
That's true, but the use of the word 'self' here isn't meant to refer to the name of a parameter. The message is aimed at the caller of the function, who doesn't necessarily know or care what the parameter is actually called.
And who doesn't necessarily know that:
The important thing is that it represents the object the method is being called for, and 'self' is the traditional term used when talking about that.
Referring to self in any such error message is going to confuse newbies, and occasional Python programmers who use it without learning about object oriented code. E.g. sys admins who use Python as a scripting language without ever writing their own classes. Error messages should be aimed at the least capable users (within reason!) rather than the most capable, since the most capable are: (1) less likely to make the error in the first place; (2) more able to interpret the error message correctly; (3) more likely to understand if the error relates to something "under the hood"; and (4) better able to debug the error even in the absence of a useful error message. Given this, it is better for the error message to relate to what the "average" user most likely has in his source code (i.e. a bound method) rather than what the better-than-average user occasionally uses (an unbound method).
I can't think of anything that would be more accurate without being excessively verbose or pedantic.
In Python 3.1, strings seem to behave more sensibly IMO:
'abc'.find() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: find() takes at least 1 argument (0 given)
That looks right to me. I'm calling a *method* with zero arguments inside the parentheses when I need to call it with one. If I call it as an unbound method (yes, I know it's actually a function) I get something more confusing:
str.find('abc') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: find() takes at least 1 argument (0 given)
While calling unbound methods isn't exactly rare, it is a more advanced thing to do than calling bound methods. If we're stuck with confusing messages, better to have it occur for the users who are more likely to be able to interpret it, that is, those advanced enough to use unbound methods rather than "ordinary" users. Note that str avoids the "-1 argument" trap:
str.find() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: descriptor 'find' of 'str' object needs an argument
Classes behave in the opposite manner: the error message is technically correct for the less common, more advanced case, but misleading for the more common, simple case:
class C: ... def method(self, x): ... return x+1 ... C.method() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: method() takes exactly 2 positional arguments (0 given)
This is exactly right, since C.method is a function object which takes two arguments, self and x. Because this is a function, we can't change this (not that we want to!) without impacting the errors you get when calling functions not associated with a class.
C().method() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: method() takes exactly 2 positional arguments (1 given)
This is misleading, since C().method is a bound method which takes one argument, x, not two. I find myself wishing that Python distinguished between ArgumentError and other TypeErrors, so that the method wrapper of bound methods could simply catch ArgumentError and subtract 1 from each argument count. -- Steven D'Aprano
On Thu, May 20, 2010 at 22:54, Steven D'Aprano
This is misleading, since C().method is a bound method which takes one argument, x, not two. I find myself wishing that Python distinguished between ArgumentError and other TypeErrors, so that the method wrapper of bound methods could simply catch ArgumentError and subtract 1 from each argument count.
But how exactly could it distinguish between various levels of call nesting? class C: def a(self, i): return self.b(i) def b(self, j): return self.c(j) def c(self, k): return self.a() What should C.a(), C().a(), and C().a(1) each yield? Does it change if c(self, k) calls C.a(self)? -- Michael Urman
On 20May2010 17:46, Ben Finney
Cameron Simpson
On 20May2010 17:46, Ben Finney
wrote: | Would it help if the traceback showed the ‘repr()’ of each of the | arguments received? That way it would be much clearer when the instance | was received as the first argument. I've occasionally passed large or deep dicts etc to functions and foolishly printed their repr in debug statements. They can be... wordy. Maybe a cropped repr, or the .__class__ attribute?
Okay. My main point is that I'm offering this as a way of avoiding a special-case message for method calls versus non-method calls. In Python, all function calls are essentially the same, and I think it's too complicated for the message to differ depending on the syntax used. Emitting the arguments in some form, or even the types of each argument, in the message would make it clearer without a special case. -- \ “We now have access to so much information that we can find | `\ support for any prejudice or opinion.” —David Suzuki, 2008-06-27 | _o__) | Ben Finney
2010/5/20 John Arbash Meinel
Giampaolo Rodolà wrote:
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
Because you wouldn't want to have
A.echo()
Say that it takes 1 argument and (-1 given) ?
John =:->
I see that as a different error type: what you're doing there is calling a method of a class which you haven't instantiated in the first place. Actually the error message returned in this other case is not very clear as well: "unbound method echo() must be called with A instance as first argument (got nothing instead)" It talks about "arguments" while no arguments are actually involved in the problem: just a class I forgot to initialize. --- Giampaolo http://code.google.com/p/pyftpdlib http://code.google.com/p/psutil
On 20/05/2010 10:49, Giampaolo Rodolà wrote:
2010/5/20 John Arbash Meinel
: Giampaolo Rodolà wrote:
class A:
... def echo(self, x): ... return x ...
a = A() a.echo()
Traceback (most recent call last): File "<stdin>", line 1, in<module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
Because you wouldn't want to have
A.echo()
Say that it takes 1 argument and (-1 given) ?
John =:->
I see that as a different error type: what you're doing there is calling a method of a class which you haven't instantiated in the first place. Actually the error message returned in this other case is not very clear as well:
"unbound method echo() must be called with A instance as first argument (got nothing instead)"
It talks about "arguments" while no arguments are actually involved in the problem: just a class I forgot to initialize.
Although the pattern of calling an unbound method with an instance as the first argument (self) is not uncommon - and if you're doing Class.method() that not only looks like what you're doing but is also an accurate error message. I would also expect that accidentally calling unbound methods is a much less common error than calling bound methods with the wrong number of arguments... All the best, Michael Foord
--- Giampaolo http://code.google.com/p/pyftpdlib http://code.google.com/p/psutil _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog READ CAREFULLY. By accepting and reading this email you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies (”BOGUS AGREEMENTS”) that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.
On Thu, 20 May 2010 11:49:02 +0200, =?ISO-8859-1?Q?Giampaolo_Rodol=E0?=
2010/5/20 John Arbash Meinel
: a.echo() Traceback (most recent call last): =A0 File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
Because you wouldn't want to have
A.echo()
Say that it takes 1 argument and (-1 given) ?
I see that as a different error type: what you're doing there is calling a method of a class which you haven't instantiated in the first place. Actually the error message returned in this other case is not very clear as well:
"unbound method echo() must be called with A instance as first argument (got nothing instead)"
It talks about "arguments" while no arguments are actually involved in the problem: just a class I forgot to initialize.
That second message is entirely accurate and IMO should not be changed. As Michael said, calling an unbound method is not that uncommon. The problem with fixing the first message (as you will see if you read issue 2516) is that it turns out to be non-trivial to do it right. Otherwise I think it would have been fixed already. -- R. David Murray www.bitdance.com
Giampaolo Rodolà wrote:
"unbound method echo() must be called with A instance as first argument (got nothing instead)"
It talks about "arguments" while no arguments are actually involved in the problem: just a class I forgot to initialize.
It's hard to see how this could be improved. If you had been intending to call an unbound method, the comment about arguments would have been relevant -- you were supposed to supply one but didn't. Python is unable to read your mind and figure out that you really meant something else altogether. BTW, unbound methods no longer exist in Py3, so you would get the standard message about incorrect number of arguments instead. Not sure whether that's better or worse in this situation. -- Greg
On Wed, May 19, 2010 at 4:42 PM, Giampaolo Rodolà
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
--- Giampaolo
Because it actually does take two arguments (self and x) and it only got one (self). I understand the confusion (and was bitten by it myself when I was a newbie) but the interpreter is only telling you the truth. Geremy Condra
On Thu, 20 May 2010 01:42:08 +0200, =?ISO-8859-1?Q?Giampaolo_Rodol=E0?=
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
You can contribute to issue 2516 if you like :) http://bugs.python.org/issue2516 -- R. David Murray www.bitdance.com
Giampaolo Rodolà writes:
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
As a function, it does take two arguments, and can be called explicitly that way, no? Adjustment is not enough, the message needs to be substantially rewritten. Something like TypeError: invoked as a method, echo() takes exactly 1 argument (0 given) captures the semantics, but is perhaps too verbose.
On Thu, May 20, 2010 at 11:55:02AM +0900, Stephen J. Turnbull wrote:
Giampaolo Rodolà writes:
class A: ... def echo(self, x): ... return x ... a = A() a.echo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: echo() takes exactly 2 arguments (1 given)
I bet my last 2 cents this has already been raised in past but I want to give it a try and revamp the subject anyway. Is there a reason why the error shouldn't be adjusted to state that *1* argument is actually required instead of 2?
As a function, it does take two arguments, and can be called explicitly that way, no? Adjustment is not enough, the message needs to be substantially rewritten. Something like
TypeError: invoked as a method, echo() takes exactly 1 argument (0 given)
captures the semantics, but is perhaps too verbose.
How about: TypeError: bound method echo() takes exactly 1 argument (0 given) That way you can also have: "unbound method echo() ...". And it's as semantically correct as the short "echo() takes ..." Not having looked at the code I don't know how hard it is for the code that raises this traceback to notice if it's a bound or unbound method tough. Regards Floris -- Debian GNU/Linux -- The Power of Freedom www.debian.org | www.gnu.org | www.kernel.org
On 5/20/2010 4:02 AM, Floris Bruynooghe wrote:
TypeError: invoked as a method, echo() takes exactly 1 argument (0 given)
captures the semantics, but is perhaps too verbose.
How about:
TypeError: bound method echo() takes exactly 1 argument (0 given)
That way you can also have: "unbound method echo() ...". And it's as semantically correct as the short "echo() takes ..."
Not having looked at the code I don't know how hard it is for the code that raises this traceback to notice if it's a bound or unbound method tough.
In 3.x, there are no unbound method objects, just functions. But that should make the difference *easier* to detect, as bound/unbound method objects were actually the same class, differing only in an attribute. I notice
list.append() Traceback (most recent call last): File "
", line 1, in <module> list.append() TypeError: descriptor 'append' of 'list' object needs an argument
So here the message is specific to the type of the callable.
Floris Bruynooghe wrote:
Not having looked at the code I don't know how hard it is for the code that raises this traceback to notice if it's a bound or unbound method tough.
The way things currently work, it would be quite difficult. The exception is raised when attempting to call the function underlying the bound method, at which point it can't be distinguished from any other way of calling it. However, it could be made to raise a special subclass of TypeError that the bound method wrapper could catch and replace with its own exception. -- Greg
participants (13)
-
Ben Finney
-
Cameron Simpson
-
Floris Bruynooghe
-
geremy condra
-
Giampaolo Rodolà
-
Greg Ewing
-
John Arbash Meinel
-
Michael Foord
-
Michael Urman
-
R. David Murray
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Terry Reedy