Discussion:
how to discover what values produced an exception?
(too old to reply)
Johanne Fairchild
2024-05-03 13:56:39 UTC
Permalink
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
For instance:

--8<-------------------------------------------------------->8---
(0,0) < 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---

It could have said something like:

--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---

We would know which were the values that caused the problem, which would
be very helpful.
Stefan Ram
2024-05-03 14:26:58 UTC
Permalink
Post by Johanne Fairchild
How to discover what values produced an exception?
An exception isn't always tied to a value. I could, like, write:

if 3 > 2: "".call()

And I'd get an AttributeError exception. Can I now say the
values 3 and 2 caused this exception? Or was it the empty
string? Or the lack of a call method?

Let me try your example here: In the console:

|>>> ( 0, 0 )< 4
|TypeError: '<' not supported between instances of 'tuple' and 'int'

In this case, the values aren't important, one could even
say they're a distraction. What matters are the types of the
values, and those were given. However, the values are visible
in the console as they were typed in. In a script:

|Traceback (most recent call last):
| File "Main.py", line 1, in <module>
| ( 0, 0 )< 4
|TypeError: '<' not supported between instances of 'tuple' and 'int'

. Now I can see the offending line, which in this case even spills
the beans on the values.

Sometimes you really need those values. If they're values of global
names, you can kind of get them in the IDLE shell up to a point.

So I write this script in the IDLE editor and then run it:

a = 4
( 0, 0 )< a

. Now I get this in the IDLE console:

|Traceback (most recent call last):
| File "Main.py", line 2, in <module>
| ( 0, 0 )< a
|TypeError: '<' not supported between instances of 'tuple' and 'int'

. Then I can type

a

into the console, hit Return, and I see the value 4.

This script here has local variables, and that trick ain't
gonna fly no more:

def main():
a = 4
( 0, 0 )< a

main()

Now you got to add a debug print statement and run the script again.

def main():
a = 4
print( a )
( 0, 0 )< a

main()

In a bigger script there's all kinds of values and variables,
and sometimes the Python implementation can't know which ones
are critical for an exception, so the programmer's got to step
in and write those debug print statements.

(You could also use the IDLE debugger to see local variables,
but I prefer debug print statements.)
Stefan Ram
2024-05-04 14:15:02 UTC
Permalink
Post by Stefan Ram
Sometimes you really need those values. If they're values of global
names, you can kind of get them in the IDLE shell up to a point.
The way I see it, the issue here isn't just on Python,
the language - it's also on us, the coders! I mean, when
I define my own function, I can roll with something like:

def function( value ):
if value > 0:
return log( value )
else:
raise ZeroDivisionError( 'domain error' )

print( function( -1 ))

. Or I can go with something like this:

def function( value ):
if value > 0:
return log( value )
else:
message = 'domain error: '
message += '\nThe argument value was: '+ repr( value ) + '.'
message += '\nbut a value > 0 was expected.'
raise ZeroDivisionError( message )

print( function( -1 ))

. And you know what? The second option's gonna give you this output:

ZeroDivisionError: math domain error:
The argument value was: -1.
but a value > 0 was expected.

.
Stefan Ram
2024-05-04 14:47:52 UTC
Permalink
Post by Stefan Ram
raise ZeroDivisionError( 'domain error' )
...
Post by Stefan Ram
raise ZeroDivisionError( message )
I meant "ValueError", not "ZeroDivisionErrror"!
Stefan Ram
2024-05-04 19:08:26 UTC
Permalink
Post by Stefan Ram
return log( value )
raise ZeroDivisionError( 'domain error' )
Otherwise, I reckon the stopgap solution for dealing with those
"features" would be something like this . . .

try:
function( value )
except ValueError:
print( 'value =', value )
raise
Thomas Passin
2024-05-03 21:39:46 UTC
Permalink
Post by Johanne Fairchild
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
--8<-------------------------------------------------------->8---
(0,0) < 4
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---
--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---
We would know which were the values that caused the problem, which would
be very helpful.
In this example it would not help at all to know the actual values.
Knowing that you are trying to compare incomparable types is enough.
Alan Bawden
2024-05-03 23:29:43 UTC
Permalink
Post by Johanne Fairchild
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
--8<-------------------------------------------------------->8---
(0,0) < 4
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---
--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---
We would know which were the values that caused the problem, which would
be very helpful.
In this example it would not help at all to know the actual values. Knowing
that you are trying to compare incomparable types is enough.

