Skip to content

Instantly share code, notes, and snippets.

@tmr232
Created November 5, 2021 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmr232/203e6193b41f933f27f7bffad8a8f590 to your computer and use it in GitHub Desktop.
Save tmr232/203e6193b41f933f27f7bffad8a8f590 to your computer and use it in GitHub Desktop.
Class decorator for enforcing the use of `with` statement for an object
from typing import Type, ContextManager, TypeVar
T = TypeVar("T")
def context_only(cls: Type[T]) -> Type[ContextManager[T]]:
def _default_enter(self):
return self
def _default_exit(self, exc_type, exc_val, exc_tb):
return False
context_members = dict(cls.__dict__)
context_enter = context_members.pop("__enter__", _default_enter)
context_exit = context_members.pop("__exit__", _default_exit)
context_cls = type(cls.__name__, cls.__bases__, context_members)
class _ContextManager:
def __init__(self, *args, **kwargs):
self.__context: T = context_cls(*args, **kwargs)
def __enter__(self) -> T:
return context_enter(self.__context)
def __exit__(self, exc_type, exc_val, exc_tb):
return context_exit(self.__context, exc_type, exc_val, exc_tb)
return _ContextManager
@context_only
class Printer:
line: int
name: str
def __init__(self, name: str):
self.line = 1
self.name = name
def print(self, thing: str) -> None:
print(f"{self.name}:{self.line:4}: {thing}")
self.line += 1
def main():
with Printer("Log") as printer:
printer.print("Hello, World!")
printer.print("Goodbye, World.")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment