During the PyCon sprint I tried to make BaseException accept only a single argument and bind it to BaseException.message . I was successful (see the p3yk_no_args_on_exc branch), but it was very painful to pull off as anyone who sat around me the last three days of the sprint will tell you as they had to listen to me curse incessantly. Because of the pain that I went through in the transition and thus the lessons learned, Guido and I discussed it and we think it would be best to give up on forcing BaseException to accept only a single argument. I think it is still doable, but requires a multi-release transition period and not the one that 2.6 -> 3.0 is offering. And so Guido and I plan on deprecating BaseException.message as its entire point in existence was to help transition to what we are not going to have happen. =) Now that means BaseException.message might hold the record for shortest lived feature as it was only introduced in 2.5 and is now to be deprecated in 2.6 and removed in 2.7/3.0. =) Below is PEP 352, revised to reflect the removal of BaseException.messageand for letting the 'args' attribute stay (along with suggesting one should only pass a single argument to BaseException). Basically the interface for exceptions doesn't really change in 3.0 except for the removal of __getitem__. -------------------------------------------------------------------------- PEP: 352 Title: Required Superclass for Exceptions Version: $Revision: 53592 $ Last-Modified: $Date: 2007-01-28 21:54:11 -0800 (Sun, 28 Jan 2007) $ Author: Brett Cannon <brett@python.org> Guido van Rossum <guido@python.org> Status: Final Type: Standards Track Content-Type: text/x-rst Created: 27-Oct-2005 Post-History: Abstract ======== In Python 2.4 and before, any (classic) class can be raised as an exception. The plan for 2.5 was to allow new-style classes, but this makes the problem worse -- it would mean *any* class (or instance) can be raised! This is a problem as it prevents any guarantees from being made about the interface of exceptions. This PEP proposes introducing a new superclass that all raised objects must inherit from. Imposing the restriction will allow a standard interface for exceptions to exist that can be relied upon. It also leads to a known hierarchy for all exceptions to adhere to. One might counter that requiring a specific base class for a particular interface is unPythonic. However, in the specific case of exceptions there's a good reason (which has generally been agreed to on python-dev): requiring hierarchy helps code that wants to *catch* exceptions by making it possible to catch *all* exceptions explicitly by writing ``except BaseException:`` instead of ``except *:``. [#hierarchy-good]_ Introducing a new superclass for exceptions also gives us the chance to rearrange the exception hierarchy slightly for the better. As it currently stands, all exceptions in the built-in namespace inherit from Exception. This is a problem since this includes two exceptions (KeyboardInterrupt and SystemExit) that often need to be excepted from the application's exception handling: the default behavior of shutting the interpreter down without a traceback is usually more desirable than whatever the application might do (with the possible exception of applications that emulate Python's interactive command loop with ``>>>`` prompt). Changing it so that these two exceptions inherit from the common superclass instead of Exception will make it easy for people to write ``except`` clauses that are not overreaching and not catch exceptions that should propagate up. This PEP is based on previous work done for PEP 348 [#pep348]_. Requiring a Common Superclass ============================= This PEP proposes introducing a new exception named BaseException that is a new-style class and has a single attribute, ``args``. Below is the code as the exception will work in Python 3.0 (how it will work in Python 2.x is covered in the `Transition Plan`_ section):: class BaseException(object): """Superclass representing the base of the exception hierarchy. Provides a 'message' attribute that contains either the single argument to the constructor or the empty string. This attribute is used in the string representation for the exception. This is so that it provides the extra details in the traceback. """ def __init__(self, *args): """Set the 'message' attribute'""" self.args = args def __str__(self): """Return the str of 'message'""" if len(self.args) == 1: return str(self.args[0]) else: return str(self.args) def __repr__(self): return "%s(*%s)" % (self.__class__.__name__, repr(self.args)) No restriction is placed upon what may be passed in for ``args`` for backwards-compatibility reasons. In practice, though, only a single string argument should be used. This keeps the string representation of the exception to be a useful message about the exception that is human-readable; this is why the ``__str__`` method special-cases on length-1 ``args`` value. Including programmatic information (e.g., an error code number) should be stored as a separate attribute in a subclass. The ``raise`` statement will be changed to require that any object passed to it must inherit from BaseException. This will make sure that all exceptions fall within a single hierarchy that is anchored at BaseException [#hierarchy-good]_. This also guarantees a basic interface that is inherited from BaseException. The change to ``raise`` will be enforced starting in Python 3.0 (see the `Transition Plan`_ below). With BaseException being the root of the exception hierarchy, Exception will now inherit from it. Exception Hierarchy Changes =========================== With the exception hierarchy now even more important since it has a basic root, a change to the existing hierarchy is called for. As it stands now, if one wants to catch all exceptions that signal an error *and* do not mean the interpreter should be allowed to exit, you must specify all but two exceptions specifically in an ``except`` clause or catch the two exceptions separately and then re-raise them and have all other exceptions fall through to a bare ``except`` clause:: except (KeyboardInterrupt, SystemExit): raise except: ... That is needlessly explicit. This PEP proposes moving KeyboardInterrupt and SystemExit to inherit directly from BaseException. :: - BaseException |- KeyboardInterrupt |- SystemExit |- Exception |- (all other current built-in exceptions) Doing this makes catching Exception more reasonable. It would catch only exceptions that signify errors. Exceptions that signal that the interpreter should exit will not be caught and thus be allowed to propagate up and allow the interpreter to terminate. KeyboardInterrupt has been moved since users typically expect an application to exit when the press the interrupt key (usually Ctrl-C). If people have overly broad ``except`` clauses the expected behaviour does not occur. SystemExit has been moved for similar reasons. Since the exception is raised when ``sys.exit()`` is called the interpreter should normally be allowed to terminate. Unfortunately overly broad ``except`` clauses can prevent the explicitly requested exit from occurring. To make sure that people catch Exception most of the time, various parts of the documentation and tutorials will need to be updated to strongly suggest that Exception be what programmers want to use. Bare ``except`` clauses or catching BaseException directly should be discouraged based on the fact that KeyboardInterrupt and SystemExit almost always should be allowed to propagate up. Transition Plan =============== Since semantic changes to Python are being proposed, a transition plan is needed. The goal is to end up with the new semantics being used in Python 3.0 while providing a smooth transition for 2.x code. All deprecations mentioned in the plan will lead to the removal of the semantics starting in the version following the initial deprecation. Here is BaseException as implemented in the 2.x series:: class BaseException(object): """Superclass representing the base of the exception hierarchy. The __getitem__ method is provided for backwards-compatibility and will be deprecated at some point. """ def __init__(self, *args): """Set the 'args' attribute.""" self.args = args def __str__(self): """Return the str of args[0] or args, depending on length.""" return str(self.args[0] if len(self.args) <= 1 else self.args) def __repr__(self): func_args = repr(self.args) if self.args else "()" return self.__class__.__name__ + func_args def __getitem__(self, index): """Index into arguments passed in during instantiation. Provided for backwards-compatibility and will be deprecated. """ return self.args[index] Deprecation of features in Python 2.9 is optional. This is because it is not known at this time if Python 2.9 (which is slated to be the last version in the 2.x series) will actively deprecate features that will not be in 3.0 . It is conceivable that no deprecation warnings will be used in 2.9 since there could be such a difference between 2.9 and 3.0 that it would make 2.9 too "noisy" in terms of warnings. Thus the proposed deprecation warnings for Python 2.9 will be revisited when development of that version begins to determine if they are still desired. * Python 2.5 [done] - all standard exceptions become new-style classes - introduce BaseException - Exception, KeyboardInterrupt, and SystemExit inherit from BaseException - deprecate raising string exceptions * Python 2.6 - deprecate catching string exceptions - deprecate ``message`` attribute (see `Retracted Ideas`_) * Python 2.7 - deprecate raising exceptions that do not inherit from BaseException - remove ``message`` attribute * Python 2.8 - deprecate catching exceptions that do not inherit from BaseException * Python 2.9 - deprecate ``__getitem__`` (optional) * Python 3.0 [done] - drop everything that was deprecated above: + string exceptions (both raising and catching) + all exceptions must inherit from BaseException + drop ``__getitem__`` Retracted Ideas =============== A previous version of this PEP that was implemented in Python 2.5 included a 'message' attribute on BaseException. Its purpose was to begin a transition to BaseException accepting only a single argument. This was to tighten the interface and to force people to use attributes in subclasses to carry arbitrary information with an exception instead of cramming it all into ``args``. Unfortunately, while implementing the removal of the ``args`` attribute in Python 3.0 at the PyCon 2007 sprint [#pycon2007-sprint-email]_, it was discovered that the transition was very painful, especially for C extension modules. It was decided that it would be better to deprecate the ``message`` attribute in Python 2.6 (and remove in Python 2.7 and Python 3.0) and consider a more long-term transition strategy in Python 3.0 to remove multiple-argument support in BaseException in preference of accepting only a single argument. Thus the introduction of ``message`` and the original deprecation of ``args`` has been retracted. References ========== .. [#pep348] PEP 348 (Exception Reorganization for Python 3.0) http://www.python.org/peps/pep-0348.html .. [#hierarchy-good] python-dev Summary for 2004-08-01 through 2004-08-15 http://www.python.org/dev/summary/2004-08-01_2004-08-15.html#an-exception-is... .. [#SF_1104669] SF patch #1104669 (new-style exceptions) http://www.python.org/sf/1104669 .. [#pycon2007-sprint-email] python-3000 email ("How far to go with cleaning up exceptions") http://mail.python.org/pipermail/python-3000/2007-March/005911.html Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 End:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Brett Cannon wrote:
During the PyCon sprint I tried to make BaseException accept only a single argument and bind it to BaseException.message . I was successful (see the p3yk_no_args_on_exc branch), but it was very painful to pull off as anyone who sat around me the last three days of the sprint will tell you as they had to listen to me curse incessantly.
Because of the pain that I went through in the transition and thus the lessons learned, Guido and I discussed it and we think it would be best to give up on forcing BaseException to accept only a single argument. I think it is still doable, but requires a multi-release transition period and not the one that 2.6 -> 3.0 is offering. And so Guido and I plan on deprecating BaseException.message as its entire point in existence was to help transition to what we are not going to have happen. =)
Now that means BaseException.message might hold the record for shortest lived feature as it was only introduced in 2.5 and is now to be deprecated in 2.6 and removed in 2.7/3.0. =)
Below is PEP 352, revised to reflect the removal of BaseException.messageand for letting the 'args' attribute stay (along with suggesting one should only pass a single argument to BaseException). Basically the interface for exceptions doesn't really change in 3.0 except for the removal of __getitem__.
Hmm, I'm working on cleaning up deprecations for Zope and related packages under Python 2.6. The irony here is that I'm receiving deprecation warnings for custom exception classes which had a 'message' attribute long before the abortive attempt to add them to the BaseException type, which hardly seems reasonable. For instance, docutils.parsers.rst defines a DirectiveError which takes two arguments, 'level' and 'message', and therefore gets hit with the deprecation (even though it never used the new signature). Likewise, ZODB.POSException defines a ConflictError type which takes 'message' as one of several arguments, all optional, and has since at least 2002. I don't think either of these classes should be subject to a deprecation warning for a feature they never used or depended on. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFJ2iqv+gerLs4ltQ4RArg7AJ9cjTweXUuGdUZNxZ3dHzYb9u6AcQCePJW/ PrXQ48wFrwrsrXSslZ0LSB4= =VU1d -----END PGP SIGNATURE-----
Tres Seaver wrote:
I don't think either of these classes should be subject to a deprecation warning for a feature they never used or depended on.
Agreed. Could you raise a tracker issue for the spurious warnings? (I believe we should be able to make the warning condition a bit smarter to eliminate these). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Nick Coghlan wrote:
Tres Seaver wrote:
I don't think either of these classes should be subject to a deprecation warning for a feature they never used or depended on.
Agreed. Could you raise a tracker issue for the spurious warnings? (I believe we should be able to make the warning condition a bit smarter to eliminate these).
Done: http://bugs.python.org/issue5716? Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFJ20IX+gerLs4ltQ4RAkuDAKCTZNp0r38d+hW8TmvjIh9Sj59CJQCfbJlQ taNbsBUT79MF8t7owySE2dg= =LjZf -----END PGP SIGNATURE-----
participants (3)
-
Brett Cannon
-
Nick Coghlan
-
Tres Seaver