Is there a way in Python to write a class that will error unless it's used with a with statement?
# Okay:
with Foo() as f1:
f1.func1()
f1.func2()
# Not okay:
f2 = Foo()
f2.func1()
I can do it manually: have __enter__
set a flag and have every other method check for that flag. But is there a nicer way to do it?
Here's code for the not-so-since way:
class Foo(object):
def __init__(self):
self._entered = False
def __enter__(self):
self._entered = True
return self
def _verify_entered(self):
if not self._entered:
raise Exception("Didn't get call to __enter__")
def __exit__(self, typ, val, traceback):
self._verify_entered()
print("In __exit__")
def func1(self):
self._verify_entered()
# do stuff ...
def func2(self):
self._verify_entered()
# do other stuff
A context manager usually takes care of setting up some resource, e.g. opening a connection, and automatically handles the clean up when we are done with it. Probably, the most common use case is opening a file. with open('/path/to/file.txt', 'r') as f: for line in f: print(line)
A default implementation for object. __enter__() is provided which returns self while object. __exit__() is an abstract method which by default returns None . See also the definition of Context Manager Types.
Python provides an easy way to manage resources: Context Managers. The with keyword is used. When it gets evaluated it should result in an object that performs context management.
Technically, I think that agf has it right in the sense that you could use a metaclass to automate the stuff. However, if I understand the fundamental motivation behind it correctly, I'd suggest a different way.
Suppose you have a Payload
class you want to protect via a context manager. In this case, you simply create a context manager that returns it:
# This should go in a private module.
class Payload(object):
def __init__(self):
print 'payload ctor'
# This should go in the public interface.
class Context(object):
def __init__(self):
# Set up here the parameters.
pass
def __enter__(self):
# Build & return a Payload object
return Payload()
def __exit__(self, exc_type, exc_val, exc_tb):
# Cleanup
pass
with Context() as f:
# f here is a Payload object.
If you hide Payload
in a private module, you're good.
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