(original) (raw)
On Mon, 12 Jun 2017 at 01:08 Nick Coghlan <ncoghlan@gmail.com> wrote:
On 12 June 2017 at 17:31, Martin (gzlist) via Python-Dev
<python-dev@python.org> wrote:
\> On 12/06/2017, Serhiy Storchaka <storchaka@gmail.com> wrote:
\>>
\>> But if an error is raised when execute undo\_something(), it replaces the
\>> original exception which become chaining as the \_\_context\_\_ attribute.
\>> The problem is that this can change the type of the exception. If
\>> do\_something\_other() raises SystemExit and undo\_something() raises
\>> KeyError, the final exception has type KeyError.
\>
\> For Python 2.7, I've used the following idiom, which always masks
\> errors from the undo:
\>
\> do\_something()
\> try:
\> do\_something\_other()
\> except:
\> try:
\> undo\_something()
\> finally:
\> raise
\>
\> Unfortunately, that breaks down on Python 3, as the new exception
\> propogates with the original as context..
Relevant tracker issue for that problem: https://bugs.python.org/issue18861
\>> Does it mean that we should rewrite every chunk of code similar to the
\>> above? And if such cumbersome code is necessary and become common, maybe
\>> it needs syntax support in the language? Or changing the semantic of
\>> exceptions raised in error handlers and finally blocks?
\>
\> What I want is a way to chain exceptions in the other direction,
\> raising the original error, but attaching a related one. Unfortunately
\> neither \_\_cause\_\_ or \_\_context\_\_ really help.
Aye, agreed. The key challenge we have is that we're trying to
represent the exception state as a linked list, when what we really
have once we start taking cleanup errors into account is an exception
\*tree\*.
The thing that finally clicked for me in this thread is that if we add
contextlib.ResourceSet, and have it add a new attribute to the
original exception (e.g. "\_\_cleanup\_errors\_\_") with a list of
exceptions raised while attempt to cleanup after the earlier
exception, then that lets us actually start modelling that tree at
runtime.
You might want to go back and read Jake's LWN article for this year's language summit as Nathaniel Smith said he wanted some sort of multi-exception type which would go along with this idea.
-Brett
Once we understand the \*behaviour\* we want, \*then\* we can consider
whether we might want to add a context manager to have any raised
exceptions be attached to the currently active exception as cleanup
errors rather than as new exceptions in their own right.
For example:
do\_something()
try:
do\_something\_other()
except BaseException as exc:
with contextlib.cleanup(exc) as reraise:
\# Exceptions raised in here would be added to
\# exc.\_\_cleanup\_errors\_\_, rather than being
\# propagated normally
undo\_something()
reraise()
The need for the "as reraise:"/"reraise()" trick arises from the need
to special case the situation where the original exception inherits
from Exception, but one of the raised exceptions \*doesn't\* - we want
SystemExit/KeyboardInterrupt/etc to take precedence in that case, and
a bare raise statement won't handle that for us (it \*could\* in a
hypothetical future version of Python that's natively
\`\_\_cleanup\_errors\_\_\` aware, but that's not going to be useful for
existing versions).
Since I don't see anything in the discussion so far that \*requires\*
changes to the standard library (aside from "we may want to use this
ourselves"), the right place to thrash out the design details is
probably contextlib2: https://github.com/jazzband/contextlib2
That's where contextlib.ExitStack was born, and I prefer using it to
iterate on context management design concepts, since we can push
updates out faster, and if we make bad choices anywhere along the way,
they can just sit around in contextlib2, rather than polluting the
standard library indefinitely.
Cheers,
Nick.
P.S. trio's MultiError is also likely worth looking into in this context
\--
Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: https://mail.python.org/mailman/options/python-dev/brett%40python.org