Showing qualified names when a function call fails

I personally find it kind of annoying when you have code like this: x = A(1, B(2, 3)) and Python's error message looks like this: TypeError: __init__() takes 1 positional argument but 2 were given It doesn't give much of a clue to which `__init__` is being called. At all. The idea: when showing the function name in an error like this, show the fully qualified name, like: TypeError: A.__init__() takes 1 positional argument but 2 were given This would be MUCH more helpful! Another related change would be to do the same thing in tracebacks: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in __init__ AssertionError to: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in MyClass.__init__ AssertionError which could make it easier to find where exactly an error originated. -- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

Also, for something like this: In [1]: class A: ...: pass ...: In [2]: A(x=2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-857108bff989> in <module>() ----> 1 A(x=2) TypeError: object() takes no parameters It would be nice to say TypeError: object() takes no parameters, but keyword argument "x" given. I understand that the value of "x" might not have a __repr__ method, but the key name has to be string, so this should be easily doable at least for extra keyword arguments? For positional arguments, maybe just print how many were passed to object? Knowing the key name would have helped me with debugging. Usually, I print(kwargs) somewhere up the inheritance chain and run my program again. On Tuesday, October 25, 2016 at 3:19:57 AM UTC-4, Neil Girdhar wrote:

Thos are great idea. I love the current trend of trying to give better error messages. Another pet peave of mine: TypeError not providing at least 'raiser' 'accepted type', 'given value' and 'given value type'. E.G, int() does an ok job: >>> int(foo) ValueError: invalid literal for int() with base 10: '1O' raiser: int() accept type: a base 10 literal value given: 1O But it could be improved by givin the type of the give value. Indeed in that case, I got a string composed of one and the letter O, but looks like the number 10. Some students can struggle with those. list, set and tuple less not as good: >>> tuple(foo) TypeError: 'int' object is not iterable No raiser, no value given. It's hard to find out what's the problem is. The biggest issue here is that if you have a long line with tuple() in the middle, yuou need to know the problem comes from tuple. Another problem is that many people don't know what iterable means. A better error message would be: TypeError: tuple() only accept iterables (any object you can use a for loop on). But it received '1', which is of type <int>. Some things deserve a big explanation to solve the problem. It would be nice to add a link to official tutorial in the documentation. E.G, encoding is a big one: In [8]: b'é' + 'é' File "<ipython-input-8-cfac1add5f26>", line 1 b'é' + 'é' ^ SyntaxError: bytes can only contain ASCII literal characters. This is not helpful to somebody unaware of the difference between text and bytes. Possible solution: In [8]: b'é' + 'é' File "<ipython-input-8-cfac1add5f26>", line 1 b'é' + 'é' ^ SyntaxError: You cannnot concatenate bytes (b'é...') with a string ('é...'). Learn more about fixing this error at https://doc.python.org/errors/7897978 Of course, the repr will often need to be shorten but a short repr is better than none. Should we make a PEP with all of those ? Le 25/10/2016 à 09:23, Neil Girdhar a écrit :

On Wed, Oct 26, 2016 at 12:20 AM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
-1 on this one. It doesn't really add very much - "iterable" is a good keyword that anyone can put into a search engine. Adding the repr of the object that was passed is nice if it's an integer, but less so if you passed in some huge object. If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
Someone unaware of the difference between text and bytes probably isn't messing with code that has b"..." strings in it. Ultimately, there's not a lot you can do about that; people just have to learn certain things, and quite probably, searching the web for this error message will find good information (it did for me). -0.5 on this change. ChrisA

On Tue, Oct 25, 2016 at 6:58 AM, Chris Angelico <rosuav@gmail.com> wrote:
Agreed that showing the repr in general is tricky. The length isn't such a big deal (you need some cleverness -- don't show the repr if it's >40 characters, say -- but that's doable if someone wants to do the work). What I'd be a little nervous of is the time/memory cost of computing the repr for an arbitrary object every time a TypeError is thrown. One possible workaround for this would be to be lazy, i.e. wait until someone actually calls str() on the exception object before computing the repr, but the problem then is that you'd have to attach the offending object to the TypeError, which pins it in memory longer than it otherwise would be. Perhaps this is an acceptable trade-off -- having the offending object around is actually pretty useful for debugging! But we probably can't just blindly start showing repr's everywhere -- it'll need some case-by-case consideration. A good example actually might be the int constructor -- if the object is an unrecognized type, then it shows the type; if it's a recognized type but the value is bad, then it shows the value: In [3]: int([]) TypeError: int() argument must be a string, a bytes-like object or a number, not 'list' In [4]: int("a") ValueError: invalid literal for int() with base 10: 'a'
If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
Yes, breaking the line will also work, but often difficult to do (e.g. you might have a traceback but not an easy recipe to reproduce the exception), and anyway, why is that a reason to punish people who haven't learned that trick? I get that this list's default is to push back on proposed changes, and it's a good principle in general, but "improved error messages" are *really* cheap. The bar should be pretty low, IMO. If someone's willing to do the work to make the error messages friendlier, while addressing technical considerations like the repr issue, then that's awesome, and if that means that users get somewhat redundant information to help them debug then... cool? Terseness per se is not a cardinal virtue :-). -n -- Nathaniel J. Smith -- https://vorpus.org

