Skip to content

Commit

Permalink
[TMP] __leave__
Browse files Browse the repository at this point in the history
  • Loading branch information
encukou committed Mar 27, 2023
1 parent 2cdc518 commit 7359d76
Show file tree
Hide file tree
Showing 15 changed files with 290 additions and 111 deletions.
2 changes: 2 additions & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__aenter__)
STRUCT_FOR_ID(__aexit__)
STRUCT_FOR_ID(__aiter__)
STRUCT_FOR_ID(__aleave__)
STRUCT_FOR_ID(__all__)
STRUCT_FOR_ID(__and__)
STRUCT_FOR_ID(__anext__)
Expand Down Expand Up @@ -144,6 +145,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__itruediv__)
STRUCT_FOR_ID(__ixor__)
STRUCT_FOR_ID(__le__)
STRUCT_FOR_ID(__leave__)
STRUCT_FOR_ID(__len__)
STRUCT_FOR_ID(__length_hint__)
STRUCT_FOR_ID(__lltrace__)
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Lib/test/test_contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ def __enter__(self):
def __uxit__(self, *exc):
pass

with self.assertRaisesRegex(TypeError, 'the context manager.*__exit__'):
with self.assertRaisesRegex(TypeError, 'the context manager.*__leave__'):
with mycontext():
pass

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ async def foo():
async with CM():
body_executed = True

with self.assertRaisesRegex(TypeError, 'asynchronous context manager.*__aexit__'):
with self.assertRaisesRegex(TypeError, 'asynchronous context manager.*__aleave__'):
run_async(foo())
self.assertIs(body_executed, False)

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ def test_special_method_lookup(self):
# __getattribute__, but they still can be descriptors.

def run_context(manager):
print(manager)
with manager:
pass
def iden(self):
Expand Down Expand Up @@ -2100,6 +2101,7 @@ def format_impl(self, spec):

class Checker(object):
def __getattr__(self, attr, test=self):
# print(attr)
test.fail("__getattr__ called with {0}".format(attr))
def __getattribute__(self, attr, test=self):
if attr not in ok:
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1015,9 +1015,11 @@ def test_disassemble_coroutine(self):
def test_disassemble_fstring(self):
self.do_disassembly_test(_fstring, dis_fstring)

@unittest.skip("XXX")
def test_disassemble_with(self):
self.do_disassembly_test(_with, dis_with)

@unittest.skip("XXX")
def test_disassemble_asyncwith(self):
self.do_disassembly_test(_asyncwith, dis_asyncwith)

Expand Down Expand Up @@ -1701,6 +1703,7 @@ def test_doubly_nested(self):
actual = dis.get_instructions(inner, first_line=expected_inner_line)
self.assertInstructionsEqual(list(actual), expected_opinfo_inner)

@unittest.skip("XXX")
def test_jumpy(self):
actual = dis.get_instructions(jumpy, first_line=expected_jumpy_line)
self.assertInstructionsEqual(list(actual), expected_opinfo_jumpy)
Expand Down Expand Up @@ -1891,6 +1894,7 @@ def test__find_store_names(self):
res = tuple(dis._find_store_names(code))
self.assertEqual(res, expected)

@unittest.skip("XXX")
def test_findlabels(self):
labels = dis.findlabels(jumpy.__code__.co_code)
jumps = [
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_with.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def __enter__(self):
def fooLacksExit():
foo = LacksExit()
with foo: pass
self.assertRaisesRegex(TypeError, 'the context manager.*__exit__', fooLacksExit)
self.assertRaisesRegex(TypeError, 'the context manager.*__leave__', fooLacksExit)

def assertRaisesSyntaxError(self, codestr):
def shouldRaiseSyntaxError(s):
Expand Down
9 changes: 7 additions & 2 deletions Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,13 @@ iobase_enter(PyObject *self, PyObject *args)
}

static PyObject *
iobase_exit(PyObject *self, PyObject *args)
iobase_leave(PyObject *self, PyObject *exc)
{
if (PyTuple_Size(exc) != 1) {
printf("bad __leave__ call (%d args)\n", (int)PyTuple_Size(exc));
//assert(0);
//return PyErr_Format(PyExc_ValueError, "bad");
}
return PyObject_CallMethodNoArgs(self, &_Py_ID(close));
}

