A problem: handling warnings generated at low levels in your code
Python has a well honed approach for handling errors that happen at a low level in your code; you raise a specific exception and let it bubble up through your program. There's even a pattern for adding more context as you go up through the call stack, where you catch the exception, add more context to it (through one of various ways), and then propagate the exception onwards.
(You can also use things like phase tracking to make error messages more specific. And you may want to catch and re-raise exceptions for other reasons, such as wrapping foreign exceptions.)
All of this is great when it's an error. But what about warnings? I recently ran into a case where I wanted to 'raise' (in the abstract) a warning at a very low level in my code, and that left me completely stymied about what the best way to do it was. The disconnect between errors and warnings is that in most cases errors immediately stop further processing while warnings don't, so you can't deal with warnings by raising an exception; you need to somehow both 'raise' the warning and continue further processing.
I can think of several ways of handling this, all of which I've sort of used in code in the past:
- Explicitly return warnings as part of the function's output. This
is the most straightforward but also sprays warnings through your
APIs, which can be a problem if you realize that you've found a
need to add warnings to existing code.
- Have functions accumulate warnings on some global or relatively
global object (perhaps hidden through 'record a warning' function
calls). Then at the end of processing, high-level code will go
through the accumulated warnings and do whatever is desired with
- Log the warnings immediately through a general logging system that you're using for all program messages (ranging from simple to very complex). This has the benefit that both warnings and errors will be produced in the correct order.
The second and third approaches have the problem that it's hard for intermediate layers to add context to warning messages; they'll wind up wanting or needing to pass the context down to the low level routines that generate the warnings. The third approach can have the general options problem when it comes to controlling what warnings are and aren't produced, or you can try to control this by having the high level code configure the logging system to discard some messages.
I don't have any answers here, but I can't help thinking that I'm missing a way of doing this that would make it all easy. Probably logging is the best general approach for this and I should just give in, learn a Python logging system, and use it for everything in the future.
(In the incident that sparked this entry, I wound up punting and just
printing out a message with
sys.stderr.write() because I wasn't in a
mood to significantly restructure the code just because I now wanted to
emit a warning.)