Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to access enclosing context manager?

There are essentially three ways to use the with statement:

Use an existing context manager:

with manager:
    pass

Create a context manager and bind its result to a variable:

with Manager() as result:
    pass

Create an context manager and discard its return value:

with Manager():
    pass

If we have place a function get_manager() inside the three with blocks above, is there any implementation that can return the enclosing context manager, or at least their __exit__ function?

It's obviously easy in the first case, but I can't think of a way to make it work in the other two. I doubt it's possible to get the entire context manager, since the value stack is popped immediately after the SETUP_WITH opcode. However, since the __exit__ function is stored on the block stack by SETUP_WITH, is there some way to access it?

like image 931
user35147863 Avatar asked Dec 24 '13 21:12

user35147863


People also ask

Can context managers be used outside the with statement?

Yes, the context manager will be available outside the with statement and that is not implementation or version dependent. with statements do not create a new execution scope.

Can we create a function to be used as a context manager?

You can also create custom function-based context managers using the contextlib. contextmanager decorator from the standard library and an appropriately coded generator function.

What does the context manager do when you are opening a file using with it closes the opened file automatically?

A context manager usually takes care of setting up some resource, e.g. opening a connection, and automatically handles the clean up when we are done with it. Probably, the most common use case is opening a file. The code above will open the file and will keep it open until we are out of the with statement.

What actions are guaranteed by a context manager?

Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you'd like to execute as a pair, with a block of code in between.


1 Answers

Unfortunately, as discussed in the comments, this is not possible in all cases. When a context manager is created, the following code is run (in cPython 2.7, at least. I can't comment on other implementations):

    case SETUP_WITH:
    {
        static PyObject *exit, *enter;
        w = TOP();
        x = special_lookup(w, "__exit__", &exit);
        if (!x)
            break;
        SET_TOP(x);
        /* more code follows... */
    }

The __exit__ method is pushed onto a stack with the SET_TOP macro, which is defined as:

#define SET_TOP(v)        (stack_pointer[-1] = (v))

The stack pointer, in turn, is set to the top of the frame's value stack at the start of frame eval:

stack_pointer = f->f_stacktop;

Where f is a frame object defined in frameobject.h. Unfortunately for us, this is where the trail stops. The python accessible frame object is defined with the following methods only:

static PyMemberDef frame_memberlist[] = {
    {"f_back",          T_OBJECT,       OFF(f_back),    RO},
    {"f_code",          T_OBJECT,       OFF(f_code),    RO},
    {"f_builtins",      T_OBJECT,       OFF(f_builtins),RO},
    {"f_globals",       T_OBJECT,       OFF(f_globals), RO},
    {"f_lasti",         T_INT,          OFF(f_lasti),   RO},
    {NULL}      /* Sentinel */
};

Which, unfortunaltey, does not include the f_valuestack that we would need. This makes sense, since f_valuestack is of the type PyObject **, which would need to be wrapped in an object to be accessible from python any way.

TL;DR: The __exit__ method we're looking for is only located in one place, the value stack of a frame object, and cPython doesn't make the value stack accessible to python code.

like image 104
user35147863 Avatar answered Oct 13 '22 00:10

user35147863