In general, it absolutely can help. The programmer can sometimes
recognize where a value of unexpected type came from just by looking at
it, allowing her to quickly deduce just what went wrong without further
investigation.

A good error message shouldn't withhold any information that can
_easily_ be included. Debugging is more art than science, so there is
no real way to predict what information might prove useful in solving
the crime. I emphasized "easily" because of course you have to draw the
line somewhere.

The fact that Python error messages often fail to mention the actual
objects that caused the error has always annoyed me. I've always
presumed that for some reason it just wasn't easy to do. And it's never
been more than a minor annoyance to me.

So the OP is not wrong for wishing for this. Other programming
languages do it. Other Python programmers miss it.

- Alan
Chris Angelico
2024-05-06 17:46:09 UTC
Permalink
On Tue, 7 May 2024 at 03:38, Alan Bawden via Python-list
Post by Alan Bawden
A good error message shouldn't withhold any information that can
_easily_ be included. Debugging is more art than science, so there is
no real way to predict what information might prove useful in solving
the crime. I emphasized "easily" because of course you have to draw the
line somewhere.
Emphasis on "easily" is correct here. How easy is it to report the
value of something that could be arbitrarily large?

ChrisA

d***@online.de
2024-05-04 16:24:29 UTC
Permalink
Post by Johanne Fairchild
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
--8<-------------------------------------------------------->8---
(0,0) < 4
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---
--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---
We would know which were the values that caused the problem, which would
be very helpful.
Typically, the traceback informs you about the source code line
where the exception has been raised.
When the source line contains literals, you see the values.
If not, then only in special (speak: rather simple) cases the
knowledge of the values will help much.
Usually, a more thorough analysis is required to find out the bug
location and how to fix the bug.
2***@potatochowder.com
2024-05-04 19:33:00 UTC
Permalink
On 2024-05-03 at 10:56:39 -0300,
Post by Johanne Fairchild
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
--8<-------------------------------------------------------->8---
(0,0) < 4
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---
--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---
We would know which were the values that caused the problem, which would
be very helpful.
I'm not disagreeing that knowing the values could be useful in many
cases. In the general case, though, it's not practical. Consider a
function like this:

def f(x, y):
return g(x) < h(y)

The printed values of x, y, g(x), and h(y) could all be millions of (or
more) glyphs. Worse, one or more of those values could contain circular
lists or similar structures. And h or g could have changed x or y. In
summary, printing run-time values isn't always safe or useful. At least
printing the types is safe. In the face of ambiguity, refuse to guess.
Stefan Ram
2024-05-04 20:13:23 UTC
Permalink
Post by Johanne Fairchild
We would know which were the values that caused the problem, which would
be very helpful.
So, why don't critical values get automatically printed out when
exceptions happen?

In Python, printing out a value usually means calling a method
like "__str__" or "__repr__". But the thing is, the exception
could have been raised inside that very method. If you then try
to automatically print out a critical value, that could trigger
more exceptions or other issues. Basically, you could end up
activating all sorts of random code, and the consequences of
that in that kind of situation are hard to predict
Left Right
2024-05-03 18:19:23 UTC
Permalink
From a practical perspective: not all values are printable (especially
if printing a value results in an error: then you'd lose the original
error, so, going crazy with printing of errors is usually not such a
hot idea).

But, if you want the values: you'd have to examine the stack, extract
the values from the local variables etc. It's easier to do this
interactively (unless you are in a multithreaded environment, where
pdb is broken). But, you could also try to find this information
automatically (by unwinding the stack to the place that generated the
error, examining the local variables etc.) It's tedious and prone to
errors. So, if you really want to do this automatically for every
error that's going to be quite a bit of work.

On Fri, May 3, 2024 at 6:58 PM Johanne Fairchild via Python-list
How to discover what values produced an exception? Or perhaps---why
doesn't the Python traceback show the values involved in the TypeError?
--8<-------------------------------------------------------->8---
(0,0) < 4
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'tuple' and 'int'
--8<-------------------------------------------------------->8---
--8<-------------------------------------------------------->8---
TypeError: '<' not supported between instances of 'tuple' and 'int'
in (0,0) < 4.
--8<-------------------------------------------------------->8---
We would know which were the values that caused the problem, which would
be very helpful.
--
https://mail.python.org/mailman/listinfo/python-list
Loading...