On 16 September 2012 23:30, Cameron Simpson <cs@zip.com.au> wrote:
On 16Sep2012 13:16, Yury Selivanov <yselivanov.ml@gmail.com> wrote:
| On 2012-09-15, at 10:20 PM, Cameron Simpson <cs@zip.com.au> wrote:
<snip> 
| This thread started with IOError and its errno attribute, and for those
| exact cases I find 'except .. if' approach quite useful.  But now, in
| 3.3 with PEP 3151 we have a much more granular exceptions tree, so instead
| of writing
|
|    except IOError as ex if ex.errno == errno.ENOENT:
|
| you will write:
|
|    except FileNotFoundError as ex:
|
| And that's actually how this class of problems should be addressed:
| instead of adding attributes to exceptions and exception guards to language -
| just design your exception classes better.
 
<snip> 

OSErrno and IOError are generally built on low level OS APIs, and
returning errno is a highly correct thing to do. It passes our, _with_ the
exception (so it doesn't get maked by another library call, as the global
POSIX error is subject to), the actual OS-level failure that was reported.

Likewise with the S3 exceptions and probably any other well designed
exception response to a library call with an informative failure code.

"Informative" failure code? FileNotFoundError(...) contains *exactly* the same information as OSError(errno.ENOENT, ....). A number is not an informative error code, and never will be.

Additionally, I don't quite follow your first paragraph ("It passes our, _with_ the exception (so it doesn't get maked by another library call, as the global POSIX error is subject to),", but from what I can tell it seems extremely irrelevant to the end-programmer. I don't care what the OS-level failure is in terms of a number, I care in terms of the actual problem and what happened. It's not like we're receiving native data from the OS itself.

You have a point that error codes can be informative. They *can* be.
When you have a ValueError it could come from almost anywhere. However, it is a rare case for you to need to distinguish between these, and when you do it is normally for a reason specific enough that two subclasses can easily account for it all? I wouldn't mind (except that I mind being wrong :P) you showing me where you do have a sort of structure where you need to differentiate between many of the same error class yet cannot split it up, but until you do I don't believe that there's an analog case where this could help.

| We have multiple inheritance after all, the perfect method of classifying
| objects/exceptions, why should we code information about the exception class
| to some attribute?
| If some library unifies all types of exceptions in one 'S3ResponseError'
| exception - that's the problem of the library design.

No, not necessarily. Having a ridiculous suite of a billion trite
subclasses to enumerate the return codes from a lower level (or more
"inner") library is just nuts.

The PEP class tree is handy to _group_ an assortment of failure
modes into small groups of the same flavour. But an exact one-to-one
between exception subclasses and errno values? Ghastly. It _doubles_
the cognitive burden on the dev and the code reader, because the
correspondence between the OS-level errno and the subclass names needs to
kept in mind if the program cares about the OS level failure mode. Which
it does if it is bothering to make a fine grained decision at all.

It is all very well to offer an, um, rich suite of subclasses representing
various library failure modes. But to toss the _actual_ library failure
indicator value in favour of a arbitrary and possibly incomplete class
name list? Bad, really bad. The exception _should_ carry with it the
underlying library failure code if the library has such a thing.

As said above, how is Library.MathError(5) more arbitrary than Library.UncalculatableMathError()?

If the number of errnos is large [n], then the cognitive burden is already large [n]. So if instead you have a large number [n] of error classes, how is the burden less [n == n]? It doesn't add any real effort on any side as you needed to allocate the numbers anyway, as you need to know the numbers.

Yes, if you have an incomplete name list you will suffer. But so what? Just cover all your bases. If you are wrapping a program from a lower-level language, wrap *everything you need*. It's no different to any other aspect of wrapping libraries.
 
| Big -1.

I'm not +1 any more, but still +.

I'm negative, but not -1. The problem is: there are bad libraries. I think the stuff I mentioned already is a better solution though, and it seems it's not even my idea :).