[pypy-commit] pypy default: Merge the first part of the cpyext-fast-typecheck branch.
antocuni
pypy.commits at gmail.com
Sat Mar 24 06:06:13 EDT 2018
Author: Antonio Cuni <anto.cuni at gmail.com>
Branch:
Changeset: r94126:66208269e66e
Date: 2018-03-24 10:47 +0100
http://bitbucket.org/pypy/pypy/changeset/66208269e66e/
Log: Merge the first part of the cpyext-fast-typecheck branch.
This branch introduce a way to implement Py*_Check efficiently as C
macros which simply checks a flag on the C typeobject, instead of
doing a full roundtrip to RPython land to do a space.isinstance_w().
For now, implement PyFloat_Check, PyBool_Check and PySlice_Check
using the new technique. Eventually, we should do the same for all
type checkers and possibly kill build_type_checkers().
This commits merges only part of the branch because later commits
have diverged from the original scope and implements a different
feature (bad me); they will be merged in a separate commit, to keep
the diff clear.
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
@@ -133,6 +133,11 @@
'TYPE', 'STRING'): # 'STRING' -> 'BYTES' in py3
constant_names.append('Py_TPFLAGS_%s_SUBCLASS' % name)
+# PyPy-specific flags
+for name in ('FLOAT',):
+ constant_names.append('Py_TPPYPYFLAGS_%s_SUBCLASS' % name)
+
+
for name in constant_names:
setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
globals().update(rffi_platform.configure(CConfig_constants))
diff --git a/pypy/module/cpyext/boolobject.py b/pypy/module/cpyext/boolobject.py
--- a/pypy/module/cpyext/boolobject.py
+++ b/pypy/module/cpyext/boolobject.py
@@ -1,9 +1,5 @@
-from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import (cpython_api, PyObject, CANNOT_FAIL,
- build_type_checkers)
-
-# Inheriting from bool isn't actually possible.
-PyBool_Check = build_type_checkers("Bool")[1]
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.api import cpython_api, PyObject
@cpython_api([rffi.LONG], PyObject)
def PyBool_FromLong(space, value):
diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py
--- a/pypy/module/cpyext/floatobject.py
+++ b/pypy/module/cpyext/floatobject.py
@@ -1,7 +1,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (PyObjectFields, bootstrap_function,
cpython_struct,
- CANNOT_FAIL, cpython_api, PyObject, build_type_checkers, CONST_STRING)
+ CANNOT_FAIL, cpython_api, PyObject, CONST_STRING)
from pypy.module.cpyext.pyobject import (
make_typedescr, track_reference, from_ref)
from pypy.interpreter.error import OperationError
@@ -38,8 +38,6 @@
track_reference(space, obj, w_obj)
return w_obj
-PyFloat_Check, PyFloat_CheckExact = build_type_checkers("Float")
-
@cpython_api([lltype.Float], PyObject)
def PyFloat_FromDouble(space, value):
return space.newfloat(value)
diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h
--- a/pypy/module/cpyext/include/boolobject.h
+++ b/pypy/module/cpyext/include/boolobject.h
@@ -16,6 +16,8 @@
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#define PyBool_Check(op) ((op)->ob_type == &PyBool_Type)
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/include/floatobject.h b/pypy/module/cpyext/include/floatobject.h
--- a/pypy/module/cpyext/include/floatobject.h
+++ b/pypy/module/cpyext/include/floatobject.h
@@ -32,6 +32,11 @@
return PyFloat_FromDouble(-Py_HUGE_VAL); \
} while(0)
+#define PyFloat_Check(op) \
+ _PyPy_Type_FastSubclass((op)->ob_type, Py_TPPYPYFLAGS_FLOAT_SUBCLASS)
+#define PyFloat_CheckExact(op) ((op)->ob_type == &PyFloat_Type)
+
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -228,6 +228,11 @@
#define Py_TPFLAGS_BASE_EXC_SUBCLASS (1L<<30)
#define Py_TPFLAGS_TYPE_SUBCLASS (1L<<31)
+/* These are conceptually the same as the flags above, but they are
+ PyPy-specific and are stored inside tp_pypy_flags */
+#define Py_TPPYPYFLAGS_FLOAT_SUBCLASS (1L<<0)
+
+
#define Py_TPFLAGS_DEFAULT_EXTERNAL ( \
Py_TPFLAGS_HAVE_GETCHARBUFFER | \
Py_TPFLAGS_HAVE_SEQUENCE_IN | \
@@ -247,6 +252,8 @@
#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)
#define PyType_FastSubclass(t,f) PyType_HasFeature(t,f)
+#define _PyPy_Type_FastSubclass(t,f) (((t)->tp_pypy_flags & (f)) != 0)
+
#define PyType_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)
diff --git a/pypy/module/cpyext/include/sliceobject.h b/pypy/module/cpyext/include/sliceobject.h
--- a/pypy/module/cpyext/include/sliceobject.h
+++ b/pypy/module/cpyext/include/sliceobject.h
@@ -17,6 +17,8 @@
PyObject *step;
} PySliceObject;
+#define PySlice_Check(op) ((op)->ob_type == &PySlice_Type)
+
#ifdef __cplusplus
}
#endif
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
@@ -311,6 +311,10 @@
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
+ /* PyPy specific extra fields: make sure that they are ALWAYS at the end,
+ for compatibility with CPython */
+ long tp_pypy_flags;
+
} PyTypeObject;
typedef struct _heaptypeobject {
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -47,7 +47,6 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
-PySlice_Check, PySlice_CheckExact = build_type_checkers("Slice")
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PySlice_New(space, w_start, w_stop, w_step):
@@ -75,9 +74,8 @@
normal slices.
Returns 0 on success and -1 on error with exception set."""
- if not PySlice_Check(space, w_slice):
+ if not isinstance(w_slice, W_SliceObject):
PyErr_BadInternalCall(space)
- assert isinstance(w_slice, W_SliceObject)
start_p[0], stop_p[0], step_p[0], slicelength_p[0] = \
w_slice.indices4(space, length)
return 0
@@ -97,9 +95,8 @@
objects in versions of Python prior to 2.3, you would probably do well to
incorporate the source of PySlice_GetIndicesEx(), suitably renamed,
in the source of your extension."""
- if not PySlice_Check(space, w_slice):
+ if not isinstance(w_slice, W_SliceObject):
PyErr_BadInternalCall(space)
- assert isinstance(w_slice, W_SliceObject)
start_p[0], stop_p[0], step_p[0] = \
w_slice.indices3(space, length)
return 0
diff --git a/pypy/module/cpyext/test/test_boolobject.py b/pypy/module/cpyext/test/test_boolobject.py
--- a/pypy/module/cpyext/test/test_boolobject.py
+++ b/pypy/module/cpyext/test/test_boolobject.py
@@ -1,7 +1,6 @@
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.boolobject import PyBool_Check, PyBool_FromLong
-from pypy.module.cpyext.floatobject import PyFloat_FromDouble
+from pypy.module.cpyext.boolobject import PyBool_FromLong
class TestBoolObject(BaseApiTest):
def test_fromlong(self, space):
@@ -12,12 +11,6 @@
else:
assert obj is space.w_False
- def test_check(self, space):
- assert PyBool_Check(space, space.w_True)
- assert PyBool_Check(space, space.w_False)
- assert not PyBool_Check(space, space.w_None)
- assert not PyBool_Check(space, PyFloat_FromDouble(space, 1.0))
-
class AppTestBoolMacros(AppTestCpythonExtensionBase):
def test_macros(self):
module = self.import_extension('foo', [
@@ -42,4 +35,14 @@
assert module.to_int(False) == 0
assert module.to_int(True) == 1
-
+ def test_check(self):
+ module = self.import_extension('foo', [
+ ("type_check", "METH_O",
+ '''
+ return PyLong_FromLong(PyBool_Check(args));
+ ''')])
+ assert module.type_check(True)
+ assert module.type_check(False)
+ assert not module.type_check(None)
+ assert not module.type_check(1.0)
+
diff --git a/pypy/module/cpyext/test/test_floatobject.py b/pypy/module/cpyext/test/test_floatobject.py
--- a/pypy/module/cpyext/test/test_floatobject.py
+++ b/pypy/module/cpyext/test/test_floatobject.py
@@ -102,9 +102,11 @@
"""
PyObject* pyobj = PyFloat_FromDouble(1.0);
PyFloatObject* pfo = (PyFloatObject*)pyobj;
- int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) &&
- PyFloat_Check(pfo) && PyFloat_CheckExact(pfo);
+ int res = (PyFloat_Check(pyobj) +
+ PyFloat_CheckExact(pyobj) * 10 +
+ PyFloat_Check(pfo) * 100 +
+ PyFloat_CheckExact(pfo) * 1000);
Py_DecRef(pyobj);
return PyLong_FromLong(res);"""),
])
- assert module.test() == 1
+ assert module.test() == 1111
diff --git a/pypy/module/cpyext/test/test_number.py b/pypy/module/cpyext/test/test_number.py
--- a/pypy/module/cpyext/test/test_number.py
+++ b/pypy/module/cpyext/test/test_number.py
@@ -11,7 +11,6 @@
PyNumber_Index, PyNumber_Coerce, PyNumber_CoerceEx, PyNumber_Add,
PyNumber_Multiply, PyNumber_InPlaceMultiply, PyNumber_Absolute,
PyNumber_Power, PyNumber_InPlacePower)
-from pypy.module.cpyext.floatobject import PyFloat_Check
from pypy.module.cpyext.intobject import PyInt_CheckExact
from pypy.module.cpyext.longobject import PyLong_CheckExact
from pypy.module.cpyext.object import PyObject_Size
@@ -86,7 +85,7 @@
w_res = from_ref(space, ppl[0])
- assert PyFloat_Check(space, w_res)
+ assert space.isinstance_w(w_res, space.w_float)
assert space.unwrap(w_res) == 123.
decref(space, pl)
decref(space, pf)
diff --git a/pypy/module/cpyext/test/test_sliceobject.py b/pypy/module/cpyext/test/test_sliceobject.py
--- a/pypy/module/cpyext/test/test_sliceobject.py
+++ b/pypy/module/cpyext/test/test_sliceobject.py
@@ -2,14 +2,8 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.api import Py_ssize_t, Py_ssize_tP
-from pypy.module.cpyext.sliceobject import PySlice_Check
class TestSliceObject(BaseApiTest):
- def test_slice(self, space):
- w_i = space.wrap(10)
- w_slice = space.newslice(w_i, w_i, w_i)
- assert PySlice_Check(space, w_slice)
- assert not PySlice_Check(space, w_i)
def test_GetIndicesEx(self, space, api):
w = space.wrap
@@ -79,3 +73,14 @@
"""),
])
assert module.get_ellipsis() is Ellipsis
+
+ def test_typecheck(self):
+ module = self.import_extension('foo', [
+ ("check", "METH_O",
+ """
+ PySliceObject *slice = (PySliceObject *)args;
+ return PyLong_FromLong(PySlice_Check(slice));
+ """),
+ ])
+ s = slice(10, 20, 30)
+ assert module.check(s)
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -1537,4 +1537,29 @@
pass
assert module.test_flags(MyList, Py_TPFLAGS_LIST_SUBCLASS) == 0
+ def test_has_pypy_subclass_flag(self):
+ module = self.import_extension('foo', [
+ ("test_pypy_flags", "METH_VARARGS",
+ '''
+ long long in_flag, my_flag;
+ PyObject * obj;
+ if (!PyArg_ParseTuple(args, "OL", &obj, &in_flag))
+ return NULL;
+ if (!PyType_Check(obj))
+ {
+ PyErr_SetString(PyExc_ValueError, "input must be type");
+ return NULL;
+ }
+ my_flag = ((PyTypeObject*)obj)->tp_pypy_flags;
+ if ((my_flag & in_flag) != in_flag)
+ return PyLong_FromLong(-1);
+ return PyLong_FromLong(0);
+ '''),])
+ # copied from object.h
+ Py_TPPYPYFLAGS_FLOAT_SUBCLASS = (1L<<0)
+ class MyFloat(float):
+ pass
+ assert module.test_pypy_flags(float, Py_TPPYPYFLAGS_FLOAT_SUBCLASS) == 0
+ assert module.test_pypy_flags(MyFloat, Py_TPPYPYFLAGS_FLOAT_SUBCLASS) == 0
+
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -22,6 +22,7 @@
Py_TPFLAGS_DICT_SUBCLASS, Py_TPFLAGS_BASE_EXC_SUBCLASS,
Py_TPFLAGS_TYPE_SUBCLASS,
Py_TPFLAGS_INT_SUBCLASS, Py_TPFLAGS_STRING_SUBCLASS, # change on py3
+ Py_TPPYPYFLAGS_FLOAT_SUBCLASS,
)
from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction, PyMethodDef,
@@ -426,6 +427,9 @@
pto.c_tp_flags |= Py_TPFLAGS_LIST_SUBCLASS
elif space.issubtype_w(w_obj, space.w_dict):
pto.c_tp_flags |= Py_TPFLAGS_DICT_SUBCLASS
+ # the following types are a pypy-specific extensions, using tp_pypy_flags
+ elif space.issubtype_w(w_obj, space.w_float):
+ pto.c_tp_pypy_flags |= Py_TPPYPYFLAGS_FLOAT_SUBCLASS
def check_descr(space, w_self, w_type):
if not space.isinstance_w(w_self, w_type):
More information about the pypy-commit
mailing list