On 19 November 2016 at 07:00, Greg Ewing <[email protected]> wrote:
> Ram Rachum wrote:
>>
>> 1. I'm making a program that lets people lease machines. They can issue a
>> command to lease 7 machines. ... If everything goes fine, I do pop_all on
>> the exit stack so it doesn't get exited and the machines stay leased,
>
>
> Seems to me that would be done more easily and clearly
> without involving a context manager at all
ExitStack() handles that case by design - doing "pop_all() gives you a
fresh ExitStack() instance, while making close() and __exit__() on the
original no-ops. Since ExitStack() deliberately never cleans up the
stack implicitly, you end up being able to do:
machines = []
with contextlib.ExitStack() as cleanup:
for i in range(num_machines_required):
machines.append(lease_machine())
cleanup.callback(release_machine, machine)
cleanup.pop_all() # It worked, so don't clean anything up yet
return machines
However, ExitStack *itself* can't readily be written using
contextlib.contextmanager - it's not impossible, but it's convoluted
enough not to be worth the hassle, since you'd need to do something
like:
class _ExitStack:
# Like the current ExitStack, but without __enter__/__exit__
@contextmanager
def exit_stack()
state = _ExitStack()
try:
yield state
finally:
state.close()
Externally, the visible differences would be that:
- "es_cm = exit_stack()" and "es_state = exit_stack().__enter__()"
would give different results
- it would be slower and use more memory due to the additional layer
of indirection
Since the extra layer of indirection doesn't buy you any new
capabilities and has a runtime performance cost, there's no real
reason to do it.
The general case of that pattern is to yield a state variable that
just has a single attribute "needs_cleanup":
@contextmanager
def my_cm()
... # Do setup operations here
state = types.SimpleNamespace(needs_cleanup=True)
try:
yield state
finally:
if state.needs_cleanup:
... # Do cleanup operations here
However, as with ExitStack, at that point, you're usually going to be
better off just implementing __enter__ and __exit__ yourself, and not
worrying about using the generator format.
This kind of difference in flexibility isn't really specific to
context managers though - it's a particular instance of the general
pattern that custom classes are often more convenient than closures
when you actually *want* to expose externally mutable state.
Cheers,
Nick.
--
Nick Coghlan | [email protected] | Brisbane, Australia
_______________________________________________
Python-ideas mailing list
[email protected]
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/