I would like to ensure that the class is only instantiated within a "with" statement.
i.e. this one is ok:
with X() as x:
...
and this is not:
x = X()
How can I ensure such functionality?
I would like to ensure that the class is only instantiated within a "with" statement.
i.e. this one is ok:
with X() as x:
...
and this is not:
x = X()
How can I ensure such functionality?
Unfortunately, you can't very cleanly.
Context managers require having
__enter__
and__exit__
methods, so you can use this to assign a member variable on the class to check in your code.The stateful object approach has the nice added benefit of being able to tell if the
__exit__
method was called later, or to cleanly handle method requirements in later calls:Here is a decorator that automates making sure methods aren't called outside of a context manager:
And here is an example of it in use:
This was written as an answer to this duplicate question.
There is no straight forward way, as far as I know. But, you can have a boolean flag, to check if
__enter__
was invoked, before the actual methods in the objects were called.When you use it with
with
,you will get
But, when you manually create an object, and invoke
do_something
,you will get
Note: This is not a solid solution. Somebody can directly invoke
__enter__
method alone, before calling any other methods and the__exit__
method may never be called in that case.If you don't want to repeat that check in every function, you can make it a decorator, like this
All answers so far do not provide what (I think) OP wants directly.
(I think) OP wants something like this:
This is what I come up with, it may not be very robust, but I think it's closest to OP's intention.
This will give the exact same output as I showed above as long as
with
is in the same line withX()
when using context manager.There is no foolproof approach to ensure that an instance is constructed within a
with
clause, but you can create an instance in the__enter__
method and return that instead ofself
; this is the value that will be assigned intox
. Thus you can considerX
as a factory that creates the actual instance in its__enter__
method, something like:Of course this is still reusable, but every
with
statement would create a new instance.Naturally one can call the
__enter__
manually, or get a reference to theActualInstanceClass
but it would be more of abuse instead of use.For an even smellier approach, the
X()
when called does actually create aXFactory
instance, instead of anX
instance; and this in turn when used as a context manager, creates theActualX
instance which is the subclass ofX
, thusisinstance(x, X)
will return true.You could take this further and have
XFactory
create an actualX
instance with a special keyword argument to__new__
, but I consider it to be too black magic to be useful.