[Python-Dev] PEP 343: Resource Composition and Idempotent __exit__
Nick Coghlan
ncoghlan at gmail.com
Sun May 15 13:39:51 CEST 2005
Ka-Ping Yee wrote:
> - The generator style in PEP 340 is the easiest to compose and
> reuse, but its implementation is the most complex to understand.
The latest version of my PEP 3XX aims to get (most of) the power of PEP 340,
with the easy comprehensibility of PEP 310. What magic it requires is almost
entirely contained in the statement_template decorator.
It can be looked at as PEP 340 without the looping or ability to suppress
exceptions, or as PEP 343, with PEP 340's style of using generators to write
templates.
It falls into the category where __enter__ and __exit__ are paired, as it uses
the same expansion as Shane describes (an exception in __enter__ means that
__exit__ is never executed).
> To evaluate these options, we could look at a few scenarios where we're
> trying to write a resource wrapper for some lock objects. Each lock
> object has two methods, .acquire() and .release().
>
> Scenario 1. You have two resource objects and you want to acquire both.
>
> Scenario 2. You want a single resource object that acquires two locks.
>
> Scenario 3. Your resource object acquires one of two locks depending
> on some runtime condition.
For your three scenarios, PEP 3XX usage and implementation are as you describe
for PEP 343. PEP 343 itself doesn't work as you describe, as it still prohibits
yielding inside a try/finally block (and, by extension, inside a with statement,
as that is just syntactic sugar for a particular type of try/finally).
Scenario 1 (two locks, handled manually):
PEP 3XX actually recommends supplying __enter__ and __exit__ directly on lock
objects, so no additional 'resource' wrapper is required:
with lock1:
with lock2:
BLOCK
And the relevant lock methods are:
def __enter__(self):
self.acquire()
def __exit__(self, *exc):
self.release()
However, if that didn't happen, and an external wrapper was needed, it could be
optimally implemented as:
class Resource(object):
def __init__(self, lock):
self.lock = lock
def __enter__(self):
self.lock.acquire()
def __exit__(self, *exc):
self.lock.release()
Or less efficiently as:
@statement_template
def Resource(lock):
lock.acquire()
try:
yield
finally:
lock.release()
Scenario 2 (two locks, handled by resource):
Used as:
with DoubleResource(lock1, lock2):
BLOCK
Implemented as:
@statement_template
def DoubleResource(resource1, resource2):
with resource1:
with resource2:
yield
Scenario 3 (runtime choice of lock):
Used as:
with ConditionalResource(condition, lock1, lock2):
BLOCK
Implemented as:
@statement_template
def ConditionalResource(condition, resource1, resource2):
if condition:
with resource1:
yield
else:
with resource2:
yield
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.blogspot.com
More information about the Python-Dev
mailing list