Author: Matti Picus <matti.pi...@gmail.com> Branch: meth_fastcall Changeset: r98304:10a5fb96ac84 Date: 2019-12-17 14:26 +0200 http://bitbucket.org/pypy/pypy/changeset/10a5fb96ac84/
Log: add python3.6 version of METH_FASTCALL, only becomes official in 3.8 diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -128,7 +128,7 @@ constant_names = """ Py_TPFLAGS_READY Py_TPFLAGS_READYING METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE Py_MAX_FMT -METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O +METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O METH_FASTCALL Py_TPFLAGS_HEAPTYPE Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_MAX_NDIMS Py_CLEANUP_SUPPORTED diff --git a/pypy/module/cpyext/include/methodobject.h b/pypy/module/cpyext/include/methodobject.h --- a/pypy/module/cpyext/include/methodobject.h +++ b/pypy/module/cpyext/include/methodobject.h @@ -27,6 +27,11 @@ #define METH_COEXIST 0x0040 +/* In python3.7 this is equivalent to METH_FASTCALL | METH_KEYWORDS, + and is used with _PyCFunctionFast which becomes in 3.7 + _PyCFunctionFastWithKeywords*/ +#define METH_FASTCALL 0x0080 + #define PyCFunction_New(ml, self) PyCFunction_NewEx((ml), (self), NULL) /* Macros for direct access to these values. Type checks are *not* diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -10,7 +10,7 @@ from pypy.objspace.std.typeobject import W_TypeObject from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, - METH_STATIC, METH_VARARGS, PyObject, bootstrap_function, + METH_STATIC, METH_VARARGS, METH_FASTCALL, PyObject, bootstrap_function, cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts, build_type_checkers) from pypy.module.cpyext.pyobject import ( @@ -21,6 +21,7 @@ PyMethodDef = cts.gettype('PyMethodDef') PyCFunction = cts.gettype('PyCFunction') PyCFunctionKwArgs = cts.gettype('PyCFunctionWithKeywords') +_PyCFunctionFast = cts.gettype('_PyCFunctionFast') PyCFunctionObject = cts.gettype('PyCFunctionObject*') @bootstrap_function @@ -57,6 +58,11 @@ space.setitem(w_kwargs, space.newtext(key), w_obj) return w_kwargs +def w_names_from_args(space, __args__): + if __args__.keywords is None: + return [] + return [space.newtext(x) for x in __args__.keywords] + def undotted_name(name): """Return the last component of a dotted name""" dotpos = name.rfind('.') @@ -106,12 +112,14 @@ def call(self, space, w_self, __args__): flags = self.flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST) length = len(__args__.arguments_w) - if not flags & METH_KEYWORDS and __args__.keywords: + if flags & METH_FASTCALL: + return self.call_fastcall(space, w_self, __args__) + elif flags & METH_KEYWORDS: + return self.call_keywords(space, w_self, __args__) + elif __args__.keywords: raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) - if flags & METH_KEYWORDS: - return self.call_keywords(space, w_self, __args__) - elif flags & METH_NOARGS: + if flags & METH_NOARGS: if length == 0: return self.call_noargs(space, w_self, __args__) raise oefmt(space.w_TypeError, @@ -154,6 +162,30 @@ finally: decref(space, py_args) + def call_fastcall(self, space, w_self, __args__): + func = rffi.cast(_PyCFunctionFast, self.ml.c_ml_meth) + args_w = __args__.arguments_w + names = w_names_from_args(space, __args__) + nargs = len(__args__.arguments_w) + with lltype.scoped_alloc(rffi.CArray(PyObject), nargs + len(names)) as args: + i = 0 + py_names = None + for w_arg in args_w: + args[i] = make_ref(space, w_arg) + i += 1 + if names: + for w_val in __args__.keywords_w: + args[i] = make_ref(space, w_val) + i += 1 + py_names = tuple_from_args_w(space, names) + try: + return generic_cpy_call(space, func, w_self, args, nargs, py_names) + finally: + for arg in args: + decref(space, arg) + if py_names: + decref(space, py_names) + def get_doc(self, space): c_doc = self.ml.c_ml_doc if c_doc: diff --git a/pypy/module/cpyext/parse/cpyext_object.h b/pypy/module/cpyext/parse/cpyext_object.h --- a/pypy/module/cpyext/parse/cpyext_object.h +++ b/pypy/module/cpyext/parse/cpyext_object.h @@ -176,6 +176,8 @@ /* from methodobject.h */ typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); +typedef PyObject *(*_PyCFunctionFast) (PyObject *self, PyObject **args, + Py_ssize_t nargs, PyObject *kwnames); typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*PyNoArgsFunction)(PyObject *); diff --git a/pypy/module/cpyext/test/test_methodobject.py b/pypy/module/cpyext/test/test_methodobject.py --- a/pypy/module/cpyext/test/test_methodobject.py +++ b/pypy/module/cpyext/test/test_methodobject.py @@ -89,6 +89,45 @@ assert mod.getarg_KW.__name__ == "getarg_KW" assert mod.getarg_KW(*(), **{}) == ((), {}) + def test_call_METH_FAST(self): + import sys + if sys.version_info[:2] != (3, 6): + skip('Python 3.6 only test, Python 3.7+ is different') + mod = self.import_extension('foo', [ + ('getarg_FAST', 'METH_FASTCALL', + ''' + int kwlen, i; + PyObject *pyargs; + if (kwnames == NULL) { + kwlen = 0; + } else { + kwlen = PySequence_Size(kwnames); + } + fprintf(stderr, "got %ld args, %d kwnames\\n", nargs, kwlen); + pyargs = PyTuple_New(nargs + kwlen); + for (i=0; i<nargs + kwlen; i++) { + if (args[i] == NULL) { + PyErr_Format(PyExc_ValueError, "bad val at %d", i); + return NULL; + } + PyTuple_SetItem(pyargs, i, args[i]); + fprintf(stderr, "arg %d refcnt %ld\\n", i, (args[i])->ob_refcnt); + } + if (kwnames == NULL) { + return Py_BuildValue("Oi", pyargs, nargs); + } else { + fprintf(stderr, "got kwnames\\n"); + return Py_BuildValue("OiO", pyargs, nargs, kwnames); + } + ''' + ), + ]) + assert mod.getarg_FAST(1) == ((1,), 1) + assert mod.getarg_FAST(1, 2) == ((1, 2), 2) + assert mod.getarg_FAST(a=3, b=4) == ((3, 4), 0, ('a', 'b')) + assert mod.getarg_FAST(1, 2, a=3, b=4) == ((1, 2, 3, 4), 2, ('a', 'b')) + assert mod.getarg_FAST.__name__ == "getarg_FAST" + assert mod.getarg_FAST(*(), **{}) == ((), 0) def test_func_attributes(self): mod = self.import_extension('MyModule', [ diff --git a/pypy/tool/cpyext/extbuild.py b/pypy/tool/cpyext/extbuild.py --- a/pypy/tool/cpyext/extbuild.py +++ b/pypy/tool/cpyext/extbuild.py @@ -118,7 +118,9 @@ codes = [] for funcname, flags, code in functions: cfuncname = "%s_%s" % (modname, funcname) - if 'METH_KEYWORDS' in flags: + if 'METH_FASTCALL' in flags: + signature = '(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)' + elif 'METH_KEYWORDS' in flags: signature = '(PyObject *self, PyObject *args, PyObject *kwargs)' else: signature = '(PyObject *self, PyObject *args)' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit