Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delegate management of child context to parent

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?

like image 495
eddiewould Avatar asked Jun 15 '14 23:06

eddiewould


1 Answers

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)

like image 162
Andrew Magerman Avatar answered Sep 20 '22 18:09

Andrew Magerman