Вы близки, однако вы можете просто вызвать ошибку, если contextmanager уже был вызван, после установки флага с помощью декоратора. Приведенный ниже код написан так, что и enter
, и __enter__
могут возвращать отдельные переменные (True
и self
), как предполагалось изначально и в соответствии с общей идеей управления контекстом:
def control_manager(f):
def wrapper(cls):
if getattr(cls, 'flag'):
raise Exception("Already expended the context manager")
setattr(cls, 'flag', True)
return f(cls)
return wrapper
class Context:
def __init__(self):
self.flag = False
@control_manager
def __enter__(self):
return self
def __exit__(self, *args):
pass
@control_manager
def enter(self):
return True
def exit(self):
#do something
pass
with Context() as f:
v = f.enter()
Traceback (последний вызов был последним):
Файл "", строка 2, в
Файл "", строка 4, в обёртке
Исключение: уже израсходован менеджер контекста
Однако, это будет работать на втором тесте:
c = Context()
v = c.enter()
c.exit()
Однако, если вы хотите использовать enter
в качестве основного блока для менеджера контекста в классе, вы можете рассматривать его как classmethod
с contextlib.contextmanager
:
import contextlib
class Control:
flag = False
def __init__(self):
pass
def __enter__(self):
with Control.enter() as f:
v = f
return self
def __exit__(self, *args):
pass
@classmethod
@control_manager
@contextlib.contextmanager
def enter(cls):
yield True
with Control() as f:
pass
#runs without exception
with Control.enter() as t:
pass
Traceback (последний вызов был последним):
Файл "", строка 1, в
Файл "", строка 4, в обёртке
Исключение: уже израсходован менеджер контекста