I have a context decorator that has side effects. I noticed that side effects do not occur if I use a dict understanding.
from contextlib import contextmanager
import traceback
import sys
accumulated = []
@contextmanager
def accumulate(s):
try:
yield
finally:
print("Appending %r to accumulated" % s)
accumulated.append(s)
def iterate_and_accumulate(iterable):
for item in iterable:
with accumulate(item):
yield item
def boom_unless_zero(i):
if i > 0:
raise RuntimeError("Boom!")
try:
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
except:
traceback.print_exc()
print(accumulated)
print('\n=====\n')
try:
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
except:
traceback.print_exc()
print(accumulated)
print('Finished!')
Conclusion:
$ python2 boom3.py
Appending 0 to accumulated
Traceback (most recent call last):
File "boom3.py", line 25, in <module>
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
File "boom3.py", line 25, in <dictcomp>
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
File "boom3.py", line 22, in boom_unless_zero
raise RuntimeError("Boom!")
RuntimeError: Boom!
[0]
=====
Appending 0 to accumulated
Appending 1 to accumulated
Traceback (most recent call last):
File "boom3.py", line 34, in <module>
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
File "boom3.py", line 34, in <dictcomp>
{i: boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])}
File "boom3.py", line 22, in boom_unless_zero
raise RuntimeError("Boom!")
RuntimeError: Boom!
[0, 0, 1]
Finished!
Appending 1 to accumulated
It is bizarre that a side effect occurs after my script is "finished". This means that users cannot use my contextdecorator if they use dict comprehension.
I noticed that this behavior disappears in Python 3, and the behavior also does not occur if I write [boom_unless_zero(i) for i in iterate_and_accumulate([0, 1])]instead of understanding the dict.
Why is this happening?
source
share