Re: [Python-ideas] Consistent programming error handling idiom

Oops, good call. That was a bad example. Maybe this one is a bit better: try: assert no_bugs() except AssertionError: # bugs are okay pass
I'm very familiar with this pattern in Python and I've used it myself countless times. Unfortunately I've seen instances where it can lead to disastrous behavior, I think we all have. It seems to have limited usefulness in production and more usefulness in development. The point of my original email was precisely to put the legitimacy and usefulness of code like that into question.
So I agree this pattern works if you assume all code is exception-safe (context managers will clean up intermediate state) and there are no programming errors. There are lots of things that good code should do but as we all well know good code doesn't always do those things. My point is that except-log loops are dangerous and careless in the face of programming errors. Programming errors are unavoidable. In a large system they are a daily fact of life. When there is a bug in production it's very dangerous to re-enter a code block that has demonstrated itself to be buggy, else you risk corrupting data. For example: def random_code(state): assert is_valid(state.data) def user_code_a(state): state.data = "bad data" # the following throws random_code(state) def user_code_b(state): state.db.write(state.data) def main_loop(): state = State() loop = [user_code_a, user_code_b] for fn in loop: try: fn() except Exception: log_exception() This code allows user_code_b() to execute and corrupt data even though random_code() was lucky enough to be called and detect bad state early on. You may say the fix is to assert correct data before writing to the database and, yes, that would fix the problem for future executions in this instance. That's not the point, the point is that incorrect buggy code is running in production today and it's imperative to have multiple safeguards to limit its damage. For example, you wouldn't have an except-log loop in an airplane control system. Sometimes an error is just an error but sometimes an error signifies the running system itself is in a bad state. It would be nice to distinguish between the two in a consistent way across all Python event loops. Halting on any escaped exception is inconvenient, but continuing after any escape exception is dangerous. Rian

On Sat, Apr 9, 2016 at 2:40 AM, Rian Hunter <rian@thelig.ht> wrote:
Not sure I understand this example. It's obviously a toy, because you'd never put an 'assert' just to catch and discard the exception - the net result is that you call no_bugs() if you're in debug mode and don't if you're not, which is more cleanly spelled "if __debug__: no_bugs()".
When does this lead to disaster? Is it because someone creates a boundary that shouldn't exist, or because the code maintains global state in bad ways? The latter is a problem even without exceptions; imagine a programming error that doesn't cause an exception, but just omits some crucial "un-modify global state" call. You have no way of detecting that in the outer code, and your state is messed up.
I don't think the except-log loop is the problem here. The problem is the code that can go in part way, come out again, and leave itself in a mess.
Can you give a non-toy example that has this kind of mutable state at top level? I suspect it's bad design. If it's truly necessary, use a context manager to guarantee the reset: def user_code_a(state): with state.set_data("bad data"): random_code(state)
You may say the fix is to assert correct data before writing to the database and, yes, that would fix the problem for future executions in this instance. That's not the point, the point is that incorrect buggy code is running in production today and it's imperative to have multiple safeguards to limit its damage. For example, you wouldn't have an except-log loop in an airplane control system.
Actually, yes I would. The alternative that you're suggesting is to have any error immediately shut down the whole system. Is that really better? To have the entire control system disabled?
Sometimes an error is just an error but sometimes an error signifies the running system itself is in a bad state. It would be nice to distinguish between the two in a consistent way across all Python event loops. Halting on any escaped exception is inconvenient, but continuing after any escape exception is dangerous.
There's no way for Python to be able to fix this for you. The tools exist - most notably context managers - so the solution is to use them. I don't think we're in python-ideas territory here. What I see here is a perfect subject for a blog post or other scholarly article on "Python exception handling best practice", plus possibly an internal style guide for your company/organization. The blog post I would definitely read with interest; the style guide ought to be stating the obvious (as most style guides should). ChrisA

On Sat, Apr 9, 2016 at 2:40 AM, Rian Hunter <rian@thelig.ht> wrote:
Not sure I understand this example. It's obviously a toy, because you'd never put an 'assert' just to catch and discard the exception - the net result is that you call no_bugs() if you're in debug mode and don't if you're not, which is more cleanly spelled "if __debug__: no_bugs()".
When does this lead to disaster? Is it because someone creates a boundary that shouldn't exist, or because the code maintains global state in bad ways? The latter is a problem even without exceptions; imagine a programming error that doesn't cause an exception, but just omits some crucial "un-modify global state" call. You have no way of detecting that in the outer code, and your state is messed up.
I don't think the except-log loop is the problem here. The problem is the code that can go in part way, come out again, and leave itself in a mess.
Can you give a non-toy example that has this kind of mutable state at top level? I suspect it's bad design. If it's truly necessary, use a context manager to guarantee the reset: def user_code_a(state): with state.set_data("bad data"): random_code(state)
You may say the fix is to assert correct data before writing to the database and, yes, that would fix the problem for future executions in this instance. That's not the point, the point is that incorrect buggy code is running in production today and it's imperative to have multiple safeguards to limit its damage. For example, you wouldn't have an except-log loop in an airplane control system.
Actually, yes I would. The alternative that you're suggesting is to have any error immediately shut down the whole system. Is that really better? To have the entire control system disabled?
Sometimes an error is just an error but sometimes an error signifies the running system itself is in a bad state. It would be nice to distinguish between the two in a consistent way across all Python event loops. Halting on any escaped exception is inconvenient, but continuing after any escape exception is dangerous.
There's no way for Python to be able to fix this for you. The tools exist - most notably context managers - so the solution is to use them. I don't think we're in python-ideas territory here. What I see here is a perfect subject for a blog post or other scholarly article on "Python exception handling best practice", plus possibly an internal style guide for your company/organization. The blog post I would definitely read with interest; the style guide ought to be stating the obvious (as most style guides should). ChrisA
participants (2)
-
Chris Angelico
-
Rian Hunter