On 25 October 2016 at 20:17, Nathaniel Smith <njs@pobox.com> wrote:
Agreed, improved error messages are always worthwhile. There may be issues (you mentioned one earlier in your message), but typically they are about implementation, not about the principle, so I'd say go ahead and implement the proposal, post it at bugs.python.org, and things like that can be thrashed out in code review. For people who don't have the knowledge to actually code the change, a feature request on the tracker is probably a fine start. Paul

On Tue, Oct 25, 2016 at 12:17:56PM -0700, Nathaniel Smith wrote:
I think its even lower than most may realise. Error messages are not part of the official API of the function or class, so IMO we can change them any time we want, even between point releases. We shouldn't do so just for the sake of change[1], because chances are that you'll break somebody's doctests. But doctests that depend on the exact wording of a error message are already broken. For a sufficiently good improvement in error reporting, I think we should be free to make that change without having to wait for a new minor release.
Indeed.
Terseness per se is not a cardinal virtue :-).
"Note the consistent user interface and error reportage. Ed is generous enough to flag errors, yet prudent enough not to overwhelm the novice with verbosity." https://www.gnu.org/fun/jokes/ed-msg.html -- Steve

On Tue, Oct 25, 2016 at 6:58 AM, Chris Angelico <rosuav@gmail.com> wrote:
yes, that's OK -- and that is the spec of the tuple constructor, yes?
I'm not sure you need the repr of the object passed in -- the type is usually sufficient. (for a TypeError -- for a ValueError, then the value IS important, and a repr is nice.
If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
yes, but it would be nice not to have to -- maybe I'm just overdoing the one-liners, but I VERY often have errors liek this on a line, and have to go in and break the line by hand to find out where the error is actually coming from. SyntaxErrors make some effort to indicate WHERE in the line the Error is - it would be great to get some help like that in these cases. Not sure how possible it is though. As I think about it, I tend to get this with indexing error, where a have a fairly complex expression with multiple objects being indexed, and then a get an IndexError and have no idea where the problem is.
or shouldn't be :-) -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On 25 October 2016 at 23:20, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
Should we make a PEP with all of those ?
No, incrementally improving error messages doesn't require PEP level advocacy - it just requires folks doing the development and review work of updating them without breaking anything, and adjusting the test suite as needed. In a lot of cases what's feasible with an error message (particularly from C code) depends a great deal on what information is readily available at the point the error is being reported, in others it's just that the particular error message hasn't been updated yet to be a bit more user friendly, so it's hard to establish new general principles around error reporting. The question does make wonder if we should consider "Find and improve an error message that annoys you because it omits frequently relevant information" as our new default "I'm interested in contributing, but I don't know what to work on" recommendation? While we don't want folks changing error messages for the sake of changing them, or overwhelming users with frequently irrelevant details, there's still a wide array of error messages that could stand to provide a bit more context regarding what went wrong, and it's the kind of change that can help more folks start to see software errors as "I can solve this!" puzzles rather than "It doesn't work and I don't know where to start in figuring out why not" road blocks. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Oct 25, 2016 at 6:20 AM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
I don't disagree with the principle, but I don't see how this particular example works. The interpreter here doesn't know that you're trying concatenate a bytes and a string, because the error happens before that, when it tries to make the bytes object. These really are two different errors. -n -- Nathaniel J. Smith -- https://vorpus.org

