Add PEP 473 "Adding structured data to built-in exceptions" by Sebastian Kreft.
This commit is contained in:
parent
84d82f06f2
commit
8fd5ac9e9a
|
@ -0,0 +1,290 @@
|
|||
PEP: 473
|
||||
Title: Adding structured data to built-in exceptions
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Sebastian Kreft <skreft@deezer.com>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 29-Mar-2014
|
||||
Post-History:
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
Exceptions like ``AttributeError``, ``IndexError``, ``KeyError``,
|
||||
``LookupError``, ``NameError``, , ``TypeError`` and ``ValueError`` do not
|
||||
provide all information required by programmers to debug and better understand
|
||||
what caused them.
|
||||
Furthermore, in some cases the messages even have slightly different formats,
|
||||
which makes it really difficult for tools to automatically provide additional
|
||||
information to diagnose the problem.
|
||||
To tackle the former and to lay ground for the latter, it is proposed to expand
|
||||
these exceptions so to hold both the offending and affected entities.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
The main issue this PEP aims to solve is the fact that currently error messages
|
||||
are not that expressive and lack some key information to resolve the exceptions.
|
||||
Additionally, the information present on the error message is not always in the
|
||||
same format, which makes it very difficult for third-party libraries to
|
||||
provide automated diagnosis of the error.
|
||||
|
||||
These automated tools could, for example, detect typos or display or log extra
|
||||
debug information. These could be particularly useful when running tests or in a
|
||||
long running application.
|
||||
|
||||
Although it is in theory possible to have such libraries, they need to resort to
|
||||
hacks in order to achieve the goal. One such example is
|
||||
python-improved-exceptions [1]_, which modifies the byte-code to keep references
|
||||
to the possibly interesting objects and also parses the error messages to
|
||||
extract information like types or names. Unfortunately, such approach is
|
||||
extremely fragile and not portable.
|
||||
|
||||
A similar proposal [2]_ has been implemented for ``ImportError`` and in the same
|
||||
fashion this idea has received support [3]_. Additionally, almost 10 years ago
|
||||
Guido asked in [11]_ to have a clean API to access the affected objects in
|
||||
Exceptions like ``KeyError``, ``AttributeError``, ``NameError`` and
|
||||
``IndexError``. Similar issues and proposals ideas have been written in the
|
||||
last year. Some other issues have been created, but despite receiving support
|
||||
they finally get abandoned. References to the created issues are listed below:
|
||||
|
||||
* ``AttributeError``: [11]_, [10]_, [5]_, [4]_, [3]_
|
||||
|
||||
* ``IndexError``: [11]_, [6]_, [3]_
|
||||
|
||||
* ``KeyError``: [11]_, [7]_, [3]_
|
||||
|
||||
* ``LookupError``: [11]_
|
||||
|
||||
* ``NameError``: [11]_, [10]_, [3]_
|
||||
|
||||
* ``TypeError``: [8]_
|
||||
|
||||
* ``ValueError``: [9]_
|
||||
|
||||
|
||||
To move forward with the development and to centralize the information and
|
||||
discussion, this PEP aims to be a meta-issue summarizing all the above
|
||||
discussions and ideas.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
IndexError
|
||||
----------
|
||||
|
||||
The error message does not reference the list's length nor the index used.
|
||||
|
||||
::
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
a[5]
|
||||
IndexError: list index out of range
|
||||
|
||||
|
||||
KeyError
|
||||
--------
|
||||
|
||||
By convention the key is the first element of the error's argument, but there's
|
||||
no other information regarding the affected dictionary (keys types, size, etc.)
|
||||
|
||||
::
|
||||
|
||||
b = {'foo': 1}
|
||||
b['fo']
|
||||
KeyError: 'fo'
|
||||
|
||||
|
||||
AttributeError
|
||||
--------------
|
||||
|
||||
The object's type and the offending attribute are part of the error message.
|
||||
However, there are some different formats and the information is not always
|
||||
available. Furthermore, although the object type is useful in some cases, given
|
||||
the dynamic nature of Python, it would be much more useful to have a reference
|
||||
to the object itself. Additionally the reference to the type is not fully
|
||||
qualified and in some cases the type is just too generic to provide useful
|
||||
information, for example in case of accessing a module's attribute.
|
||||
|
||||
::
|
||||
|
||||
c = object()
|
||||
c.foo
|
||||
AttributeError: 'object' object has no attribute 'foo'
|
||||
|
||||
import string
|
||||
string.foo
|
||||
AttributeError: 'module' object has no attribute 'foo'
|
||||
|
||||
a = string.Formatter()
|
||||
a.foo
|
||||
AttributeError: 'Formatter' object has no attribute 'foo'
|
||||
|
||||
|
||||
NameError
|
||||
---------
|
||||
|
||||
The error message provides typically the name.
|
||||
|
||||
::
|
||||
|
||||
foo = 1
|
||||
fo
|
||||
NameError: global name 'fo' is not defined
|
||||
|
||||
|
||||
Other Cases
|
||||
-----------
|
||||
|
||||
Issues are even harder to debug when the target object is the result of
|
||||
another expression, for example:
|
||||
|
||||
::
|
||||
|
||||
a[b[c[0]]]
|
||||
|
||||
This issue is also related to the fact that opcodes only have line number
|
||||
information and not the offset. This proposal would help in this case but not as
|
||||
much as having offsets.
|
||||
|
||||
|
||||
Proposal
|
||||
========
|
||||
|
||||
Extend the exceptions ``AttributeError``, ``IndexError``, ``KeyError``,
|
||||
``LookupError``, ``NameError``, , ``TypeError`` and ``ValueError`` with the
|
||||
following:
|
||||
|
||||
* ``AttributeError``: target :sup:`w`, attribute
|
||||
|
||||
* ``IndexError``: target :sup:`w`, key :sup:`w`, index (just an alias to
|
||||
key)
|
||||
|
||||
* ``KeyError``: target :sup:`w`, key :sup:`w`
|
||||
|
||||
* ``LookupError``: target :sup:`w`, key :sup:`w`
|
||||
|
||||
* ``NameError``: name, scope?
|
||||
|
||||
* ``TypeError``: unexpected_type
|
||||
|
||||
* ``ValueError``: unexpected_value :sup:`w`
|
||||
|
||||
Attributes with the superscript :sup:`w` may need to be weak references [12]_ to
|
||||
prevent any memory cycles. However, this may add an unnecessary extra
|
||||
complexity as noted by R. David Murray [13]_. This is specially true given that
|
||||
builtin types do not support being weak referenced.
|
||||
|
||||
TODO(skreft): expand this with examples of corner cases.
|
||||
|
||||
To remain backwards compatible these new attributes will be optional and keyword
|
||||
only.
|
||||
|
||||
It is proposed to add this information, rather than just improve the error, as
|
||||
the former would allow new debugging frameworks and tools and also in the future
|
||||
to switch to a lazy generated message. Generated messages are discussed in [2]_,
|
||||
although they are not implemented at the moment. They would not only save some
|
||||
resources, but also uniform the messages.
|
||||
|
||||
The stdlib will be then gradually changed so to start using these new
|
||||
attributes.
|
||||
|
||||
|
||||
Potential Uses
|
||||
==============
|
||||
|
||||
An automated tool could for example search for similar keys within the object,
|
||||
allowing to display the following:::
|
||||
|
||||
a = {'foo': 1}
|
||||
a['fo']
|
||||
KeyError: 'fo'. Did you mean 'foo'?
|
||||
|
||||
foo = 1
|
||||
fo
|
||||
NameError: global name 'fo' is not defined. Did you mean 'foo'?
|
||||
|
||||
See [3]_ for the output a TestRunner could display.
|
||||
|
||||
|
||||
Performance
|
||||
===========
|
||||
|
||||
Filling these new attributes would only require two extra parameters with data
|
||||
already available so the impact should be marginal. However, it may need
|
||||
special care for ``KeyError`` as the following pattern is already widespread.
|
||||
|
||||
::
|
||||
|
||||
try:
|
||||
a[foo] = a[foo] + 1
|
||||
except:
|
||||
a[foo] = 0
|
||||
|
||||
Note as well that storing these objects into the error itself would allow the
|
||||
lazy generation of the error message, as discussed in [2]_.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Python Exceptions Improved
|
||||
(https://www.github.com/sk-/python-exceptions-improved)
|
||||
|
||||
.. [2] ImportError needs attributes for module and file name
|
||||
(http://bugs.python.org/issue1559549)
|
||||
|
||||
.. [3] Enhance exceptions by attaching some more information to them
|
||||
(https://mail.python.org/pipermail/python-ideas/2014-February/025601.html)
|
||||
|
||||
.. [4] Specifity in AttributeError
|
||||
(https://mail.python.org/pipermail/python-ideas/2013-April/020308.html)
|
||||
|
||||
.. [5] Add an 'attr' attribute to AttributeError
|
||||
(http://bugs.python.org/issue18156)
|
||||
|
||||
.. [6] Add index attribute to IndexError
|
||||
(http://bugs.python.org/issue18162)
|
||||
|
||||
.. [7] Add a 'key' attribute to KeyError
|
||||
(http://bugs.python.org/issue18163)
|
||||
|
||||
.. [8] Add 'unexpected_type' to TypeError
|
||||
(http://bugs.python.org/issue18165)
|
||||
|
||||
.. [9] 'value' attribute for ValueError
|
||||
(http://bugs.python.org/issue18166)
|
||||
|
||||
.. [10] making builtin exceptions more informative
|
||||
(http://bugs.python.org/issue1182143)
|
||||
|
||||
.. [11] LookupError etc. need API to get the key
|
||||
(http://bugs.python.org/issue614557)
|
||||
|
||||
.. [12] weakref - Weak References
|
||||
(https://docs.python.org/3/library/weakref.html)
|
||||
|
||||
.. [13] Message by R. David Murray: Weak refs on exceptions?
|
||||
(http://bugs.python.org/issue18163#msg190791)
|
||||
|
||||
|
||||
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
|
||||
coding: utf-8
|
||||
End:
|
Loading…
Reference in New Issue