try..except with empty exceptions

Cameron Simpson cs at zip.com.au
Sun Apr 12 08:33:39 CEST 2015


On 12Apr2015 09:21, Chris Angelico <rosuav at gmail.com> wrote:
>On Sun, Apr 12, 2015 at 9:08 AM, Cameron Simpson <cs at zip.com.au> wrote:
>> Catching all exceptions isn't terribly common, _except_ in service routines
>> that wrap "unknown" operations. Classic example from my Asynchron class: [...]
>>
>>      try:
>>        r = func(*a, **kw)
>>      except:
>>        self.exc_info = sys.exc_info
>>      else:
>>        self.result = r
>>
>> All sorts of things like thread pools and other "worker" functions, and
>> run-forever daemons like mail filers that can have arbitrary exceptions
>> occur in (partly) third party code eg from config files; you need to catch
>> any unknown exception and fail the specific action, but continue the main
>> daemon operation.
>>
>> And since I use this code in Python 2, and since not all exceptions are
>> BaseException subclasses, I need the bare syntax.
>
>Fair enough. Do you know how often you actually catch stuff that
>wouldn't be caught by "except BaseException:"?

I don't know and I'm not sure I care (but discussion below). I would need to 
examine carefully what they were. Looking at the 2.7.9 doco for Exception it 
almost looks like that is what I should often want. (Though I think I have some 
circumstances where I might prefer BaseException.)

>I would guess it's
>pretty rare. In fact, you can find out.
>
>     try:
>       r = func(*a, **kw)
>     except BaseException:
>       self.exc_info = sys.exc_info
>     except:
>       self.exc_info = sys.exc_info
>       emit_warning()
>    else:
>       self.result = r
>
>You could simply mandate that, from version X.Y of your Asynchron
>module onward, old-style classes must not be thrown.

Except that Asynchron, like several of the other circumstances, is expected to 
be used with arbitrary callables from outside sources.

>> Also, IMO, a bare "except:" syntax is _far_ more pleasing to the eye than
>> "except magic_exception_name_that+gets_everything:". Also, I wish
>> "BaseException" were just spelled "Exception", if it has to be used.
>
>> I'm -0.1 on the idea myself. I consider "except:" succinct and evocative,
>> and prefer it to "except BaseException:".
>
>What you're looking at here is exactly why it's not spelled that way.
>The normal thing to do is NOT to catch absolutely everything, but to
>allow KeyboardInterrupt and SystemExit to continue to bubble up.
>That's spelled "except Exception".

Yes. But can I guarrentee that those are the only two? The 2.7.9 doco says:

   exception Exception
       All built-in, non-system-exiting exceptions are derived from this class.

I suppose I could consider that clear, but certainly in case of 
KeyboardInterrupt I might want to involve cleanup actions. I'll ignore 
interactive situations; messy.

>My whole point here is that the bare except catches too much for normal use, 
>and therefore should _not_ be pleasing to the eye. The thing you should most 
>often be doing is "except ValueError" or equivalent; forcing you to put at 
>least _some_ name into the exception clause emphasizes that being specific
>is not the unusual case.

Yes. But it seems to me that "normal use" generally means the situation where 
one catches a quite narrow set of exceptions for which particular recoveries 
are well defined.

However, when I want "all exceptions", that really is what I mean. I need to 
think quite carefully about KeyboardInterrupt and SystemExit. The former is 
guarrenteed to be delivered to the main thread IIRC and I'd want to catch that 
specificly in my main() function, if at all. And I suspect that SystemExit 
really shouldn't be stopped; if something untoward is calling it, should it not 
succeed? Or be exposed and then hunted down and killed!

So your argument for "except Exception:" is quite compelling for many 
situations where I do currently use "except:". I will review them and have a 
proper think.

However, my Asynchron class really is a little special. I use Asynchron to 
underpin a bunch of classes whose instances get fulfilled later, especially 
callables that get queued and run at a suitable time (or which get run in 
response to some external long awaited event). So, example sketch:

  L = Later()
  ...
  # LF is a "LateFunction", a subclass of Asynchron
  LF = L.defer(function, *a, **kw)
  ...
  try:
    result = LF()
  except Whatever as e:
    ...

My Later class serves a purpose much like the futures module and I suppose a 
LateFunction is somewhat like a future.

Anyway, at some point in the above example "function" gets called (by the 
infrastructure of "Later") with the supplied arguments as the Later processes 
its queue.

The user can collect the result by calling "LF()" whenever they like, as though 
they were calling "function()" directly.  If the function has already run, the 
value is returned or whatever exception _it_ raised is raised now. Otherwise it 
blocks until the function is run; same behaviour for the caller except for a 
delay.

So in this situation: should I catch and defer KeyboardInterrupt or SystemExit?  
Maybe not, but the clean/naive implementation says to catch absolutely 
everything.

Finally, if we were to expunge support for "except:", one would also need a 
cast iron guarrentee that no exception could possibly occur which was not a 
subclass of BaseException. I'd expect that to mean that "raise" of a 
non-instance of BaseException to itself raise a TypeError.

Thoughts?

Cheers,
Cameron Simpson <cs at zip.com.au>

What's fair got to do with it? It's going to happen.    - Lawrence of Arabia



More information about the Python-list mailing list