Hi all, I want to point out that if it's not common to dispatch on values of exceptions it might be **because** it is hard to do or to know wether an exception will be structured or not. If Exceptions were by default more structured, if CPython would provide a default "StructuredException", or were the norm because CPython would use more of them – then you might see more use case, and new code patterns.
In particular the fact that you have to catch and exception, then check values is annoying, I could see: try: ... except OSError(EACCES): # handle not permitted only except OSError(ENOENT): # handle does not exists only #implicit otherwise reraise Be a much cleaner way of filtering exception _if_ python were to provide that. Yes in Python 3 these are sometime their own subclass (FileNotFoundError), but isn't the fact that you _have_ to subclass be a hint that something might be wrong there ? You can see it in the fact that FilenotFoundError need to be caught _before_ OSError, it is not obvious to the beginner that FileNotFoundError is a subclass of OSError and that you can't refactor by changing the excepts order. Of course the custom subclass names are way more readable, (at least to me) but it is easy as an experienced Python programmer to forget what is difficult for newcommers. And this is (IMHO) one point which is not easy. One example I'm often wishing to have better filter is warnings where you have to use a regular expression to mute (or turn into errors) a subset of warnings. Of course most of these use case can be dealt with by Subclassing and having custom attributes (if you are in control of the raising code). But nobody will use it if it's not encouraged by the core. > Where there is a clear and obvious need the core devs have spent the > time to give the exception class a rich API for extracting useful > information. See OSError, which offers named attributes for the errno, > error message, Windows error number (when appropriate) and two file > names. This does not encourage and show that doing structured exception is hard and _not_ the standard. > There should be one-- and preferably only one --obvious way to do it. So structured or not ? Please find post signature an extremely non scientific grep of all the usage of structured information on Exceptions on my machine Thanks, -- Matthias $ rg -tpy ' e\.[^g]' | grep -v test | grep -v ENOENT | grep if | grep -v sympy | grep -v 'for e ' | grep -v getorg | grep -v ename |grep -v errno gh-activity/ghactivity.py: source = get_source_login(e.repo) if e.repo[0] == login else e.repo[0] pypi/store.py: if e.code == 204: pypi-legacy/store.py: if e.code == 204: cpython/Lib/asyncore.py: if e.args[0] not in _DISCONNECTED: cpython/Lib/nntplib.py: if e.response.startswith('480'): cpython/Lib/pdb.py: if e.startswith(text)] cpython/Lib/runpy.py: if e.name is None or (e.name != pkg_name and flit/flit/upload.py: if (not repo['is_warehouse']) and e.response.status_code == 403: gitsome/xonsh/execer.py: if (e.loc is None) or (last_error_line == e.loc.lineno and git-cpython/Lib/asyncore.py: if e.args[0] not in _DISCONNECTED: git-cpython/Lib/nntplib.py: if e.response.startswith('480'): git-cpython/Lib/pdb.py: if e.startswith(text)] git-cpython/Lib/runpy.py: if e.name is None or (e.name != pkg_name and jupyter_client/jupyter_client/manager.py: if e.winerror != 5: jupyterhub/jupyterhub/utils.py: if e.code >= 500: jupyterhub/jupyterhub/utils.py: if e.code != 599: procbuild/procbuild/builder.py: if not 'Resource temporarily unavailable' in e.strerror: qtconsole/qtconsole/console_widget.py: if e.mimeData().hasUrls(): qtconsole/qtconsole/console_widget.py: elif e.mimeData().hasText(): qtconsole/qtconsole/console_widget.py: if e.mimeData().hasUrls(): qtconsole/qtconsole/console_widget.py: elif e.mimeData().hasText(): qtconsole/qtconsole/console_widget.py: if e.mimeData().hasUrls(): qtconsole/qtconsole/console_widget.py: elif e.mimeData().hasText(): xonsh/xonsh/execer.py: if (e.loc is None) or (last_error_line == e.loc.lineno and xonsh/xonsh/execer.py: if not greedy and maxcol in (e.loc.column + 1, e.loc.column): xonsh/xonsh/proc.py: r = e.code if isinstance(e.code, int) else int(bool(e.code)) django/django/forms/fields.py: if hasattr(e, 'code') and e.code in self.error_messages: django/django/forms/fields.py: errors.extend(m for m in e.error_list if m not in errors) django/django/http/multipartparser.py: if not e.connection_reset: cpython/Lib/asyncio/streams.py: if self._buffer.startswith(sep, e.consumed): cpython/Lib/idlelib/MultiCall.py: if not APPLICATION_GONE in e.args[0]: cpython/Lib/idlelib/MultiCall.py: if not APPLICATION_GONE in e.args[0]: cpython/Lib/idlelib/MultiCall.py: if not APPLICATION_GONE in e.args[0]: cpython/Lib/multiprocessing/connection.py: if e.winerror == _winapi.ERROR_BROKEN_PIPE: cpython/Lib/multiprocessing/connection.py: if e.winerror != _winapi.ERROR_NO_DATA: cpython/Lib/multiprocessing/connection.py: if e.winerror not in (_winapi.ERROR_SEM_TIMEOUT, cpython/Lib/multiprocessing/process.py: if not e.args: git-cpython/cpython/Lib/asyncore.py: if e.args[0] not in (EBADF, ECONNRESET, ENOTCONN, ESHUTDOWN, git-cpython/cpython/Lib/nntplib.py: if user and e.response[:3] == '480': git-cpython/cpython/Lib/socket.py: if e.args[0] == EINTR: git-cpython/cpython/Lib/socket.py: if e.args[0] == EINTR: git-cpython/cpython/Lib/socket.py: if e.args[0] == EINTR: git-cpython/cpython/Lib/socket.py: if e.args[0] == EINTR: git-cpython/cpython/Lib/socket.py: if e.args[0] == EINTR: git-cpython/Lib/asyncio/streams.py: if self._buffer.startswith(sep, e.consumed): ipyparallel/ipyparallel/client/client.py: if e.engine_info: ipython/IPython/core/inputtransformer.py: if 'multi-line string' in e.args[0]: ipython/IPython/core/inputsplitter.py: if 'multi-line string' in e.args[0]: ipython/IPython/core/inputsplitter.py: elif 'multi-line statement' in e.args[0]: git-cpython/Lib/idlelib/multicall.py: if not APPLICATION_GONE in e.args[0]: git-cpython/Lib/idlelib/multicall.py: if not APPLICATION_GONE in e.args[0]: git-cpython/Lib/idlelib/multicall.py: if not APPLICATION_GONE in e.args[0]: git-cpython/Lib/multiprocessing/connection.py: if e.winerror == _winapi.ERROR_BROKEN_PIPE: git-cpython/Lib/multiprocessing/connection.py: if e.winerror != _winapi.ERROR_NO_DATA: git-cpython/Lib/multiprocessing/connection.py: if e.winerror not in (_winapi.ERROR_SEM_TIMEOUT, git-cpython/Lib/multiprocessing/process.py: if not e.args: jedi/jedi/api/completion.py: if e.error_leaf.value == '.': meeseeksbox/meeseeksdev/meeseeksbox/commands.py: if ('git commit --allow-empty' in e.stderr) or ('git commit --allow-empty' in e.stdout): meeseeksbox/meeseeksdev/meeseeksbox/commands.py: elif "after resolving the conflicts" in e.stderr: notebook/notebook/notebook/handlers.py: if e.status_code == 404 and 'files' in path.split('/'): pandas/pandas/core/strings.py: if len(e.args) >= 1 and re.search(p_err, e.args[0]): pip/pip/_vendor/pyparsing.py: if not e.mayReturnEmpty: prompt_toolkit/prompt_toolkit/terminal/vt100_output.py: elif e.args and e.args[0] == 0: rust/src/etc/dec2flt_table.py:range of exponents e. The output is one array of 64 bit significands and rust/src/etc/htmldocck.py: if e.tail: rust/src/etc/htmldocck.py: if attr in e.attrib: setuptools/pkg_resources/_vendor/pyparsing.py: if not e.mayReturnEmpty: scikit-learn/sklearn/datasets/mldata.py: if e.code == 404: numpy/tools/npy_tempita/__init__.py: if e.args: django/django/core/files/images.py: if e.args[0].startswith("Error -5"): django/django/core/management/base.py: if e.is_serious() cpython/Lib/xml/etree/ElementInclude.py: if e.tag == XINCLUDE_INCLUDE: cpython/Lib/xml/etree/ElementInclude.py: if e.tail: cpython/Lib/xml/etree/ElementInclude.py: elif e.tag == XINCLUDE_FALLBACK: cpython/Lib/xml/etree/ElementPath.py: if e.tag == tag: git-cpython/cpython/Demo/pdist/rcvs.py: if not e.commitcheck(): git-cpython/cpython/Demo/pdist/rcvs.py: if e.commit(message): git-cpython/cpython/Demo/pdist/rcvs.py: e.diff(opts) git-cpython/cpython/Demo/pdist/rcvs.py: if e.proxy is None: git-cpython/cpython/Lib/multiprocessing/connection.py: if e.args[0] != win32.ERROR_PIPE_CONNECTED: git-cpython/cpython/Lib/multiprocessing/connection.py: if e.args[0] != win32.ERROR_PIPE_CONNECTED: git-cpython/cpython/Lib/multiprocessing/connection.py: if e.args[0] not in (win32.ERROR_SEM_TIMEOUT, git-cpython/cpython/Lib/multiprocessing/process.py: if not e.args: git-cpython/Lib/xml/etree/ElementInclude.py: if e.tag == XINCLUDE_INCLUDE: git-cpython/Lib/xml/etree/ElementInclude.py: if e.tail: git-cpython/Lib/xml/etree/ElementInclude.py: elif e.tag == XINCLUDE_FALLBACK: git-cpython/Lib/xml/etree/ElementPath.py: if e.tag == tag: pip/pip/_vendor/distlib/locators.py: if e.code != 404: pip/pip/_vendor/distlib/scripts.py: if e.startswith('.py'): pip/pip/_vendor/html5lib/serializer.py: if not e.endswith(";"): pythondotorg/blogs/management/commands/update_blogs.py: if e.pub_date < entry['pub_date']: scikit-learn/doc/tutorial/machine_learning_map/svg2imagemap.py: if e.nodeName == 'g': scikit-learn/doc/tutorial/machine_learning_map/svg2imagemap.py: if e.hasAttribute('transform'): scikit-learn/doc/tutorial/machine_learning_map/pyparsing.py: if not e.mayReturnEmpty: scikit-learn/doc/tutorial/machine_learning_map/pyparsing.py: if not e.mayReturnEmpty: scikit-learn/doc/tutorial/machine_learning_map/pyparsing.py: if e.mayReturnEmpty: scikit-learn/doc/tutorial/machine_learning_map/pyparsing.py: if e.mayReturnEmpty: scikit-learn/doc/tutorial/machine_learning_map/pyparsing.py: if not e.mayReturnEmpty: django/django/db/backends/mysql/base.py: if e.args[0] in self.codes_for_integrityerror: django/django/db/backends/mysql/base.py: if e.args[0] in self.codes_for_integrityerror: django/django/db/models/fields/__init__.py: if hasattr(e, 'code') and e.code in self.error_messages: git-cpython/cpython/Lib/xml/etree/ElementInclude.py: if e.tag == XINCLUDE_INCLUDE: git-cpython/cpython/Lib/xml/etree/ElementInclude.py: if e.tail: git-cpython/cpython/Lib/xml/etree/ElementInclude.py: elif e.tag == XINCLUDE_FALLBACK: pip/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py: if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): pip/pip/_vendor/requests/packages/urllib3/contrib/pyopenssl.py: if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): pip/pip/_vendor/requests/packages/urllib3/contrib/socks.py: if e.socket_err: On Wed, Jul 5, 2017 at 1:36 PM, Steven D'Aprano <st...@pearwood.info> wrote: > On Tue, Jul 04, 2017 at 11:37:51PM -0400, Terry Reedy wrote: > >> I personally been on the side of wanting richer exceptions. > > Could you explain what you would use them for? Ken has give two > use-cases which I personally consider are relatively niche, and perhaps > even counter-productive: > > - translation into the user's native language; > > - providing some sort of "did you mean...?" functionality. > > Jeff Walker also suggested being able to extract the line and column > from certain kinds of JSON errors. (But that would depend on the json > module having an API that supports that use-case. You can't just say > line_no = exception.args[0] if there's no guarantee that it actually > will be the line number.) > > What would you use these for? I imagine you're thinking of this as the > maintainer of IDLE? > > >> So what has >> been the resistance? Speed is definitely one. Maybe space? Probably >> maintenance cost. Lack of interest among true 'core' (C competent) >> developers? > > Where there is a clear and obvious need the core devs have spent the > time to give the exception class a rich API for extracting useful > information. See OSError, which offers named attributes for the errno, > error message, Windows error number (when appropriate) and two file > names. > > I expect that the fact that few of the other builtin or stdlib > exceptions similarly offer named attributes is because nobody thought > of it, or saw any need. > > > > -- > Steve > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/