Let's say we've got an class 'A' that acts as a context manager in it's own right, so it implements the
def __enter__()
def __exit__()
interface. It's valid for client code to make 'A' objects directly using a with
statement.
Now, we've also got another class 'B' that encapsulates other functionality, as well makes use of an 'A' object.
If we'd like to have 'B' act as a context manager as well, what's the right way of managing it's 'A' instance?
Should the implementation of __enter__
and __exit__
on 'B' call __enter__
and __exit__
(respectively) on it's A-object instance? Or is there a better way?
To give a concrete example (this is not what I'm using in my application, it's just the first non-abstract example that came to mind) consider two classes
DatabaseConnection
DatabaseConnectionPool
It's valid to use a single DatabaseConnection
on it's own, thus DatabaseConnection
implements the context-manager interface.
DatabaseConnectionPool
makes use of several DatabaseConnections
as well as other bits and bobs. Using DatabaseConnectionPool
(i.e. "with") should do the set up and tear-down on it's DatabaseConnection
instances (along with whatever else it might want to do)
Update: I've written some test code which I was hoping would give the following output:
Enter invoked on Outer Enter invoked on Inner Within outer context... do_foo invoked! Still using outer... Exit invoked on inner Exit invoked on outer Done using outer
but I got the following:
Enter invoked on Outer Enter invoked on Inner Exit invoked on inner Within outer context... do_foo invoked! Still using outer... Exit invoked on outer Done using outer
Code:
class Inner(object):
def __enter__(self):
print "Enter invoked on Inner"
return self
def __exit__(self, typ, val, tb):
print "Exit invoked on inner"
def do_foo(self):
print "do_foo invoked!"
class Outer(object):
def __init__(self):
self._inner = Inner()
def __enter__(self):
print "Enter invoked on Outer"
with self._inner as ctx:
return self
def __exit__(self, typ, val, tb):
print "Exit invoked on outer"
with Outer() as outer:
print "Within outer context..."
outer._inner.do_foo()
print "Still using outer..."
print "Done using outer"
Any ideas on how to make this work?
FWIW, I had the same design consideration, and ended up writing exit and enter functions in my 'child' classes, and only have the 'parent' class be a context manager.
(If I've understood your question correctly, which would be 'how can I elegantly also have all the classes that I've used in a context manager class close correctly when I've closed the parent context manager)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With