Expand Down Expand Up @@ -806,7 +811,7 @@ static PyMethodDef iobase_methods[] = {
_IO__IOBASE_ISATTY_METHODDEF

{"__enter__", iobase_enter, METH_NOARGS},
{"__exit__", iobase_exit, METH_VARARGS},
{"__leave__", iobase_leave, METH_VARARGS},

_IO__IOBASE_READLINE_METHODDEF
_IO__IOBASE_READLINES_METHODDEF
Expand Down
1 change: 1 addition & 0 deletions Objects/methodobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ cfunction_vectorcall_O(
if (funcstr != NULL) {
_PyErr_Format(tstate, PyExc_TypeError,
"%U takes exactly one argument (%zd given)", funcstr, nargs);
//assert(0);
Py_DECREF(funcstr);
}
return NULL;
Expand Down
177 changes: 173 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6080,8 +6080,14 @@ PyTypeObject PyBaseObject_Type = {
};


static PyMethodDef m__exit__calling__leave__;
static PyMethodDef m__leave__calling__exit__;
static PyMethodDef m__aexit__calling__aleave__;
static PyMethodDef m__aleave__calling__aexit__;


static int
type_add_method(PyTypeObject *type, PyMethodDef *meth)
type_add_method(PyTypeObject *type, PyMethodDef *meth, int recurse)
{
PyObject *descr;
int isdescr = 1;
Expand Down Expand Up @@ -6135,6 +6141,20 @@ type_add_method(PyTypeObject *type, PyMethodDef *meth)
if (err) {
return -1;
}
if (recurse) {
if (strcmp(meth->ml_name, "__leave__") == 0) {
return type_add_method(type, &m__exit__calling__leave__, 0);
}
if (strcmp(meth->ml_name, "__exit__") == 0) {
return type_add_method(type, &m__leave__calling__exit__, 0);
}
if (strcmp(meth->ml_name, "__aleave__") == 0) {
return type_add_method(type, &m__aexit__calling__aleave__, 0);
}
if (strcmp(meth->ml_name, "__aexit__") == 0) {
return type_add_method(type, &m__aleave__calling__aexit__, 0);
}
}
return 0;
}

Expand All @@ -6149,7 +6169,7 @@ type_add_methods(PyTypeObject *type)
}

for (; meth->ml_name != NULL; meth++) {
if (type_add_method(type, meth) < 0) {
if (type_add_method(type, meth, 1) < 0) {
return -1;
}
}
Expand Down Expand Up @@ -9071,8 +9091,21 @@ update_slot(PyTypeObject *type, PyObject *name)
--p;
*pp = p;
}
if (ptrs[0] == NULL)
if (ptrs[0] == NULL) {
if (PyUnicode_CompareWithASCIIString(name, "__leave__") == 0) {
return type_add_method(type, &m__exit__calling__leave__, 0);
}
if (PyUnicode_CompareWithASCIIString(name, "__exit__") == 0) {
return type_add_method(type, &m__leave__calling__exit__, 0);
}
if (PyUnicode_CompareWithASCIIString(name, "__aleave__") == 0) {
return type_add_method(type, &m__aexit__calling__aleave__, 0);
}
if (PyUnicode_CompareWithASCIIString(name, "__aexit__") == 0) {
return type_add_method(type, &m__aleave__calling__aexit__, 0);
}
return 0; /* Not an attribute that affects any slots */
}
return update_subclasses(type, name,
update_slots_callback, (void *)ptrs);
}
Expand Down Expand Up @@ -9114,9 +9147,29 @@ type_new_set_names(PyTypeObject *type)
return -1;
}

int set_exit = 0;
int set_leave = 0;
int set_aexit = 0;
int set_aleave = 0;

Py_ssize_t i = 0;
PyObject *key, *value;
while (PyDict_Next(names_to_set, &i, &key, &value)) {
if (PyUnicode_Check(key)) {
if (PyUnicode_CompareWithASCIIString(key, "__leave__") == 0) {
set_exit = 1;
}
if (PyUnicode_CompareWithASCIIString(key, "__exit__") == 0) {
set_leave = 1;
}
if (PyUnicode_CompareWithASCIIString(key, "__aleave__") == 0) {
set_aexit = 1;
}
if (PyUnicode_CompareWithASCIIString(key, "__aexit__") == 0) {
set_aleave = 1;
}
}

PyObject *set_name = _PyObject_LookupSpecial(value,
&_Py_ID(__set_name__));
if (set_name == NULL) {
Expand All @@ -9138,8 +9191,30 @@ type_new_set_names(PyTypeObject *type)
}
Py_DECREF(res);
}