So, based on everyone's feedback, I just created this: http://bugs.python.org/issue28536 On Mon, Oct 24, 2016 at 5:07 PM, Ryan Gonzalez <rymg19@gmail.com> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

Also, as an extension of this idea, would it be possible to improve errors like this: class X: pass X() # object() takes no parameters to show the actual type instead of just 'object'? On Tue, Oct 25, 2016 at 4:48 PM, Ryan Gonzalez <rymg19@gmail.com> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

On Tue, Oct 25, 2016 at 04:55:21PM -0500, Ryan Gonzalez wrote:
My wild guess is that the cause is that __new__ looks like this: class object(): def __new__(cls, *args): if args: raise TypeError('object() takes no parameters') Except in C, of course. This is probably a left-over from the days when object() took and ignored any parameters. If my guess is close, then maybe we can do this: if args: raise TypeError('%s() takes no parameters' % cls.__name__) or equivalent. -- Steve

Yeah, I just checked the source and tried changing it. Seems to work well. On Tue, Oct 25, 2016 at 8:11 PM, Steven D'Aprano <steve@pearwood.info> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

On Sat, Oct 29, 2016 at 8:36 AM, Sven R. Kunze <srkunze@mail.de> wrote:
+1. Showing the list's contents might be problematic, but it could show the valid indices - if the lists are different lengths, it would tell you which is which. IndexError: list index 5 is out of range -4..3. Added bonus: Someone who doesn't know that negative indices are valid will get a cool hint. ChrisA

Also, for something like this: In [1]: class A: ...: pass ...: In [2]: A(x=2) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-857108bff989> in <module>() ----> 1 A(x=2) TypeError: object() takes no parameters It would be nice to say TypeError: object() takes no parameters, but keyword argument "x" given. I understand that the value of "x" might not have a __repr__ method, but the key name has to be string, so this should be easily doable at least for extra keyword arguments? For positional arguments, maybe just print how many were passed to object? Knowing the key name would have helped me with debugging. Usually, I print(kwargs) somewhere up the inheritance chain and run my program again. On Tuesday, October 25, 2016 at 3:19:57 AM UTC-4, Neil Girdhar wrote:

Thos are great idea. I love the current trend of trying to give better error messages. Another pet peave of mine: TypeError not providing at least 'raiser' 'accepted type', 'given value' and 'given value type'. E.G, int() does an ok job: >>> int(foo) ValueError: invalid literal for int() with base 10: '1O' raiser: int() accept type: a base 10 literal value given: 1O But it could be improved by givin the type of the give value. Indeed in that case, I got a string composed of one and the letter O, but looks like the number 10. Some students can struggle with those. list, set and tuple less not as good: >>> tuple(foo) TypeError: 'int' object is not iterable No raiser, no value given. It's hard to find out what's the problem is. The biggest issue here is that if you have a long line with tuple() in the middle, yuou need to know the problem comes from tuple. Another problem is that many people don't know what iterable means. A better error message would be: TypeError: tuple() only accept iterables (any object you can use a for loop on). But it received '1', which is of type <int>. Some things deserve a big explanation to solve the problem. It would be nice to add a link to official tutorial in the documentation. E.G, encoding is a big one: In [8]: b'é' + 'é' File "<ipython-input-8-cfac1add5f26>", line 1 b'é' + 'é' ^ SyntaxError: bytes can only contain ASCII literal characters. This is not helpful to somebody unaware of the difference between text and bytes. Possible solution: In [8]: b'é' + 'é' File "<ipython-input-8-cfac1add5f26>", line 1 b'é' + 'é' ^ SyntaxError: You cannnot concatenate bytes (b'é...') with a string ('é...'). Learn more about fixing this error at https://doc.python.org/errors/7897978 Of course, the repr will often need to be shorten but a short repr is better than none. Should we make a PEP with all of those ? Le 25/10/2016 à 09:23, Neil Girdhar a écrit :

