Commit 06cac90b authored by Kirill Smelkov's avatar Kirill Smelkov

golang: Fix defer exception chaining for regular exceptions raised from under @func

Defer support for exception chaining on Python2 (see bb9a94c3 "golang:
Teach defer to chain exceptions (PEP 3134) even on Python2") simulates
PEP 3134 by manually setting and chaining exc .__context__, .__cause__
and .__traceback__: if exceptions are thrown by several deferred
functions they are linked into exception chain via .__context__
attribute. Defer support in @func also makes sure that any exception
that comes out of a function has all those PEP 3134 attributes and
presets them to their default values if exception object did not have
them initially. In particular .__context__ is preset to None for first
raised exception.

There was a logic error in chaining handling: .__context__ was glued to
previously raised exception only if current exception object did not
have .__context__ attribute at all. And this was failing to chain them
if current exception was raised from under another function wrapped in
@func, because GoFrame wrapping that function makes sure to set
curexc.__context__=None and oops - it was not changed later.

-> Fix chaining implementation by gluing .__context__ either if there is
no such attribute in current exception, or if .__context__ is None.

This is correct to do since .__context__, by its definition, represents
implicitly / automatically chained exceptions - contrary to .__cause__
which is explicitly set and would be incorrect to automatically change
from None to something. We also know that the end result is the same as
Python3 behaviour since updated tests (see bellow) pass OK also when run
under Python3 where exceptions chaining is implemented by Python runtime
natively.

Update test_defer_excchain() in golang_test.py, which verifies how
raised exceptions are chained.

No need to update testprog/golang_test_defer_excchain.{py,txt} since the
test there verifies that our traceback dumper is correctly hooked into
Python interpreter - where we know that exceptions are already chained
correctly and we verify only that automatic traceback dump takes this
chaining into account.

Fixes: bb9a94c3 (golang: Teach defer to chain exceptions (PEP 3134) even on Python2)
parent 7faaecbc
...@@ -131,7 +131,7 @@ class _GoFrame: ...@@ -131,7 +131,7 @@ class _GoFrame:
# py2: simulate exception chaining (PEP 3134) # py2: simulate exception chaining (PEP 3134)
if six.PY2: if six.PY2:
if exc_val is not None: if exc_val is not None:
if not hasattr(exc_val, '__context__'): if getattr(exc_val, '__context__', None) is None:
exc_val.__context__ = __goframe__.exc_ctx exc_val.__context__ = __goframe__.exc_ctx
if not hasattr(exc_val, '__cause__'): if not hasattr(exc_val, '__cause__'):
exc_val.__cause__ = None exc_val.__cause__ = None
......
...@@ -1243,8 +1243,9 @@ def test_defer_excchain(): ...@@ -1243,8 +1243,9 @@ def test_defer_excchain():
# exceptions in deferred calls are chained # exceptions in deferred calls are chained
def d1(): def d1():
raise RuntimeError("d1: aaa") raise RuntimeError("d1: aaa")
def d2(): @func
1/0 def d2(): # NOTE regular raise inside @func
1/0 # which initially sets .__context__ to None
def d3(): def d3():
raise RuntimeError("d3: bbb") raise RuntimeError("d3: bbb")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment