Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__enter__ and __exit__ on class level in python 3

I am unsuccessfully trying to get the magic with-statement methods __enter__ and __exit__ running on class-level:

class Spam():

    @classmethod
    def __enter__(cls):
        return cls

    @classmethod
    def __exit__(cls, typ, value, tb):
        cls.cleanup_stuff()


with Spam:
    pass

However, this will result in an AttributeError:

Traceback (most recent call last):
  File "./test.py", line 15, in <module>
    with Spam:
AttributeError: __exit__

Is it possible to use the __enter__ and __exit__ methods on class-level anyway?

like image 819
Richard Neumann Avatar asked Feb 04 '15 20:02

Richard Neumann


Video Answer


1 Answers

__enter__ and __exit__ are special methods, and as such only work correctly when defined on a object's type, not in it's instance dictionary.

Now Spam is a instance of type, and type(Spam).__enter__ and type(Spam).__exit__ do not exist. Therefore you get an attribute error.

To make this work, the methods would need to be declared on the metaclass of the class you want to use. Example:

class Spam(type):

    def __enter__(cls):
        print('enter')
        return cls

    def __exit__(cls, typ, value, tb):
        print('exit')

class Eggs(metaclass=Spam):
    pass

with Eggs:
    pass

Now Eggs is an instance of Spam (type(Eggs) == Spam, and therefore type(Eggs).__enter__ and type(Eggs).__exit__ do exist).

However defining a metaclass just to use an instance of it as a context manager seems a little over the top. The more straight forward solution starting from your example would be to just use

with Spam():
    pass

Or if you want to reuse the same instance later:

spam = Spam()
with spam:
    pass
like image 68
mata Avatar answered Sep 23 '22 21:09

mata