On Wed, Jul 21, 2010 at 12:34 PM, Antoine Pitrou firstname.lastname@example.org:
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/
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
- ``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
- ``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).
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``).
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.
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).
While this is the first time such as formal proposal is made, the idea has received informal support in the past _; 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 _, but there seemed to be a consensus that the distinction with OSError wasn't meaningful. This supports at least its aliasing with OSError.
The moratorium in effect on language builtins means this PEP has little chance to be accepted for Python 3.2.
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
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.