If the evaluation of any expression inside the f-string fails, the whole print fails and all debug info is lost. In an error handling code there is plenty of reasons for that, because the data might be incomplete or in an inconsistent state due to the error being handled.
The idea is to allow for a “best effort evaluation” and replace failed expressions with a placeholder. I leave open how the placeholder should look like, that’s a secondary question now.
In the case this proposal wouId find supporters, I think the Python developers should specify how to achieve this. Just to start the discussion, here is an example of a new conversion specifier 'e' (in the case of an error / exception) that could be combined with existing 'a', 'r' and 's'.
Yes, and in my understanding, that’s a really really bad idea. Leads to far worse problems.
You mean trying to get an attribute off something that might not have it? You’ve been given getattr as a good way of doing that. Your “expr1”, “expr2”, “expr3” one isn’t realistic so I assume you must mean “foo.state”.
Remember, too, that you can always do something like “foo and foo.state” if you’re expecting the possibility of None.
Yes, and if you do, you’ll get an exception, which can itself be logged.
Absorb ANY exception and just print <N/A>? This seems like a really good way to lose useful information.
Once again, can you show a real-world example please? How often does this happen, how frequently do you really need to use print (rather than a dedicated logging subsystem), how many times do you need multiple of these expressions, and do you actually need to have multiple expressions that can succeed or fail independently? I can’t tell you what I would do in this situation because your examples are still entirely artificial. Yes, they’re vaguely plausible, but that’s not enough to make concrete recommendations.
You shouldn’t really be writing debug data so that everything is lost of there’s a problem with one of the values you want to display. Error handling is hard, and yes, it’s nice to make it as easy as possible, but at some point you have to be responsible for thinking about what could go wrong and being prepared for it.
In your example
you’ll get a NameError or a ZeroDivisionError with a traceback that points you at the problem. That doesn’t sound like you’ve “lost all your debug data” - rather the opposite, actually, you have exactly what you want. Or were you hoping to debug a different problem? Fix one problem at a time - fix this one, then reproduce the issue and fix the other problem. If you can’t reproduce the issue, you’re always going to have problems, no matter how much debug data you write (you’ll always, in my experience, want the precise thing you forgot to log…)
While a traceback is helpful, sometimes it does not point to the problem.
# some long non-trivial code controlling
# an external system while maintaining a complex state
# dealing with not 100% reliable sensors and not very well
# documented hardware
except Exception as err:
# we encountered an unexpected situation, let's log as many
# values as possible for further analysis
IMO it is not an error in the handler, it is a problem with the data. An unknown problem.
To succesfully print everyhing helpful in a human readable form either a LOTS OF try-except or if-else constructs would be required cluttering the error handling code or a simple feature I dared to propose today would do the same in one line.
No, no it is not. The log attempt would fail with an exception. Not just with the text “<N/A>” which tells you nothing about WHY it failed.
When you’re working with unknown/unexpected failures, get all the information. Throwing that away in favour of a generic “<N/A>” is about the worst thing I can think of for a debug message.
No actually, that’s not true; a worse thing is when attempting to log a non-scalar value segfaults the entire program. That is definitely worse, and extremely annoying to debug. But bland non-information isn’t far behind.
At least for numeric errors, it might be better to use numpy, which already has mechanisms for specifying ‘ignore’, ‘warn’, ‘raise’, ‘call’, ‘print’, ‘log’ for different kinds of error: numpy.seterr — NumPy v1.25 Manual
And if you’re tabulating data, consider pandas:
In : import pandas as pd
In : import numpy as np
In : df = pd.DataFrame(dict(vals=np.arange(-5, 5)))
In : df.apply(np.log10)
Yes, I know. I’m sorry if it is still not clear, but I really don’t care about why it failed, I simply don’t want that failure to affect the logging of other data. If you don’t have an use-case for it, that’s fine. But I do have. In my work there were situations when I would really appreciate the proposed feature. It does not change any existing code or workflows, you don’t have to use this option it when it does not suit your needs. As I have shown, It can be achieved by standard Python, but at the cost of many additional lines violating the DRY principle.