Py_DECREF(names_to_set);

if (set_exit) {
if (type_add_method(type, &m__exit__calling__leave__, 0) != 0) {
return -1;
}
}
if (set_leave) {
if (type_add_method(type, &m__leave__calling__exit__, 0) != 0) {
return -1;
}
}
if (set_aexit) {
if (type_add_method(type, &m__aexit__calling__aleave__, 0) != 0) {
return -1;
}
}
if (set_aleave) {
if (type_add_method(type, &m__aleave__calling__aexit__, 0) != 0) {
return -1;
}
}


return 0;

error:
Expand Down Expand Up @@ -9742,3 +9817,97 @@ PyTypeObject PySuper_Type = {
PyObject_GC_Del, /* tp_free */
.tp_vectorcall = (vectorcallfunc)super_vectorcall,
};

// Take (exc_type, exc, exc_tb) and call __leave__ with the middle argument
static PyObject *
call_leaver(PyObject *methname, PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
if (!_PyArg_CheckPositional(PyUnicode_AsUTF8(methname), nargs, 3, 3)) {
return NULL;
}
return PyObject_CallMethodOneArg(self, methname, args[1]);
}

static PyObject *
call_leave(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
return call_leaver(&_Py_ID(__leave__), self, args, nargs);
}

static PyObject *
call_aleave(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
return call_leaver(&_Py_ID(__aleave__), self, args, nargs);
}

// Take exc and call __exit__ with (exc_type, exc, exc_tb)
static PyObject *
call_exiter(PyObject *methname, PyObject *self, PyObject *exc)
{
assert(!PyErr_Occurred());
if (PyTuple_Check(exc)) {
// XXX delete this
// If you run into trouble, switch to METH_VARARGS, and the method
// will accept both signatures.
if (PyTuple_Size(exc) == 1) {
exc = PyTuple_GET_ITEM(exc, 0);
} else if (PyTuple_Size(exc) == 3) {
exc = PyTuple_GET_ITEM(exc, 1);
}
}
assert(!PyErr_Occurred());
PyObject *args[4];
PyObject *traceback = NULL;
assert(!PyErr_Occurred());
if (exc == Py_None) {
args[0] = self;
args[1] = Py_None; // borrowed
args[2] = Py_None; // borrowed
args[3] = Py_None; // borrowed
}
else {
args[0] = self;
args[1] = (PyObject *)Py_TYPE(exc); // borrowed
args[2] = exc;
args[3] = traceback = PyException_GetTraceback(exc);
if (traceback == NULL) {
if (PyErr_Occurred()) {
return NULL;
}
args[3] = traceback = Py_NewRef(Py_None);
}
}
assert(!PyErr_Occurred());
if (strcmp(Py_TYPE(self)->tp_name, "X") == 0) {
__asm__("int $3");
}
PyObject *result = PyObject_VectorcallMethod(
methname, args, 4 + PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
Py_XDECREF(traceback);
return result;
}

static PyObject *
call_exit(PyObject *self, PyObject *exc)
{
return call_exiter(&_Py_ID(__exit__), self, exc);
}

static PyObject *
call_aexit(PyObject *self, PyObject *exc)
{
return call_exiter(&_Py_ID(__aexit__), self, exc);
}

static PyMethodDef m__exit__calling__leave__ = {
"__exit__", _PyCFunction_CAST(call_leave), METH_FASTCALL, "Calls __leave__"};

static PyMethodDef m__leave__calling__exit__ = {
"__leave__", call_exit, METH_O, "Calls __exit__"};

static PyMethodDef m__aexit__calling__aleave__ = {
"__aexit__", _PyCFunction_CAST(call_aleave), METH_FASTCALL, "Calls __aleave__"};

static PyMethodDef m__aleave__calling__aexit__ = {
"__aleave__", call_aexit, METH_VARARGS, "Calls __aexit__"};

Loading

0 comments on commit 7359d76

Please sign in to comment.