On Wed, Jul 21, 2010 at 12:34 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Hello,

I would like to propose the following PEP for feedback and review.
Permanent link to up-to-date version with proper HTML formatting:
http://www.python.org/dev/peps/pep-3151/

Thank you,

Antoine.

... ...

New exception classes
---------------------

The following tentative list of subclasses, along with a description and
the list of errnos mapped to them, is submitted to discussion:

* ``FileAlreadyExists``: trying to create a file or directory which already
 exists (EEXIST)

* ``FileNotFound``: for all circumstances where a file and directory is
 requested but doesn't exist (ENOENT)

* ``IsADirectory``: file-level operation (open(), os.remove()...) requested
 on a directory (EISDIR)

* ``NotADirectory``: directory-level operation requested on something else
 (ENOTDIR)

* ``PermissionDenied``: trying to run an operation without the adequate access
 rights - for example filesystem permissions (EACCESS, optionally EPERM)

* ``BlockingIOError``: an operation would block on an object (e.g. socket) set
 for non-blocking operation (EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS);
 this is the existing ``io.BlockingIOError`` with an extended role

* ``BadFileDescriptor``: operation on an invalid file descriptor (EBADF);
 the default error message could point out that most causes are that
 an existing file descriptor has been closed

* ``ConnectionAborted``: connection attempt aborted by peer (ECONNABORTED)

* ``ConnectionRefused``: connection reset by peer (ECONNREFUSED)

* ``ConnectionReset``: connection reset by peer (ECONNRESET)

* ``TimeoutError``: connection timed out (ECONNTIMEOUT); this could be re-cast
 as a generic timeout exception, useful for other types of timeout (for
 example in Lock.acquire())

This list assumes step 1 is accepted in full; the exception classes
described above would all derive from the now unified exception type
OSError.  It will need reworking if a partial version of step 1 is accepted
instead (again, see appendix A for the current distribution of errnos
and exception types).


Exception attributes
--------------------

In order to preserve *useful compatibility*, these subclasses should still
set adequate values for the various exception attributes defined on the
superclass (for example ``errno``, ``filename``, and optionally
``winerror``).

Implementation
--------------

Since it is proposed that the subclasses are raised based purely on the
value of ``errno``, little or no changes should be required in extension
modules (either standard or third-party).  As long as they use the
``PyErr_SetFromErrno()`` family of functions (or the
``PyErr_SetFromWindowsErr()`` family of functions under Windows), they
should automatically benefit from the new, finer-grained exception classes.

Library modules written in Python, though, will have to be adapted where
they currently use the following idiom (seen in ``Lib/tempfile.py``)::

   raise IOError(_errno.EEXIST, "No usable temporary file name found")

Fortunately, such Python code is quite rare since raising OSError or IOError
with an errno value normally happens when interfacing with system calls,
which is usually done in C extensions.

If there is popular demand, the subroutine choosing an exception type based
on the errno value could be exposed for use in pure Python.


Possible objections
===================

Namespace pollution
-------------------

Making the exception hierarchy finer-grained makes the root (or builtins)
namespace larger.  This is to be moderated, however, as:

* only a handful of additional classes are proposed;

* while standard exception types live in the root namespace, they are
 visually distinguished by the fact that they use the CamelCase convention,
 while almost all other builtins use lowercase naming (except True, False,
 None, Ellipsis and NotImplemented)

An alternative would be to provide a separate module containing the
finer-grained exceptions, but that would defeat the purpose of
encouraging careful code over careless code, since the user would first
have to import the new module instead of using names already accessible.

+1 in on this whole PEP!

The EnvrionmentError hierarchy and common errno test code has bothered me for a while.  While I think the namespace pollution concern is valid I would suggest adding "Error" to the end of all of the names (your initial proposal only says "Error" on the end of one of them) as that is consistent with the bulk of the existing standard exceptions and warnings.  They are unlikely to conflict with anything other than exceptions people have already defined themselves in any existing code (which could likely be refactored out after we officially define these).




Earlier discussion
==================

While this is the first time such as formal proposal is made, the idea
has received informal support in the past [1]_; both the introduction
of finer-grained exception classes and the coalescing of OSError and
IOError.

The removal of WindowsError alone has been discussed and rejected
as part of another PEP [2]_, but there seemed to be a consensus that the
distinction with OSError wasn't meaningful.  This supports at least its
aliasing with OSError.


Moratorium
==========

The moratorium in effect on language builtins means this PEP has little
chance to be accepted for Python 3.2.


Possible alternative
====================

Pattern matching
----------------

Another possibility would be to introduce an advanced pattern matching
syntax when catching exceptions.  For example::

   try:
       os.remove(filename)
   except OSError as e if e.errno == errno.ENOENT:
       pass

Several problems with this proposal:

* it introduces new syntax, which is perceived by the author to be a heavier
 change compared to reworking the exception hierarchy
* it doesn't decrease typing effort significantly
* it doesn't relieve the programmer from the burden of having to remember
 errno mnemonics

ugh.  no.  :)  That only works well for single exceptions and encourages less explicit exception types.  Exceptions are a class hierarchy, we should encourage its use rather than encouraging magic type specific attributes with conditionals.

-gps