Attributeerror: __enter__ in context manager implementation

Asked

Viewed 1,437 times

1

I’m trying to understand the workings of gerenciadores de contexto Python, implementing a contextmanager. Here follows the script:

class contextmanager(object):

    def __init__(self, func):
        self._func = func

    def __call__(self, *args, **kwargs):
        print('__call__')
        self._gen = self._func(*args, **kwargs)
        return self.__enter__()

    def __enter__(self):
        print('__enter__')
        return next(self._gen)

    def __exit__(self, exc_type, exc_value, exc_tb):
        print('__exit__')
        next(self._gen)


import os

@contextmanager
def changepath(path):
    actual = os.getcwd()
    os.chdir(path)
    yield
    os.chdir(actual)


with changepath('downloads') as path:
    print(os.getcwd())
print(os.getcwd())

But the way out is:

__call__
__enter__
Traceback (most recent call last):
  File "C:\Users\Thiaguinho\AppData\Local\Programs\Python\Python36-32\Code\NIGHT\_eigth_asyncio.py", line 99, in <module>
    with changepath('downloads') as path:
AttributeError: __enter__

Can someone tell me where the mistake is ?

1 answer

3


Can someone tell me where the mistake is ?

Yes.

On this line inside the __call__:

    return self.__enter__()

The object that the with will use have to have a method __enter__. It is called automatically by language when executing the command with. That is, this line should simply return self. The method __enter__ the object will be called soon after by the language. When you call the __enter__ directly, the code will perform the decorated function until the yield and return, as the value of __next__ the amount sent by yield - that since it’s blank, it’s None. In sequence, Python tries to call the __enter__ in that amount None, and then your mistake occurs.

As a rule, all methods that are flagged by the two underscores at the beginning and at the end (the so-called "Dunder methods") are called by the language, not explicitly by the programmer.

Besides, for those who are trying to "understand the command with", you are reimplementing a function that exists in the contextlib of the standard library and which makes very advanced use of properties of generators to allow them to be implemented with the syntax of a function - I don’t know if this is the most didactic way to understand the with. Simple use is with a class who has the methods __enter__ and __exit__ that do directly what you want to when entering and leaving the block with.

  • Thanks again jsbueno

Browser other questions tagged

You are not signed in. Login or sign up in order to post.