On Wed, Oct 26, 2016 at 12:20 AM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
-1 on this one. It doesn't really add very much - "iterable" is a good keyword that anyone can put into a search engine. Adding the repr of the object that was passed is nice if it's an integer, but less so if you passed in some huge object. If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
Someone unaware of the difference between text and bytes probably isn't messing with code that has b"..." strings in it. Ultimately, there's not a lot you can do about that; people just have to learn certain things, and quite probably, searching the web for this error message will find good information (it did for me). -0.5 on this change. ChrisA

On Tue, Oct 25, 2016 at 6:58 AM, Chris Angelico <rosuav@gmail.com> wrote:
Agreed that showing the repr in general is tricky. The length isn't such a big deal (you need some cleverness -- don't show the repr if it's >40 characters, say -- but that's doable if someone wants to do the work). What I'd be a little nervous of is the time/memory cost of computing the repr for an arbitrary object every time a TypeError is thrown. One possible workaround for this would be to be lazy, i.e. wait until someone actually calls str() on the exception object before computing the repr, but the problem then is that you'd have to attach the offending object to the TypeError, which pins it in memory longer than it otherwise would be. Perhaps this is an acceptable trade-off -- having the offending object around is actually pretty useful for debugging! But we probably can't just blindly start showing repr's everywhere -- it'll need some case-by-case consideration. A good example actually might be the int constructor -- if the object is an unrecognized type, then it shows the type; if it's a recognized type but the value is bad, then it shows the value: In [3]: int([]) TypeError: int() argument must be a string, a bytes-like object or a number, not 'list' In [4]: int("a") ValueError: invalid literal for int() with base 10: 'a'
If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
Yes, breaking the line will also work, but often difficult to do (e.g. you might have a traceback but not an easy recipe to reproduce the exception), and anyway, why is that a reason to punish people who haven't learned that trick? I get that this list's default is to push back on proposed changes, and it's a good principle in general, but "improved error messages" are *really* cheap. The bar should be pretty low, IMO. If someone's willing to do the work to make the error messages friendlier, while addressing technical considerations like the repr issue, then that's awesome, and if that means that users get somewhat redundant information to help them debug then... cool? Terseness per se is not a cardinal virtue :-). -n -- Nathaniel J. Smith -- https://vorpus.org

On 25 October 2016 at 20:17, Nathaniel Smith <njs@pobox.com> wrote:
Agreed, improved error messages are always worthwhile. There may be issues (you mentioned one earlier in your message), but typically they are about implementation, not about the principle, so I'd say go ahead and implement the proposal, post it at bugs.python.org, and things like that can be thrashed out in code review. For people who don't have the knowledge to actually code the change, a feature request on the tracker is probably a fine start. Paul

On Tue, Oct 25, 2016 at 12:17:56PM -0700, Nathaniel Smith wrote:
I think its even lower than most may realise. Error messages are not part of the official API of the function or class, so IMO we can change them any time we want, even between point releases. We shouldn't do so just for the sake of change[1], because chances are that you'll break somebody's doctests. But doctests that depend on the exact wording of a error message are already broken. For a sufficiently good improvement in error reporting, I think we should be free to make that change without having to wait for a new minor release.
Indeed.
Terseness per se is not a cardinal virtue :-).
"Note the consistent user interface and error reportage. Ed is generous enough to flag errors, yet prudent enough not to overwhelm the novice with verbosity." https://www.gnu.org/fun/jokes/ed-msg.html -- Steve

