I second the general idea of using context managers and the with statement instead of relying on __del__ (for much the same reasons one prefers try/finally to finalizer methods in Java, plus one: in Python, the presence of __del__ methods can make cyclic garbage uncollectable).
However, given that the goal is to have "an object that cleans up after itself upon exit or an exception", the implementation by @~unutbu is not correct:
@contextlib.contextmanager def make_client(): c=Client() yield c c.disconnect_from_server() with make_client() as c: ...
If an exception is raised in the ... part, disconnect_from_server_ does not get called (since the exception propagates through make_client, being uncaught there, and therefore terminates it while it's waiting at the yield).
The fix is simple:
@contextlib.contextmanager def make_client(): c=Client() try: yield c finally: c.disconnect_from_server()
Essentially, the with statement lets you almost forget about the good old try/finally statement... except when you're writing context managers with contextlib, and then it's really important to remember it!-)