Search code examples
pythonyieldcontextmanager

Python - Class Content manager. Why __exit__ method is omitted if you put yield inside of it?


class OracleConnected():
    def __init__(self):
        self.oracle_username = oracle_username[1]
        self.oracle_password = oracle_password[1]
        self.dsn = cx_Oracle.makedsn(hostname[1], port[1], sid[1])
    def __enter__(self):
        try:
            print('Then this')
            cx_Oracle.init_oracle_client(lib_dir=lib_dir)
            self.connection = cx_Oracle.connect(user=self.oracle_username, password=self.oracle_password, dsn=self.dsn)
        except Exception as e:
            print(f'Error connecting to Oracle: \n{e}')
        else:
            yield self.connection

    def __exit__(self, exc_type, exc_val, exc_tb):
            print('Finally do this')
            #self.connection.close()
            yield self.connection.close()


with OracleConnected() as connection:
    print('It first do this')
    cur = next(connection).cursor()
    cur.execute("select *From payments where id = 324986093")
    print(cur.fetchall())
  1. If everything stays the same I got: It first do this Then this [(324986093, 391908893, 391908893, 1228, None, datetime.datetime(2023, 4, 24, 0, 0)]

  2. Instead of yield self.connection.close() -> write just self.connection.close(): It first do this Then this [(324986093, 391908893, 391908893, 1228, None, datetime.datetime(2023, 4, 24, 0, 0)] Finally do this

So the question is, what is so special about yield that it gives me so much troubles?

P.S. Also, Instead of yield self.connection.close() -> return self.connection.close() I got desired answer: It first do this Then this [(324986093, 391908893, 391908893, 1228, None, datetime.datetime(2023, 4, 24, 0, 0)] Finally do this

P.S.S. On top of that, If I may ask - imagine that we solve this mystery. Another problem in the same code, if i get an error while connecting to server using self.connection and say it gives an error then i passed this error to exit and exit gives me an error as well - how to handle this situation also. So i did not pass it to exit?

I tried to read documentations, but nothing seemed to work. I just curious what are the causes of omitting exit


Solution

  • __enter__ and __exit__ shouldn't have yield because then they'll return a generator and you have to iterate over the generator to completion for the method to finish executing.

    You may be getting confused with @contextlib.contextmanager which is a decorator that can be applied to a generator function to create a simple context manager without having to write a class with __enter__ and __exit__.