On Tue, Oct 25, 2016 at 6:58 AM, Chris Angelico <rosuav@gmail.com> wrote:
yes, that's OK -- and that is the spec of the tuple constructor, yes?
I'm not sure you need the repr of the object passed in -- the type is usually sufficient. (for a TypeError -- for a ValueError, then the value IS important, and a repr is nice.
If your lines of code are so complicated that you can't pinpoint the cause of the TypeError, the solution is probably to break the line.
yes, but it would be nice not to have to -- maybe I'm just overdoing the one-liners, but I VERY often have errors liek this on a line, and have to go in and break the line by hand to find out where the error is actually coming from. SyntaxErrors make some effort to indicate WHERE in the line the Error is - it would be great to get some help like that in these cases. Not sure how possible it is though. As I think about it, I tend to get this with indexing error, where a have a fairly complex expression with multiple objects being indexed, and then a get an IndexError and have no idea where the problem is.
or shouldn't be :-) -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On 25 October 2016 at 23:20, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
Should we make a PEP with all of those ?
No, incrementally improving error messages doesn't require PEP level advocacy - it just requires folks doing the development and review work of updating them without breaking anything, and adjusting the test suite as needed. In a lot of cases what's feasible with an error message (particularly from C code) depends a great deal on what information is readily available at the point the error is being reported, in others it's just that the particular error message hasn't been updated yet to be a bit more user friendly, so it's hard to establish new general principles around error reporting. The question does make wonder if we should consider "Find and improve an error message that annoys you because it omits frequently relevant information" as our new default "I'm interested in contributing, but I don't know what to work on" recommendation? While we don't want folks changing error messages for the sake of changing them, or overwhelming users with frequently irrelevant details, there's still a wide array of error messages that could stand to provide a bit more context regarding what went wrong, and it's the kind of change that can help more folks start to see software errors as "I can solve this!" puzzles rather than "It doesn't work and I don't know where to start in figuring out why not" road blocks. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Oct 25, 2016 at 6:20 AM, Michel Desmoulin <desmoulinmichel@gmail.com> wrote:
I don't disagree with the principle, but I don't see how this particular example works. The interpreter here doesn't know that you're trying concatenate a bytes and a string, because the error happens before that, when it tries to make the bytes object. These really are two different errors. -n -- Nathaniel J. Smith -- https://vorpus.org

So, based on everyone's feedback, I just created this: http://bugs.python.org/issue28536 On Mon, Oct 24, 2016 at 5:07 PM, Ryan Gonzalez <rymg19@gmail.com> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

Also, as an extension of this idea, would it be possible to improve errors like this: class X: pass X() # object() takes no parameters to show the actual type instead of just 'object'? On Tue, Oct 25, 2016 at 4:48 PM, Ryan Gonzalez <rymg19@gmail.com> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

On Tue, Oct 25, 2016 at 04:55:21PM -0500, Ryan Gonzalez wrote:
My wild guess is that the cause is that __new__ looks like this: class object(): def __new__(cls, *args): if args: raise TypeError('object() takes no parameters') Except in C, of course. This is probably a left-over from the days when object() took and ignored any parameters. If my guess is close, then maybe we can do this: if args: raise TypeError('%s() takes no parameters' % cls.__name__) or equivalent. -- Steve

Yeah, I just checked the source and tried changing it. Seems to work well. On Tue, Oct 25, 2016 at 8:11 PM, Steven D'Aprano <steve@pearwood.info> wrote:
-- Ryan (ライアン) [ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong. http://kirbyfan64.github.io/

On Sat, Oct 29, 2016 at 8:36 AM, Sven R. Kunze <srkunze@mail.de> wrote:
+1. Showing the list's contents might be problematic, but it could show the valid indices - if the lists are different lengths, it would tell you which is which. IndexError: list index 5 is out of range -4..3. Added bonus: Someone who doesn't know that negative indices are valid will get a cool hint. ChrisA
participants (10)
-
Chris Angelico
-
Chris Barker
-
Michel Desmoulin
-
Nathaniel Smith
-
Neil Girdhar
-
Nick Coghlan
-
Paul Moore
-
Ryan Gonzalez
-
Steven D'Aprano
-
Sven R. Kunze