[pypy-commit] pypy meth_fastcall: add python3.6 version of METH_FASTCALL, only becomes official in 3.8
mattip
pypy.commits at gmail.com
Tue Dec 17 07:27:20 EST 2019
Author: Matti Picus <matti.picus at 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)'
More information about the pypy-commit
mailing list