import asyncio class ContextPlayground: def __init__(self) -> None: self.keka = "Hi, i am keka" def __enter__(self): print("Entered context") return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: print(f"\tException raised. Type: {exc_type}") if exc_val: print(f"\tException raised. Value: {exc_val}") if exc_tb: print(f"\tException raised. Traceback: {exc_tb}") print("\tExiting context") # Returning True means that context manager will supress the exception and it will not be propagated outside # the with block. IOW it indicated, that exception is going to be handled inside context return True # Returning False means that the exception will be propagated and it will be raised outside the with block # IOW it means that the exception will not be handled inside context manager and must be handled by the caller # Note from documentation: __exit__() methods should not reraise the passed-in exception; # this is the caller’s responsibility. # return False # Or return None def main(): with ContextPlayground() as kek: print(f"\t\t{kek.keka}") print("\t\tInside context") print("Outside context") class AsyncContextPlayground: """Asyncio version of ContextPlayground""" def __init__(self) -> None: self.keka = "Hi, i am async Keka" async def __aenter__(self) -> None: print("\tEntered async context") return self async def __aexit__(self, exc_type, exc_val, exc_tb): if exc_type: print(f"\t\tException raised. Type: {exc_type}") if exc_val: print(f"\t\tException raised. Value: {exc_val}") if exc_tb: print(f"\t\tException raised. Traceback: {exc_tb}") print("\tExiting async context") return True async def async_main(): async with AsyncContextPlayground() as kek: print(f"\t\t{kek.keka}") print("\t\tInside async context") raise ValueError("An error occurred") # Uncomment to test exception handling print("Outside async context") if __name__ == "__main__": asyncio.run(async_main())