1

I'm trying to use the with statement with a class I implemented. But I don't want the constructor of the class to be called.

I tried this:

class Model(ABC):
    def __init__(self):
        # some code that uses class variable from child classes
        # should not be called with with syntax
        pass


    @classmethod
    def __enter__(cls):
        print('call enter')
        return cls

    @classmethod
    def __exit__(cls, *args):
        print('call exit')

with Model:
  print('inside with')

but i get the error: AttributeError: __enter__

I don't understand why i get this error (my class has an enter method). Is there a way to make it work ?

Vince M
  • 890
  • 1
  • 9
  • 21
  • Would [this post](https://stackoverflow.com/questions/3012488/what-is-the-python-with-statement-designed-for) help you? – Christophe Feb 03 '22 at 13:00
  • 1
    Why do you want to use a class as a context manager, rather than an instance of a class? (Ignoring the fact that `Model` itself is an instance of `ABCMeta`.) – chepner Feb 03 '22 at 13:07
  • `Model.__init__` depending on anything from a subclass is somewhat suspect to begin with. – chepner Feb 03 '22 at 13:08
  • @chepner Why Model.__init__ depends on child classes class variables ? I'm building an ORM. Child classes corresponds to db tables. For instance, i have two tables: Comapny & Employee I have the two corresponding classes. Company and Employee class have a list of sql_fields (a field has a name and sql properties such as NON NULL) When instantiating a Model (a company or an employee), I need to check that all non null attributes are defined. This logic is shared between Company and employee, therefore it's implemented in Model Class that uses the sql_fields variable to check. – Vince M Feb 07 '22 at 13:56
  • @chepner Why I use a class as context rather than instance ? Because I can not instantiate Model, I can not use instance of class model as context. Why I need to use a context ? The Model class is responsible for sql executions. For instance the Model class implements insert method using sql_fields child class variables. I need a context manager because if I want to run multiple insert, I want to open a db connection only once. – Vince M Feb 07 '22 at 13:59
  • I'm not a python senior developper, If you think this it's a bad design, I'm open to any other solution. – Vince M Feb 07 '22 at 14:00

2 Answers2

2

You would have to implement __enter__ and __exit__ on the classes' class.

class MyType(type):
    def __enter__(cls):
        print('call enter')
        return cls

    def __exit__(cls, *args):
        print('call exit')

class Model(metaclass=MyType):
    pass

Demo:

>>> with Model:
...     pass
call enter
call exit

Model is an instance of MyType, the latter providing the magic methods.

timgeb
  • 76,762
  • 20
  • 123
  • 145
  • is there a way to add parameters to __enter__ method in your solution? when i try to do so, python is ungry about that Model() receives no arguments. – Bravhek Sep 14 '22 at 14:15
  • @Bravhek see the answers [here](https://stackoverflow.com/questions/5109507/pass-argument-to-enter). – timgeb Sep 14 '22 at 15:07
-2

This should work.

with Model() as m:
    print('inside with')

output:

call enter
inside with
call exit
cyborg
  • 554
  • 2
  • 7