On 12Apr2015 09:21, Chris Angelico <ros...@gmail.com> wrote:
On Sun, Apr 12, 2015 at 9:08 AM, Cameron Simpson <c...@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 <c...@zip.com.au>

What's fair got to do with it? It's going to happen.    - Lawrence of Arabia
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to