nesting context managers
Ian Kelly
ian.g.kelly at gmail.com
Tue Dec 20 13:55:59 EST 2011
On Tue, Dec 20, 2011 at 11:46 AM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> On Tue, Dec 20, 2011 at 9:56 AM, Ulrich Eckhardt
> <ulrich.eckhardt at dominolaser.com> wrote:
>> Am 20.12.2011 15:15, schrieb Ulrich Eckhardt:
>>
>>> Let us assume I had a class HTTPClient that has a socket for HTTP and a
>>> logfile for filing logs. I want to make this HTTPClient a context
>>> manager, so that I can write
>>>
>>> with HTTPClient(url) as client:
>>> pass
>>
>>
>> Actually, I overestimated the task:
>>
>> class HTTPClient(url):
>> def __enter__(self):
>> return self
>> def __exit__(self, ctx_type, ctx_val, ctx_tb):
>> self.close()
>> def close(self):
>> self._myfile.close()
>> self._mysocket.close()
>>
>> I'll simply ignore the fact that closing a file can fail if you can't flush
>> buffers. Now, my error was that I have to somehow invoke the file's and
>> socket's enter function in my client's enter function. The default for all
>> files and sockets is that they are already open, they are not opened in the
>> enter function! That way, the client remains usable outside a with context
>> but gives me the desired guarantees inside one.
>>
>> For the case of on-demand allocated stuff, I could write the enter function
>> like this:
>>
>> def __enter__(self):
>> # allocate resources
>> f = ... # open file
>> s = ... # connect socket
>> # attach after completion
>> self._myfile = f
>> self._mysocket = s
>>
>> To be extra safe or in more complicated scenarios, I could wrap this in a
>> try-except and explicitly close those that were already created, but
>> normally I'd expect the garbage collector to do that for me ... or am I then
>> implicitly assuming a specific implementation?
>
> For full generality, copy the code for the contextlib.nested function
> in Python 2.7 or 3.1 and use that. Don't use contextlib.nested
> itself, though, because it's deprecated and does not exist in Python
> 3.2.
Also note that in versions that do support the nested with syntax, you
can do something like:
class HTTPClient(url):
@contextlib.contextmanager
def _contextmanager(file, socket):
with file, socket:
yield
def __enter__(self):
self._cm = self._contextmanager(self._myfile, self._mysocket)
return self._cm.__enter__()
def __exit__(self, exc_type, exc_value, traceback):
try:
return self._cm.__exit__(exc_type, exc_value, traceback)
finally:
del self._cm
Cheers,
Ian
More information about the Python-list
mailing list