Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a Python class that can only be used as a context manager [duplicate]

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
like image 709
kuzzooroo Avatar asked Jun 05 '15 18:06

kuzzooroo


People also ask

What is a context manager Python?

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)

What does __ enter __ do in Python?

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.

Which of the following keyword is used to enable a context manager in Python?

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.


1 Answers

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.

like image 169
Ami Tavory Avatar answered Oct 12 '22 10:10

Ami Tavory