Commit b8b87e2c authored by Stefan Behnel's avatar Stefan Behnel

backport PyAsyncMethods and tp_as_async slot to all Py3.x versions (not only Py3.5+)

parent 8dbbb34b
...@@ -510,29 +510,6 @@ class BaseClassSlot(SlotDescriptor): ...@@ -510,29 +510,6 @@ class BaseClassSlot(SlotDescriptor):
base_type.typeptr_cname)) base_type.typeptr_cname))
class AlternativeSlot(SlotDescriptor):
"""Slot descriptor that delegates to different slots using C macros."""
def __init__(self, alternatives):
SlotDescriptor.__init__(self, "")
self.alternatives = alternatives
def generate(self, scope, code):
# state machine: "#if ... (#elif ...)* #else ... #endif"
test = 'if'
for guard, slot in self.alternatives:
if guard:
assert test in ('if', 'elif'), test
else:
assert test == 'elif', test
test = 'else'
code.putln("#%s %s" % (test, guard))
slot.generate(scope, code)
if test == 'if':
test = 'elif'
assert test == 'else', test
code.putln("#endif")
# The following dictionary maps __xxx__ method names to slot descriptors. # The following dictionary maps __xxx__ method names to slot descriptors.
method_name_to_slot = {} method_name_to_slot = {}
...@@ -794,11 +771,11 @@ slot_table = ( ...@@ -794,11 +771,11 @@ slot_table = (
EmptySlot("tp_print"), #MethodSlot(printfunc, "tp_print", "__print__"), EmptySlot("tp_print"), #MethodSlot(printfunc, "tp_print", "__print__"),
EmptySlot("tp_getattr"), EmptySlot("tp_getattr"),
EmptySlot("tp_setattr"), EmptySlot("tp_setattr"),
AlternativeSlot([
("PY_MAJOR_VERSION < 3", MethodSlot(cmpfunc, "tp_compare", "__cmp__")), # tp_compare (Py2) / tp_reserved (Py3<3.5) / tp_as_async (Py3.5+) is always used as tp_as_async in Py3
("PY_VERSION_HEX < 0x030500B1", EmptySlot("tp_reserved")), MethodSlot(cmpfunc, "tp_compare", "__cmp__", ifdef="PY_MAJOR_VERSION < 3"),
("", SuiteSlot(PyAsyncMethods, "PyAsyncMethods", "tp_as_async", ifdef="PY_VERSION_HEX >= 0x030500B1")), SuiteSlot(PyAsyncMethods, "__Pyx_PyAsyncMethodsStruct", "tp_as_async", ifdef="PY_MAJOR_VERSION >= 3"),
]),
MethodSlot(reprfunc, "tp_repr", "__repr__"), MethodSlot(reprfunc, "tp_repr", "__repr__"),
SuiteSlot(PyNumberMethods, "PyNumberMethods", "tp_as_number"), SuiteSlot(PyNumberMethods, "PyNumberMethods", "tp_as_number"),
......
...@@ -96,29 +96,33 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAwaitableIter(PyObject *o) { ...@@ -96,29 +96,33 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAwaitableIter(PyObject *o) {
// adapted from genobject.c in Py3.5 // adapted from genobject.c in Py3.5
static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *obj) { static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *obj) {
PyObject *res; PyObject *res;
#if PY_VERSION_HEX >= 0x030500B1 #if PY_MAJOR_VERSION >= 3
PyAsyncMethods* am = Py_TYPE(obj)->tp_as_async; __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
if (likely(am && am->am_await)) { if (likely(am && am->am_await)) {
res = (*am->am_await)(obj); res = (*am->am_await)(obj);
} else { } else
#endif
{
#if PY_VERSION_HEX >= 0x030500B1
// no slot => no method
goto slot_error; goto slot_error;
}
#else #else
PyObject *method = __Pyx_PyObject_GetAttrStr(obj, PYIDENT("__await__")); PyObject *method = __Pyx_PyObject_GetAttrStr(obj, PYIDENT("__await__"));
if (unlikely(!method)) goto slot_error; if (unlikely(!method)) goto slot_error;
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
if (likely(PyMethod_Check(method))) { if (likely(PyMethod_Check(method))) {
PyObject *self = PyMethod_GET_SELF(method); PyObject *self = PyMethod_GET_SELF(method);
if (likely(self)) { if (likely(self)) {
PyObject *function = PyMethod_GET_FUNCTION(method); PyObject *function = PyMethod_GET_FUNCTION(method);
res = __Pyx_PyObject_CallOneArg(function, self); res = __Pyx_PyObject_CallOneArg(function, self);
} else
res = __Pyx_PyObject_CallNoArg(method);
} else } else
#endif
res = __Pyx_PyObject_CallNoArg(method); res = __Pyx_PyObject_CallNoArg(method);
} else Py_DECREF(method);
#endif
res = __Pyx_PyObject_CallNoArg(method);
Py_DECREF(method);
#endif #endif
}
if (unlikely(!res)) goto bad; if (unlikely(!res)) goto bad;
if (!PyIter_Check(res)) { if (!PyIter_Check(res)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
...@@ -161,18 +165,21 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *o); /*pro ...@@ -161,18 +165,21 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *o); /*pro
//@requires: ObjectHandling.c::PyObjectCallMethod0 //@requires: ObjectHandling.c::PyObjectCallMethod0
static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) { static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) {
#if PY_VERSION_HEX >= 0x030500B1 #if PY_MAJOR_VERSION >= 3
PyAsyncMethods* am = Py_TYPE(obj)->tp_as_async; __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
if (likely(am && am->am_aiter)) { if (likely(am && am->am_aiter)) {
return (*am->am_aiter)(obj); return (*am->am_aiter)(obj);
} }
#else #endif
PyObject *iter = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__")); #if PY_VERSION_HEX < 0x030500B1
if (likely(iter)) {
return iter; PyObject *iter = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__"));
// FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__aiter__' if (likely(iter))
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return iter;
return NULL; // FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__aiter__'
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
return NULL;
}
#endif #endif
PyErr_Format(PyExc_TypeError, "'async for' requires an object with __aiter__ method, got %.100s", PyErr_Format(PyExc_TypeError, "'async for' requires an object with __aiter__ method, got %.100s",
...@@ -181,22 +188,23 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) { ...@@ -181,22 +188,23 @@ static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) {
} }
static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *obj) { static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *obj) {
#if PY_VERSION_HEX >= 0x030500B1 #if PY_MAJOR_VERSION >= 3
PyAsyncMethods* am = Py_TYPE(obj)->tp_as_async; __Pyx_PyAsyncMethodsStruct* am = __Pyx_PyType_AsAsync(obj);
if (likely(am && am->am_anext)) { if (likely(am && am->am_anext)) {
return (*am->am_anext)(obj); return (*am->am_anext)(obj);
} else { }
#else #endif
PyObject *value = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__anext__")); #if PY_VERSION_HEX < 0x030500B1
if (likely(value)) {
return value; PyObject *value = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__anext__"));
if (likely(value))
return value;
}
// FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__anext__' // FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__anext__'
if (PyErr_ExceptionMatches(PyExc_AttributeError)) { if (PyErr_ExceptionMatches(PyExc_AttributeError))
#endif #endif
PyErr_Format(PyExc_TypeError, "'async for' requires an object with __anext__ method, got %.100s", PyErr_Format(PyExc_TypeError, "'async for' requires an object with __anext__ method, got %.100s",
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);
}
return NULL; return NULL;
} }
...@@ -991,6 +999,20 @@ static void __Pyx_Coroutine_check_and_dealloc(PyObject *self) { ...@@ -991,6 +999,20 @@ static void __Pyx_Coroutine_check_and_dealloc(PyObject *self) {
__Pyx_Coroutine_dealloc(self); __Pyx_Coroutine_dealloc(self);
} }
#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
static PyObject *__Pyx_Coroutine_compare(PyObject *obj, PyObject *other, int op) {
PyObject* result;
switch (op) {
case Py_EQ: result = (other == obj) ? Py_True : Py_False; break;
case Py_NE: result = (other != obj) ? Py_True : Py_False; break;
default:
result = Py_NotImplemented;
}
Py_INCREF(result);
return result;
}
#endif
static PyObject *__Pyx_Coroutine_await(PyObject *self) { static PyObject *__Pyx_Coroutine_await(PyObject *self) {
Py_INCREF(self); Py_INCREF(self);
return self; return self;
...@@ -1006,8 +1028,8 @@ static PyMethodDef __pyx_Coroutine_methods[] = { ...@@ -1006,8 +1028,8 @@ static PyMethodDef __pyx_Coroutine_methods[] = {
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
#if PY_VERSION_HEX >= 0x030500B1 #if PY_MAJOR_VERSION >= 3
static PyAsyncMethods __pyx_Coroutine_as_async = { static __Pyx_PyAsyncMethodsStruct __pyx_Coroutine_as_async = {
__Pyx_Coroutine_await, /*am_await*/ __Pyx_Coroutine_await, /*am_await*/
0, /*am_aiter*/ 0, /*am_aiter*/
0, /*am_anext*/ 0, /*am_anext*/
...@@ -1023,10 +1045,10 @@ static PyTypeObject __pyx_CoroutineType_type = { ...@@ -1023,10 +1045,10 @@ static PyTypeObject __pyx_CoroutineType_type = {
0, /*tp_print*/ 0, /*tp_print*/
0, /*tp_getattr*/ 0, /*tp_getattr*/
0, /*tp_setattr*/ 0, /*tp_setattr*/
#if PY_VERSION_HEX >= 0x030500B1 #if PY_MAJOR_VERSION >= 3
&__pyx_Coroutine_as_async, /*tp_as_async*/ &__pyx_Coroutine_as_async, /*tp_as_async (tp_reserved)*/
#else #else
0, /*tp_reserved resp. tp_compare*/ 0, /*tp_reserved*/
#endif #endif
0, /*tp_repr*/ 0, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
...@@ -1042,7 +1064,12 @@ static PyTypeObject __pyx_CoroutineType_type = { ...@@ -1042,7 +1064,12 @@ static PyTypeObject __pyx_CoroutineType_type = {
0, /*tp_doc*/ 0, /*tp_doc*/
(traverseproc) __Pyx_Coroutine_traverse, /*tp_traverse*/ (traverseproc) __Pyx_Coroutine_traverse, /*tp_traverse*/
0, /*tp_clear*/ 0, /*tp_clear*/
#if CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
// in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
__Pyx_Coroutine_compare, /*tp_richcompare*/
#else
0, /*tp_richcompare*/ 0, /*tp_richcompare*/
#endif
offsetof(__pyx_CoroutineObject, gi_weakreflist), /*tp_weaklistoffset*/ offsetof(__pyx_CoroutineObject, gi_weakreflist), /*tp_weaklistoffset*/
// no tp_iter() as iterator is only available through __await__() // no tp_iter() as iterator is only available through __await__()
0, /*tp_iter*/ 0, /*tp_iter*/
......
...@@ -184,6 +184,22 @@ ...@@ -184,6 +184,22 @@
#define __Pyx_PyMethod_New(func, self, klass) PyMethod_New(func, self, klass) #define __Pyx_PyMethod_New(func, self, klass) PyMethod_New(func, self, klass)
#endif #endif
// backport of PyAsyncMethods from Py3.5 to older Py3.x versions
// (mis-)using the "tp_reserved" type slot which is re-activated as "tp_as_async" in Py3.5
#if PY_VERSION_HEX >= 0x030500B1
#define __Pyx_PyAsyncMethodsStruct PyAsyncMethods
#define __Pyx_PyType_AsAsync(obj) (Py_TYPE(obj)->tp_as_async)
#elif PY_MAJOR_VERSION >= 3
typedef struct {
unaryfunc am_await;
unaryfunc am_aiter;
unaryfunc am_anext;
} __Pyx_PyAsyncMethodsStruct;
#define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved))
#else
#define __Pyx_PyType_AsAsync(obj) NULL
#endif
/* inline attribute */ /* inline attribute */
#ifndef CYTHON_INLINE #ifndef CYTHON_INLINE
#if defined(__GNUC__) #if defined(__GNUC__)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment