[New-bugs-announce] [issue44292] contextmanager + ExitStack.pop_all()
Luca Mattiello
report at bugs.python.org
Wed Jun 2 17:59:52 EDT 2021
New submission from Luca Mattiello <lucae.mattiello at gmail.com>:
Reading the contextlib documentation, one might assume the following to be functionally equivalent, when used in a with statement:
@contextlib.contextmanager
def managed_resource():
resource = acquire()
try:
yield resource
finally:
resource.release()
class ManagedResource:
def __init__(self):
self.resource = acquire()
def __enter__(self):
return self.resource
def __exit__(self, *args):
self.resource.release()
However, the first version has a seemingly unexpected behavior when used in conjunction with an ExitStack, and pop_all().
with contextlib.ExitStack() as es:
r = es.enter_context(managed_resource())
es.pop_all()
# Uh-oh, r gets released anyway
with contextlib.ExitStack() as es:
r = es.enter_context(ManagedResource())
es.pop_all()
# Works as expected
I think the reason is https://docs.python.org/3/reference/expressions.html#yield-expressions, in particular
> Yield expressions are allowed anywhere in a try construct.
> If the generator is not resumed before it is finalized (by
> reaching a zero reference count or by being garbage collected),
> the generator-iterator’s close() method will be called,
> allowing any pending finally clauses to execute.
I guess this is working according to the specs, but I found it very counter-intuitive. Could we improve the documentation to point out this subtle difference?
Full repro:
import contextlib
@contextlib.contextmanager
def cm():
print("acquire cm")
try:
yield 1
finally:
print("release cm")
class CM:
def __init__(self):
print("acquire CM")
def __enter__(self):
return 1
def __exit__(self, *args):
print("release CM")
def f1():
with contextlib.ExitStack() as es:
es.enter_context(cm())
es.pop_all()
def f2():
with contextlib.ExitStack() as es:
es.enter_context(CM())
es.pop_all()
f1()
f2()
Output:
acquire cm
release cm
acquire CM
----------
assignee: docs at python
components: Documentation, Library (Lib)
messages: 394948
nosy: docs at python, lucae.mattiello
priority: normal
severity: normal
status: open
title: contextmanager + ExitStack.pop_all()
versions: Python 3.6, Python 3.7, Python 3.8, Python 3.9
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue44292>
_______________________________________
More information about the New-bugs-announce
mailing list