[pypy-commit] pypy unicode-utf8: merge default into branch

mattip pypy.commits at gmail.com
Mon Mar 26 18:16:18 EDT 2018


Author: Matti Picus <matti.picus at gmail.com>
Branch: unicode-utf8
Changeset: r94143:c564ff0e3d7d
Date: 2018-03-27 01:15 +0300
http://bitbucket.org/pypy/pypy/changeset/c564ff0e3d7d/

Log:	merge default into branch

diff too long, truncating to 2000 out of 2249 lines

diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -4,42 +4,40 @@
 
 Welcome to PyPy!
 
-PyPy is both an implementation of the Python programming language, and
-an extensive compiler framework for dynamic language implementations.
-You can build self-contained Python implementations which execute
-independently from CPython.
+PyPy is an interperter that implements the Python programming language, based
+on the RPython compiler framework for dynamic language implementations.
 
-The home page is:
+The home page for the interpreter is:
 
     http://pypy.org/
 
-If you want to help developing PyPy, this document might help you:
+If you want to help developing PyPy, this documentation might help you:
 
     http://doc.pypy.org/
 
-It will also point you to the rest of the documentation which is generated
-from files in the pypy/doc directory within the source repositories. Enjoy
-and send us feedback!
+More documentation about the RPython framework can be found here
 
-    the pypy-dev team <pypy-dev at python.org>
+    http://rpython.readthedocs.io
 
+The source for the documentation is in the pypy/doc directory 
+
+Using PyPy instead of CPython
+=============================
+
+Please read the information at http://pypy.org to find the correct way to
+download and use PyPy as an alternative to CPython. 
 
 Building
 ========
 
-First switch to or download the correct branch.  The basic choices are
-``default`` for Python 2.7 and, for Python 3.X, the corresponding py3.X
-branch (e.g. ``py3.5``).
+Building PyPy is not the recommended way to obtain the PyPy alternative python
+interpreter. It is time-consuming and requires significant computing resources.
+More information can be found here
 
-Build with:
+    http://doc.pypy.org/en/latest/build.html
 
-.. code-block:: console
+Enjoy and send us feedback!
 
-    $ rpython/bin/rpython -Ojit pypy/goal/targetpypystandalone.py
+    the pypy-dev team <pypy-dev at python.org>
 
-This ends up with a ``pypy-c`` or ``pypy3-c`` binary in the main pypy
-directory.  We suggest to use virtualenv with the resulting
-pypy-c/pypy3-c as the interpreter; you can find more details about
-various installation schemes here:
 
-    http://doc.pypy.org/en/latest/install.html
diff --git a/pypy/doc/install.rst b/pypy/doc/install.rst
--- a/pypy/doc/install.rst
+++ b/pypy/doc/install.rst
@@ -17,13 +17,18 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The quickest way to start using PyPy is to download a prebuilt binary for your
-OS and architecture.  You can either use the `most recent release`_ or one of
-our `development nightly build`_.  Please note that the nightly builds are not
+OS and architecture.  You may be able to use either use the
+`most recent release`_ or one of our `development nightly build`_. These
+builds depend on dynamically linked libraries that may not be available on your
+OS. See the section about `Linux binaries` for more info and alternatives that
+may work on your system.
+
+Please note that the nightly builds are not
 guaranteed to be as stable as official releases, use them at your own risk.
 
 .. _most recent release: http://pypy.org/download.html
 .. _development nightly build: http://buildbot.pypy.org/nightly/trunk/
-
+.. _Linux binaries: http://pypy.org/download.html#linux-binaries-and-common-distributions
 
 Installing PyPy
 ~~~~~~~~~~~~~~~
@@ -69,9 +74,9 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 It is often convenient to run pypy inside a virtualenv.  To do this
-you need a recent version of virtualenv -- 1.6.1 or greater.  You can
+you need a version of virtualenv -- 1.6.1 or greater.  You can
 then install PyPy both from a precompiled tarball or from a mercurial
-checkout::
+checkout after translation::
 
 	# from a tarball
 	$ virtualenv -p /opt/pypy-xxx/bin/pypy my-pypy-env
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -55,6 +55,10 @@
 Speed up branchy code that does a lot of function inlining by saving one call
 to read the TLS in most bridges.
 
+.. branch: rpython-sprint
+
+Refactor in rpython signatures
+
 .. branch: unicode-utf8-re
 .. branch: utf8-io
 Utf8 handling for unicode
diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py
--- a/pypy/module/_sre/interp_sre.py
+++ b/pypy/module/_sre/interp_sre.py
@@ -319,12 +319,13 @@
         n = 0
         last_pos = ctx.ZERO
         while not count or n < count:
+            pattern = ctx.pattern
             sub_jitdriver.jit_merge_point(
                 self=self,
                 use_builder=use_builder,
                 filter_is_callable=filter_is_callable,
                 filter_type=type(w_filter),
-                ctx=ctx,
+                ctx=ctx, pattern=pattern,
                 w_filter=w_filter,
                 strbuilder=strbuilder,
                 filter_as_string=filter_as_string,
@@ -401,7 +402,7 @@
             filter_as_string
             w_string sublist_w
             self""".split(),
-    greens=["filter_is_callable", "use_builder", "filter_type", "ctx.pattern"])
+    greens=["filter_is_callable", "use_builder", "filter_type", "pattern"])
 
 
 def _sub_append_slice(ctx, space, use_builder, sublist_w,
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/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -82,10 +82,10 @@
 def PyTraceBack_Here(space, w_frame):
     from pypy.interpreter.pytraceback import record_application_traceback
     state = space.fromcache(State)
-    if state.operror is None:
+    if state.get_exception() is None:
         return -1
     frame = space.interp_w(PyFrame, w_frame)
-    record_application_traceback(space, state.operror, frame, 0)
+    record_application_traceback(space, state.get_exception(), frame, 0)
     return 0
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
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/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -45,6 +45,18 @@
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
+def w_kwargs_from_args(space, __args__):
+    w_kwargs = None
+    if __args__.keywords:
+        # CCC: we should probably have a @jit.look_inside_iff if the
+        # keyword count is constant, as we do in Arguments.unpack
+        w_kwargs = space.newdict()
+        for i in range(len(__args__.keywords)):
+            key = __args__.keywords[i]
+            w_obj = __args__.keywords_w[i]
+            space.setitem(w_kwargs, space.newtext(key), w_obj)
+    return w_kwargs
+
 class W_PyCFunctionObject(W_Root):
     _immutable_fields_ = ["flags"]
 
@@ -103,15 +115,7 @@
     def call_keywords(self, space, w_self, __args__):
         func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
         py_args = tuple_from_args_w(space, __args__.arguments_w)
-        w_kwargs = None
-        if __args__.keywords:
-            # CCC: we should probably have a @jit.look_inside_iff if the
-            # keyword count is constant, as we do in Arguments.unpack
-            w_kwargs = space.newdict()
-            for i in range(len(__args__.keywords)):
-                key = __args__.keywords[i]
-                w_obj = __args__.keywords_w[i]
-                space.setitem(w_kwargs, space.newtext(key), w_obj)
+        w_kwargs = w_kwargs_from_args(space, __args__)
         try:
             return generic_cpy_call(space, func, w_self, py_args, w_kwargs)
         finally:
@@ -213,14 +217,15 @@
                             (self.name, self.w_objclass.getname(self.space)))
 
 
+class W_PyCWrapperObject(W_Root):
+    """
+    Abstract class; for concrete subclasses, see slotdefs.py
+    """
+    _immutable_fields_ = ['offset[*]']
 
-class W_PyCWrapperObject(W_Root):
-    def __init__(self, space, pto, method_name, wrapper_func,
-                 wrapper_func_kwds, doc, func, offset=None):
+    def __init__(self, space, pto, method_name, doc, func, offset):
         self.space = space
         self.method_name = method_name
-        self.wrapper_func = wrapper_func
-        self.wrapper_func_kwds = wrapper_func_kwds
         self.doc = doc
         self.func = func
         self.offset = offset
@@ -229,10 +234,17 @@
         assert isinstance(w_type, W_TypeObject)
         self.w_objclass = w_type
 
-    def call(self, space, w_self, w_args, w_kw):
+    def descr_call(self, space, w_self, __args__):
+        return self.call(space, w_self, __args__)
+
+    def call(self, space, w_self, __args__):
+        raise NotImplementedError
+
+    @jit.unroll_safe
+    def get_func_to_call(self):
         func_to_call = self.func
         if self.offset:
-            pto = as_pyobj(space, self.w_objclass)
+            pto = as_pyobj(self.space, self.w_objclass)
             # make ptr the equivalent of this, using the offsets
             #func_to_call = rffi.cast(rffi.VOIDP, ptr.c_tp_as_number.c_nb_multiply)
             if pto:
@@ -246,31 +258,33 @@
                 assert False, "failed to convert w_type %s to PyObject" % str(
                                                               self.w_objclass)
         assert func_to_call
-        if self.wrapper_func is None:
-            assert self.wrapper_func_kwds is not None
-            return self.wrapper_func_kwds(space, w_self, w_args, func_to_call,
-                                          w_kw)
-        if space.is_true(w_kw):
-            raise oefmt(space.w_TypeError,
+        return func_to_call
+
+    def check_args(self, __args__, arity):
+        length = len(__args__.arguments_w)
+        if length != arity:
+            raise oefmt(self.space.w_TypeError, "expected %d arguments, got %d",
+                        arity, length)
+        if __args__.keywords:
+            raise oefmt(self.space.w_TypeError,
                         "wrapper %s doesn't take any keyword arguments",
                         self.method_name)
-        return self.wrapper_func(space, w_self, w_args, func_to_call)
+
+    def check_argsv(self, __args__, min, max):
+        length = len(__args__.arguments_w)
+        if not min <= length <= max:
+            raise oefmt(self.space.w_TypeError, "expected %d-%d arguments, got %d",
+                        min, max, length)
+        if __args__.keywords:
+            raise oefmt(self.space.w_TypeError,
+                        "wrapper %s doesn't take any keyword arguments",
+                        self.method_name)
 
     def descr_method_repr(self):
         return self.space.newtext("<slot wrapper '%s' of '%s' objects>" %
                                   (self.method_name,
                                    self.w_objclass.name))
 
- at jit.dont_look_inside
-def cwrapper_descr_call(space, w_self, __args__):
-    self = space.interp_w(W_PyCWrapperObject, w_self)
-    args_w, kw_w = __args__.unpack()
-    w_args = space.newtuple(args_w[1:])
-    w_self = args_w[0]
-    w_kw = space.newdict()
-    for key, w_obj in kw_w.items():
-        space.setitem(w_kw, space.newtext(key), w_obj)
-    return self.call(space, w_self, w_args, w_kw)
 
 def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
     asking_for_bound = (space.is_none(w_cls) or
@@ -323,7 +337,7 @@
 
 W_PyCWrapperObject.typedef = TypeDef(
     'wrapper_descriptor',
-    __call__ = interp2app(cwrapper_descr_call),
+    __call__ = interp2app(W_PyCWrapperObject.descr_call),
     __get__ = interp2app(cmethod_descr_get),
     __name__ = interp_attrproperty('method_name', cls=W_PyCWrapperObject,
         wrapfn="newtext_or_none"),
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/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -31,9 +31,10 @@
 @cpython_api([], PyObject, result_borrowed=True)
 def PyErr_Occurred(space):
     state = space.fromcache(State)
-    if state.operror is None:
+    operror = state.get_exception()
+    if operror is None:
         return None
-    return state.operror.w_type     # borrowed ref
+    return operror.w_type     # borrowed ref
 
 @cpython_api([], lltype.Void)
 def PyErr_Clear(space):
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/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -19,6 +19,8 @@
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext import userslot
 from pypy.module.cpyext.buffer import CBuffer, CPyBuffer, fq
+from pypy.module.cpyext.methodobject import (W_PyCWrapperObject, tuple_from_args_w,
+                                             w_kwargs_from_args)
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.argument import Arguments
 from rpython.rlib.unroll import unrolling_iterable
@@ -38,29 +40,6 @@
 Py_GT = 4
 Py_GE = 5
 
-
-def check_num_args(space, w_ob, n):
-    from pypy.module.cpyext.tupleobject import PyTuple_CheckExact
-    if not PyTuple_CheckExact(space, w_ob):
-        raise oefmt(space.w_SystemError,
-                    "PyArg_UnpackTuple() argument list is not a tuple")
-    if n == space.len_w(w_ob):
-        return
-    raise oefmt(space.w_TypeError,
-                "expected %d arguments, got %d",
-                n, space.len_w(w_ob))
-
-def check_num_argsv(space, w_ob, low, high):
-    from pypy.module.cpyext.tupleobject import PyTuple_CheckExact
-    if not PyTuple_CheckExact(space, w_ob):
-        raise oefmt(space.w_SystemError,
-                    "PyArg_UnpackTuple() argument list is not a tuple")
-    if low <=space.len_w(w_ob) <= high:
-        return
-    raise oefmt(space.w_TypeError,
-                "expected %d-%d arguments, got %d",
-                low, high, space.len_w(w_ob))
-
 @not_rpython
 def llslot(space, func):
     return func.api_func.get_llhelper(space)
@@ -71,337 +50,413 @@
     get_llhelper = v_func.value.api_func.get_llhelper
     return ctx.appcall(get_llhelper, v_space)
 
+# NOTE: the following wrap_* are subclasses of W_PyCWrapperObject, even if
+# they don't follow the usual W_* naming convention for subclasses of W_Root:
+# we do this because we automatically generate most of the slots from the
+# CPython code copy&pasted inside slotdefs_str, and thus we need to keep the
+# same names as they are used in C.
 
-def wrap_init(space, w_self, w_args, func, w_kwargs):
-    func_init = rffi.cast(initproc, func)
-    res = generic_cpy_call(space, func_init, w_self, w_args, w_kwargs)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return None
+class wrap_init(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_init = rffi.cast(initproc, func)
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        w_kwargs = w_kwargs_from_args(space, __args__)
+        res = generic_cpy_call(space, func_init, w_self, py_args, w_kwargs)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return None
 
-def wrap_unaryfunc(space, w_self, w_args, func):
-    func_unary = rffi.cast(unaryfunc, func)
-    check_num_args(space, w_args, 0)
-    return generic_cpy_call(space, func_unary, w_self)
+class wrap_unaryfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 0)
+        func = self.get_func_to_call()
+        func_unary = rffi.cast(unaryfunc, func)
+        return generic_cpy_call(space, func_unary, w_self)
 
-def wrap_binaryfunc(space, w_self, w_args, func):
-    func_binary = rffi.cast(binaryfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    return generic_cpy_call(space, func_binary, w_self, args_w[0])
+class wrap_binaryfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_binary = rffi.cast(binaryfunc, func)
+        w_x = __args__.arguments_w[0]
+        return generic_cpy_call(space, func_binary, w_self, w_x)
 
 def _get_ob_type(space, w_obj):
     # please ensure that w_obj stays alive
     ob_type = as_pyobj(space, space.type(w_obj))
     return rffi.cast(PyTypeObjectPtr, ob_type)
 
-def wrap_binaryfunc_l(space, w_self, w_args, func):
-    func_binary = rffi.cast(binaryfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    ob_type = _get_ob_type(space, w_self)
-    if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
-        not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
-        return space.w_NotImplemented
-    return generic_cpy_call(space, func_binary, w_self, args_w[0])
+class wrap_binaryfunc_l(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_binary = rffi.cast(binaryfunc, func)
+        w_value = __args__.arguments_w[0]
+        ob_type = _get_ob_type(space, w_self)
+        if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+            not space.issubtype_w(space.type(w_value), space.type(w_self))):
+            return space.w_NotImplemented
+        return generic_cpy_call(space, func_binary, w_self, w_value)
 
-def wrap_binaryfunc_r(space, w_self, w_args, func):
-    func_binary = rffi.cast(binaryfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    ob_type = _get_ob_type(space, w_self)
-    if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
-        not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
-        return space.w_NotImplemented
-    return generic_cpy_call(space, func_binary, args_w[0], w_self)
+class wrap_binaryfunc_r(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_binary = rffi.cast(binaryfunc, func)
+        w_value = __args__.arguments_w[0]
+        ob_type = _get_ob_type(space, w_self)
+        if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+            not space.issubtype_w(space.type(w_value), space.type(w_self))):
+            return space.w_NotImplemented
+        return generic_cpy_call(space, func_binary, w_value, w_self)
 
-def wrap_ternaryfunc(space, w_self, w_args, func):
-    # The third argument is optional
-    func_ternary = rffi.cast(ternaryfunc, func)
-    check_num_argsv(space, w_args, 1, 2)
-    args_w = space.fixedview(w_args)
-    arg3 = space.w_None
-    if len(args_w) > 1:
-        arg3 = args_w[1]
-    return generic_cpy_call(space, func_ternary, w_self, args_w[0], arg3)
+class wrap_ternaryfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        # The third argument is optional
+        self.check_argsv(__args__, 1, 2)
+        func = self.get_func_to_call()
+        func_ternary = rffi.cast(ternaryfunc, func)
+        w_arg0 = __args__.arguments_w[0]
+        if len(__args__.arguments_w) == 2:
+            w_arg1 = __args__.arguments_w[1]
+        else:
+            w_arg1 = space.w_None
+        return generic_cpy_call(space, func_ternary, w_self, w_arg0, w_arg1)
 
-def wrap_ternaryfunc_r(space, w_self, w_args, func):
-    # The third argument is optional
-    func_ternary = rffi.cast(ternaryfunc, func)
-    check_num_argsv(space, w_args, 1, 2)
-    args_w = space.fixedview(w_args)
-    ob_type = _get_ob_type(space, w_self)
-    if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
-        not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
-        return space.w_NotImplemented
-    arg3 = space.w_None
-    if len(args_w) > 1:
-        arg3 = args_w[1]
-    return generic_cpy_call(space, func_ternary, args_w[0], w_self, arg3)
+class wrap_ternaryfunc_r(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):    
+        # The third argument is optional
+        self.check_argsv(__args__, 1, 2)
+        func = self.get_func_to_call()
+        func_ternary = rffi.cast(ternaryfunc, func)
+        w_arg0 = __args__.arguments_w[0]
+        if len(__args__.arguments_w) == 2:
+            w_arg1 = __args__.arguments_w[1]
+        else:
+            w_arg1 = space.w_None
+        ob_type = _get_ob_type(space, w_self)
+        if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+            not space.issubtype_w(space.type(w_arg0), space.type(w_self))):
+            return space.w_NotImplemented
+        return generic_cpy_call(space, func_ternary, w_arg0, w_self, w_arg1)
 
+class wrap_inquirypred(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 0)
+        func = self.get_func_to_call()
+        func_inquiry = rffi.cast(inquiry, func)
+        res = generic_cpy_call(space, func_inquiry, w_self)
+        res = rffi.cast(lltype.Signed, res)
+        if res == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.newbool(bool(res))
 
-def wrap_inquirypred(space, w_self, w_args, func):
-    func_inquiry = rffi.cast(inquiry, func)
-    check_num_args(space, w_args, 0)
-    res = generic_cpy_call(space, func_inquiry, w_self)
-    res = rffi.cast(lltype.Signed, res)
-    if res == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.newbool(bool(res))
+class wrap_getattr(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(getattrfunc, func)
+        w_name = __args__.arguments_w[0]
+        name_ptr = rffi.str2charp(space.text_w(w_name))
+        try:
+            return generic_cpy_call(space, func_target, w_self, name_ptr)
+        finally:
+            rffi.free_charp(name_ptr)
 
-def wrap_getattr(space, w_self, w_args, func):
-    func_target = rffi.cast(getattrfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    name_ptr = rffi.str2charp(space.text_w(args_w[0]))
-    try:
-        return generic_cpy_call(space, func_target, w_self, name_ptr)
-    finally:
-        rffi.free_charp(name_ptr)
+class wrap_getattro(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(getattrofunc, func)
+        w_name = __args__.arguments_w[0]
+        return generic_cpy_call(space, func_target, w_self, w_name)
 
-def wrap_getattro(space, w_self, w_args, func):
-    func_target = rffi.cast(getattrofunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    return generic_cpy_call(space, func_target, w_self, args_w[0])
+class wrap_setattr(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 2)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(setattrofunc, func)
+        w_name = __args__.arguments_w[0]
+        w_value = __args__.arguments_w[1]
+        # XXX "Carlo Verre hack"?
+        res = generic_cpy_call(space, func_target, w_self, w_name, w_value)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_setattr(space, w_self, w_args, func):
-    func_target = rffi.cast(setattrofunc, func)
-    check_num_args(space, w_args, 2)
-    w_name, w_value = space.fixedview(w_args)
-    # XXX "Carlo Verre hack"?
-    res = generic_cpy_call(space, func_target, w_self, w_name, w_value)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_delattr(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(setattrofunc, func)
+        w_name = __args__.arguments_w[0]
+        # XXX "Carlo Verre hack"?
+        res = generic_cpy_call(space, func_target, w_self, w_name, None)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_delattr(space, w_self, w_args, func):
-    func_target = rffi.cast(setattrofunc, func)
-    check_num_args(space, w_args, 1)
-    w_name, = space.fixedview(w_args)
-    # XXX "Carlo Verre hack"?
-    res = generic_cpy_call(space, func_target, w_self, w_name, None)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_descr_get(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_target = rffi.cast(descrgetfunc, func)
+        length = len(__args__.arguments_w)
+        if length == 1:
+            w_obj = __args__.arguments_w[0]
+            w_type = None
+        elif length == 2:
+            w_obj = __args__.arguments_w[0]
+            w_type = __args__.arguments_w[1]
+        else:
+            raise oefmt(space.w_TypeError,
+                        "expected 1 or 2 arguments, got %d", len(__args__.arguments_w))
+        if w_obj is space.w_None:
+            w_obj = None
+        if w_type is space.w_None:
+            w_type = None
+        if w_obj is None and w_type is None:
+            raise oefmt(space.w_TypeError, "__get__(None, None) is invalid")
+        return generic_cpy_call(space, func_target, w_self, w_obj, w_type)
 
-def wrap_descr_get(space, w_self, w_args, func):
-    func_target = rffi.cast(descrgetfunc, func)
-    args_w = space.fixedview(w_args)
-    if len(args_w) == 1:
-        w_obj, = args_w
-        w_type = None
-    elif len(args_w) == 2:
-        w_obj, w_type = args_w
-    else:
-        raise oefmt(space.w_TypeError,
-                    "expected 1 or 2 arguments, got %d", len(args_w))
-    if w_obj is space.w_None:
-        w_obj = None
-    if w_type is space.w_None:
-        w_type = None
-    if w_obj is None and w_type is None:
-        raise oefmt(space.w_TypeError, "__get__(None, None) is invalid")
-    return generic_cpy_call(space, func_target, w_self, w_obj, w_type)
+class wrap_descr_set(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 2)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(descrsetfunc, func)
+        w_obj = __args__.arguments_w[0]
+        w_value = __args__.arguments_w[1]
+        res = generic_cpy_call(space, func_target, w_self, w_obj, w_value)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_descr_set(space, w_self, w_args, func):
-    func_target = rffi.cast(descrsetfunc, func)
-    check_num_args(space, w_args, 2)
-    w_obj, w_value = space.fixedview(w_args)
-    res = generic_cpy_call(space, func_target, w_self, w_obj, w_value)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_descr_delete(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(descrsetfunc, func)
+        w_obj = __args__.arguments_w[0]
+        res = generic_cpy_call(space, func_target, w_self, w_obj, None)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_descr_delete(space, w_self, w_args, func):
-    func_target = rffi.cast(descrsetfunc, func)
-    check_num_args(space, w_args, 1)
-    w_obj, = space.fixedview(w_args)
-    res = generic_cpy_call(space, func_target, w_self, w_obj, None)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_call(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ternaryfunc, func)
+        py_args = tuple_from_args_w(space, __args__.arguments_w)
+        w_kwargs = w_kwargs_from_args(space, __args__)
+        return generic_cpy_call(space, func_target, w_self, py_args, w_kwargs)
 
-def wrap_call(space, w_self, w_args, func, w_kwds):
-    func_target = rffi.cast(ternaryfunc, func)
-    return generic_cpy_call(space, func_target, w_self, w_args, w_kwds)
+class wrap_ssizessizeobjargproc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 3)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ssizessizeobjargproc, func)
+        i = space.int_w(space.index(__args__.arguments_w[0]))
+        j = space.int_w(space.index(__args__.arguments_w[1]))
+        w_y = __args__.arguments_w[2]
+        res = generic_cpy_call(space, func_target, w_self, i, j, w_y)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_ssizessizeobjargproc(space, w_self, w_args, func):
-    func_target = rffi.cast(ssizessizeobjargproc, func)
-    check_num_args(space, w_args, 3)
-    args_w = space.fixedview(w_args)
-    i = space.int_w(space.index(args_w[0]))
-    j = space.int_w(space.index(args_w[1]))
-    w_y = args_w[2]
-    res = generic_cpy_call(space, func_target, w_self, i, j, w_y)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_lenfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 0)
+        func = self.get_func_to_call()
+        func_len = rffi.cast(lenfunc, func)
+        res = generic_cpy_call(space, func_len, w_self)
+        if widen(res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.newint(res)
 
-def wrap_lenfunc(space, w_self, w_args, func):
-    func_len = rffi.cast(lenfunc, func)
-    check_num_args(space, w_args, 0)
-    res = generic_cpy_call(space, func_len, w_self)
-    if widen(res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.newint(res)
+class wrap_sq_item(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ssizeargfunc, func)
+        w_index = __args__.arguments_w[0]
+        index = space.int_w(space.index(w_index))
+        return generic_cpy_call(space, func_target, w_self, index)
 
-def wrap_sq_item(space, w_self, w_args, func):
-    func_target = rffi.cast(ssizeargfunc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    index = space.int_w(space.index(args_w[0]))
-    return generic_cpy_call(space, func_target, w_self, index)
+class wrap_sq_setitem(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 2)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ssizeobjargproc, func)
+        w_index = __args__.arguments_w[0]
+        w_value = __args__.arguments_w[1]
+        index = space.int_w(space.index(w_index))
+        res = generic_cpy_call(space, func_target, w_self, index, w_value)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
-def wrap_sq_setitem(space, w_self, w_args, func):
-    func_target = rffi.cast(ssizeobjargproc, func)
-    check_num_args(space, w_args, 2)
-    args_w = space.fixedview(w_args)
-    index = space.int_w(space.index(args_w[0]))
-    res = generic_cpy_call(space, func_target, w_self, index, args_w[1])
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-
-def wrap_sq_delitem(space, w_self, w_args, func):
-    func_target = rffi.cast(ssizeobjargproc, func)
-    check_num_args(space, w_args, 1)
-    args_w = space.fixedview(w_args)
-    index = space.int_w(space.index(args_w[0]))
-    null = rffi.cast(PyObject, 0)
-    res = generic_cpy_call(space, func_target, w_self, index, null)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_sq_delitem(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ssizeobjargproc, func)
+        w_index = __args__.arguments_w[0]
+        index = space.int_w(space.index(w_index))
+        null = rffi.cast(PyObject, 0)
+        res = generic_cpy_call(space, func_target, w_self, index, null)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
 
 # Warning, confusing function name (like CPython).  Used only for sq_contains.
-def wrap_objobjproc(space, w_self, w_args, func):
-    func_target = rffi.cast(objobjproc, func)
-    check_num_args(space, w_args, 1)
-    w_value, = space.fixedview(w_args)
-    res = generic_cpy_call(space, func_target, w_self, w_value)
-    res = rffi.cast(lltype.Signed, res)
-    if res == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.newbool(bool(res))
+class wrap_objobjproc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(objobjproc, func)
+        w_value = __args__.arguments_w[0]
+        res = generic_cpy_call(space, func_target, w_self, w_value)
+        res = rffi.cast(lltype.Signed, res)
+        if res == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.newbool(bool(res))
 
-def wrap_objobjargproc(space, w_self, w_args, func):
-    func_target = rffi.cast(objobjargproc, func)
-    check_num_args(space, w_args, 2)
-    w_key, w_value = space.fixedview(w_args)
-    res = generic_cpy_call(space, func_target, w_self, w_key, w_value)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.w_None
+class wrap_objobjargproc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 2)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(objobjargproc, func)
+        w_key = __args__.arguments_w[0]
+        w_value = __args__.arguments_w[1]
+        res = generic_cpy_call(space, func_target, w_self, w_key, w_value)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.w_None
 
-def wrap_delitem(space, w_self, w_args, func):
-    func_target = rffi.cast(objobjargproc, func)
-    check_num_args(space, w_args, 1)
-    w_key, = space.fixedview(w_args)
-    null = rffi.cast(PyObject, 0)
-    res = generic_cpy_call(space, func_target, w_self, w_key, null)
-    if rffi.cast(lltype.Signed, res) == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.w_None
+class wrap_delitem(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(objobjargproc, func)
+        w_key = __args__.arguments_w[0]
+        null = rffi.cast(PyObject, 0)
+        res = generic_cpy_call(space, func_target, w_self, w_key, null)
+        if rffi.cast(lltype.Signed, res) == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.w_None
 
-def wrap_ssizessizeargfunc(space, w_self, w_args, func):
-    func_target = rffi.cast(ssizessizeargfunc, func)
-    check_num_args(space, w_args, 2)
-    args_w = space.fixedview(w_args)
-    start = space.int_w(args_w[0])
-    end = space.int_w(args_w[1])
-    return generic_cpy_call(space, func_target, w_self, start, end)
+class wrap_ssizessizeargfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 2)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(ssizessizeargfunc, func)
+        start = space.int_w(__args__.arguments_w[0])
+        end = space.int_w(__args__.arguments_w[1])
+        return generic_cpy_call(space, func_target, w_self, start, end)
 
-def wrap_next(space, w_self, w_args, func):
-    from pypy.module.cpyext.api import generic_cpy_call_expect_null
-    func_target = rffi.cast(iternextfunc, func)
-    check_num_args(space, w_args, 0)
-    w_res = generic_cpy_call_expect_null(space, func_target, w_self)
-    if not w_res and not PyErr_Occurred(space):
-        raise OperationError(space.w_StopIteration, space.w_None)
-    return w_res
+class wrap_next(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        from pypy.module.cpyext.api import generic_cpy_call_expect_null
+        self.check_args(__args__, 0)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(iternextfunc, func)
+        w_res = generic_cpy_call_expect_null(space, func_target, w_self)
+        if not w_res and not PyErr_Occurred(space):
+            raise OperationError(space.w_StopIteration, space.w_None)
+        return w_res
 
-def wrap_hashfunc(space, w_self, w_args, func):
-    func_target = rffi.cast(hashfunc, func)
-    check_num_args(space, w_args, 0)
-    res = generic_cpy_call(space, func_target, w_self)
-    if res == -1:
-        space.fromcache(State).check_and_raise_exception(always=True)
-    return space.newint(res)
+class wrap_hashfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 0)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(hashfunc, func)
+        res = generic_cpy_call(space, func_target, w_self)
+        if res == -1:
+            space.fromcache(State).check_and_raise_exception(always=True)
+        return space.newint(res)
 
-def wrap_getreadbuffer(space, w_self, w_args, func):
-    func_target = rffi.cast(readbufferproc, func)
-    py_type = _get_ob_type(space, w_self)
-    rbp = rffi.cast(rffi.VOIDP, 0)
-    if py_type.c_tp_as_buffer:
-        rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
-    with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
-        index = rffi.cast(Py_ssize_t, 0)
-        size = generic_cpy_call(space, func_target, w_self, index, ptr)
-        if size < 0:
-            space.fromcache(State).check_and_raise_exception(always=True)
-        view = CPyBuffer(space, ptr[0], size, w_self,
-                               releasebufferproc=rbp)
-        fq.register_finalizer(view)
-        return space.newbuffer(CBuffer(view))
+class wrap_getreadbuffer(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_target = rffi.cast(readbufferproc, func)
+        py_type = _get_ob_type(space, w_self)
+        rbp = rffi.cast(rffi.VOIDP, 0)
+        if py_type.c_tp_as_buffer:
+            rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+        with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
+            index = rffi.cast(Py_ssize_t, 0)
+            size = generic_cpy_call(space, func_target, w_self, index, ptr)
+            if size < 0:
+                space.fromcache(State).check_and_raise_exception(always=True)
+            view = CPyBuffer(space, ptr[0], size, w_self,
+                                   releasebufferproc=rbp)
+            fq.register_finalizer(view)
+            return space.newbuffer(CBuffer(view))
 
-def wrap_getwritebuffer(space, w_self, w_args, func):
-    func_target = rffi.cast(readbufferproc, func)
-    py_type = _get_ob_type(space, w_self)
-    rbp = rffi.cast(rffi.VOIDP, 0)
-    if py_type.c_tp_as_buffer:
-        rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
-    with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
-        index = rffi.cast(Py_ssize_t, 0)
-        size = generic_cpy_call(space, func_target, w_self, index, ptr)
-        if size < 0:
-            space.fromcache(State).check_and_raise_exception(always=True)
-        view = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
-                               releasebufferproc=rbp)
-        fq.register_finalizer(view)
-        return space.newbuffer(CBuffer(view))
+class wrap_getwritebuffer(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_target = rffi.cast(readbufferproc, func)
+        py_type = _get_ob_type(space, w_self)
+        rbp = rffi.cast(rffi.VOIDP, 0)
+        if py_type.c_tp_as_buffer:
+            rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+        with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
+            index = rffi.cast(Py_ssize_t, 0)
+            size = generic_cpy_call(space, func_target, w_self, index, ptr)
+            if size < 0:
+                space.fromcache(State).check_and_raise_exception(always=True)
+            view = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
+                                   releasebufferproc=rbp)
+            fq.register_finalizer(view)
+            return space.newbuffer(CBuffer(view))
 
-def wrap_getbuffer(space, w_self, w_args, func):
-    func_target = rffi.cast(getbufferproc, func)
-    py_type = _get_ob_type(space, w_self)
-    rbp = rffi.cast(rffi.VOIDP, 0)
-    if py_type.c_tp_as_buffer:
-        rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
-    with lltype.scoped_alloc(Py_buffer) as pybuf:
-        _flags = 0
-        if space.len_w(w_args) > 0:
-            _flags = space.int_w(space.listview(w_args)[0])
-        flags = rffi.cast(rffi.INT_real,_flags)
-        size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
-        if widen(size) < 0:
-            space.fromcache(State).check_and_raise_exception(always=True)
-        ptr = pybuf.c_buf
-        size = pybuf.c_len
-        ndim = widen(pybuf.c_ndim)
-        shape = None
-        if pybuf.c_shape:
-            shape = [pybuf.c_shape[i]   for i in range(ndim)]
-        strides = None
-        if pybuf.c_strides:
-            strides = [pybuf.c_strides[i] for i in range(ndim)]
-        if pybuf.c_format:
-            format = rffi.charp2str(pybuf.c_format)
-        else:
-            format = 'B'
-        # the CPython docs mandates that you do an incref whenever you call
-        # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't
-        # leak we release the buffer:
-        # https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
-        buf = CPyBuffer(space, ptr, size, w_self, format=format,
-                            ndim=ndim, shape=shape, strides=strides,
-                            itemsize=pybuf.c_itemsize,
-                            readonly=widen(pybuf.c_readonly),
-                            needs_decref=True,
-                            releasebufferproc = rbp)
-        fq.register_finalizer(buf)
-        return buf.wrap(space)
+
+class wrap_getbuffer(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        func = self.get_func_to_call()
+        func_target = rffi.cast(getbufferproc, func)
+        py_type = _get_ob_type(space, w_self)
+        rbp = rffi.cast(rffi.VOIDP, 0)
+        if py_type.c_tp_as_buffer:
+            rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+        with lltype.scoped_alloc(Py_buffer) as pybuf:
+            _flags = 0
+            if len(__args__.arguments_w) > 0:
+                _flags = space.int_w(__args__.arguments_w[0])
+            flags = rffi.cast(rffi.INT_real,_flags)
+            size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
+            if widen(size) < 0:
+                space.fromcache(State).check_and_raise_exception(always=True)
+            ptr = pybuf.c_buf
+            size = pybuf.c_len
+            ndim = widen(pybuf.c_ndim)
+            shape = None
+            if pybuf.c_shape:
+                shape = [pybuf.c_shape[i]   for i in range(ndim)]
+            strides = None
+            if pybuf.c_strides:
+                strides = [pybuf.c_strides[i] for i in range(ndim)]
+            if pybuf.c_format:
+                format = rffi.charp2str(pybuf.c_format)
+            else:
+                format = 'B'
+            # the CPython docs mandates that you do an incref whenever you call
+            # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't
+            # leak we release the buffer:
+            # https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
+            buf = CPyBuffer(space, ptr, size, w_self, format=format,
+                                ndim=ndim, shape=shape, strides=strides,
+                                itemsize=pybuf.c_itemsize,
+                                readonly=widen(pybuf.c_readonly),
+                                needs_decref=True,
+                                releasebufferproc = rbp)
+            fq.register_finalizer(buf)
+            return buf.wrap(space)
 
 def get_richcmp_func(OP_CONST):
-    def inner(space, w_self, w_args, func):
-        func_target = rffi.cast(richcmpfunc, func)
-        check_num_args(space, w_args, 1)
-        w_other, = space.fixedview(w_args)
-        return generic_cpy_call(space, func_target,
-            w_self, w_other, rffi.cast(rffi.INT_real, OP_CONST))
-    return inner
+    class wrap_richcmp(W_PyCWrapperObject):
+        def call(self, space, w_self, __args__):
+            self.check_args(__args__, 1)
+            func = self.get_func_to_call()
+            func_target = rffi.cast(richcmpfunc, func)
+            w_other = __args__.arguments_w[0]
+            return generic_cpy_call(space, func_target,
+                w_self, w_other, rffi.cast(rffi.INT_real, OP_CONST))
+    return wrap_richcmp
 
 richcmp_eq = get_richcmp_func(Py_EQ)
 richcmp_ne = get_richcmp_func(Py_NE)
@@ -410,17 +465,19 @@
 richcmp_gt = get_richcmp_func(Py_GT)
 richcmp_ge = get_richcmp_func(Py_GE)
 
-def wrap_cmpfunc(space, w_self, w_args, func):
-    func_target = rffi.cast(cmpfunc, func)
-    check_num_args(space, w_args, 1)
-    w_other, = space.fixedview(w_args)
+class wrap_cmpfunc(W_PyCWrapperObject):
+    def call(self, space, w_self, __args__):
+        self.check_args(__args__, 1)
+        func = self.get_func_to_call()
+        func_target = rffi.cast(cmpfunc, func)
+        w_other = __args__.arguments_w[0]
 
-    if not space.issubtype_w(space.type(w_self), space.type(w_other)):
-        raise oefmt(space.w_TypeError,
-                    "%T.__cmp__(x,y) requires y to be a '%T', not a '%T'",
-                    w_self, w_self, w_other)
+        if not space.issubtype_w(space.type(w_self), space.type(w_other)):
+            raise oefmt(space.w_TypeError,
+                        "%T.__cmp__(x,y) requires y to be a '%T', not a '%T'",
+                        w_self, w_self, w_other)
 
-    return space.newint(generic_cpy_call(space, func_target, w_self, w_other))
+        return space.newint(generic_cpy_call(space, func_target, w_self, w_other))
 
 SLOT_FACTORIES = {}
 def slot_factory(tp_name):
@@ -804,9 +861,10 @@
 missing_wrappers = ['wrap_indexargfunc', 'wrap_delslice', 'wrap_coercefunc']
 for name in missing_wrappers:
     assert name not in globals()
-    def missing_wrapper(space, w_self, w_args, func):
-        print "cpyext: missing slot wrapper " + name
-        raise NotImplementedError("Slot wrapper " + name)
+    class missing_wrapper(W_PyCWrapperObject):
+        def call(self, space, w_self, __args__):
+            print "cpyext: missing slot wrapper " + name
+            raise NotImplementedError("Slot wrapper " + name)
     missing_wrapper.__name__ = name
     globals()[name] = missing_wrapper
 
@@ -836,13 +894,12 @@
 PyWrapperFlag_KEYWORDS = 1
 
 class TypeSlot:
-    def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc):
+    def __init__(self, method_name, slot_name, function, wrapper, doc):
         self.method_name = method_name
         self.slot_name = slot_name
         self.slot_names = tuple(("c_" + slot_name).split("."))
         self.slot_func = function
-        self.wrapper_func = wrapper1
-        self.wrapper_func_kwds = wrapper2
+        self.wrapper_class = wrapper
         self.doc = doc
 
 # adapted from typeobject.c
@@ -863,13 +920,7 @@
 
     function = getattr(userslot, FUNCTION or '!missing', None)
     assert FLAGS == 0 or FLAGS == PyWrapperFlag_KEYWORDS
-    if FLAGS:
-        wrapper1 = None
-        wrapper2 = wrapper
-    else:
-        wrapper1 = wrapper
-        wrapper2 = None
-    return TypeSlot(NAME, SLOT, function, wrapper1, wrapper2, DOC)
+    return TypeSlot(NAME, SLOT, function, wrapper, DOC)
 
 def TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC):
     return FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, 0)
@@ -1158,7 +1209,7 @@
       x.slot_func.api_func if x.slot_func else None) for x in slotdefs])
 
 slotdefs_for_wrappers = unrolling_iterable(
-    [(x.method_name, x.slot_names, x.wrapper_func, x.wrapper_func_kwds, x.doc)
+    [(x.method_name, x.slot_names, x.wrapper_class, x.doc)
      for x in slotdefs])
 
 if __name__ == "__main__":
diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -2,11 +2,18 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter import executioncontext
+from pypy.interpreter.executioncontext import ExecutionContext
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rdynload import DLLHANDLE
 from rpython.rlib import rawrefcount
 import sys
 
+
+# Keep track of exceptions raised in cpyext for a particular execution
+# context.
+ExecutionContext.cpyext_operror = None
+
+
 class State:
     def __init__(self, space):
         self.space = space
@@ -18,7 +25,8 @@
 
     def reset(self):
         from pypy.module.cpyext.modsupport import PyMethodDef
-        self.operror = None
+        ec = self.space.getexecutioncontext()
+        ec.cpyext_operror = None
         self.new_method_def = lltype.nullptr(PyMethodDef)
 
         # When importing a package, use this to keep track
@@ -37,17 +45,24 @@
 
     def set_exception(self, operror):
         self.clear_exception()
-        self.operror = operror
+        ec = self.space.getexecutioncontext()
+        ec.cpyext_operror = operror
 
     def clear_exception(self):
         """Clear the current exception state, and return the operror."""
-        operror = self.operror
-        self.operror = None
+        ec = self.space.getexecutioncontext()
+        operror = ec.cpyext_operror
+        ec.cpyext_operror = None
         return operror
 
+    def get_exception(self):
+        ec = self.space.getexecutioncontext()
+        return ec.cpyext_operror
+
     @specialize.arg(1)
     def check_and_raise_exception(self, always=False):
-        operror = self.operror
+        ec = self.space.getexecutioncontext()
+        operror = ec.cpyext_operror
         if operror:
             self.clear_exception()
             raise operror
diff --git a/pypy/module/cpyext/test/test_api.py b/pypy/module/cpyext/test/test_api.py
--- a/pypy/module/cpyext/test/test_api.py
+++ b/pypy/module/cpyext/test/test_api.py
@@ -39,7 +39,7 @@
             raise Exception("%s is not callable" % (f,))
         f(*args)
         state = space.fromcache(State)
-        operror = state.operror
+        operror = state.get_exception()
         if not operror:
             raise Exception("DID NOT RAISE")
         if getattr(space, 'w_' + expected_exc.__name__) is not operror.w_type:
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_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -636,7 +636,8 @@
             Py_ssize_t refcnt_after;
             Py_INCREF(true_obj);
             Py_INCREF(true_obj);
-            PyBool_Check(true_obj);
+            if (!PyBool_Check(true_obj))
+                Py_RETURN_NONE;
             refcnt_after = true_obj->ob_refcnt;
             Py_DECREF(true_obj);
             Py_DECREF(true_obj);
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_pyerrors.py b/pypy/module/cpyext/test/test_pyerrors.py
--- a/pypy/module/cpyext/test/test_pyerrors.py
+++ b/pypy/module/cpyext/test/test_pyerrors.py
@@ -52,7 +52,8 @@
         api.PyErr_SetObject(space.w_ValueError, space.wrap("a value"))
         assert api.PyErr_Occurred() is space.w_ValueError
         state = space.fromcache(State)
-        assert space.eq_w(state.operror.get_w_value(space),
+        operror = state.get_exception()
+        assert space.eq_w(operror.get_w_value(space),
                           space.wrap("a value"))
 
         api.PyErr_Clear()
@@ -60,12 +61,14 @@
     def test_SetNone(self, space, api):
         api.PyErr_SetNone(space.w_KeyError)
         state = space.fromcache(State)
-        assert space.eq_w(state.operror.w_type, space.w_KeyError)
-        assert space.eq_w(state.operror.get_w_value(space), space.w_None)
+        operror = state.get_exception()
+        assert space.eq_w(operror.w_type, space.w_KeyError)
+        assert space.eq_w(operror.get_w_value(space), space.w_None)
         api.PyErr_Clear()
 
         api.PyErr_NoMemory()
-        assert space.eq_w(state.operror.w_type, space.w_MemoryError)
+        operror = state.get_exception()
+        assert space.eq_w(operror.w_type, space.w_MemoryError)
         api.PyErr_Clear()
 
     def test_Warning(self, space, api, capfd):
@@ -437,3 +440,59 @@
              '''),
             ])
         raises(SystemError, module.oops)
+
+    def test_error_thread_race(self):
+        # Check race condition: thread 0 returns from cpyext with error set,
+        # after thread 1 has set an error but before it returns.
+        module = self.import_extension('foo', [
+            ("emit_error", "METH_VARARGS",
+             '''
+             PyThreadState *save = NULL;
+             PyGILState_STATE gilsave;
+
+             /* NB. synchronization due to GIL */
+             static volatile int flag = 0;
+             int id;
+
+             if (!PyArg_ParseTuple(args, "i", &id))
+                 return NULL;
+
+             /* Proceed in thread 1 first */
+             save = PyEval_SaveThread();
+             while (id == 0 && flag == 0);
+             gilsave = PyGILState_Ensure();
+
+             PyErr_Format(PyExc_ValueError, "%d", id);
+
+             /* Proceed in thread 0 first */
+             if (id == 1) flag = 1;
+             PyGILState_Release(gilsave);
+             while (id == 1 && flag == 1);
+             PyEval_RestoreThread(save);
+
+             if (id == 0) flag = 0;
+             return NULL;
+             '''
+             ),
+            ])
+
+        import threading
+
+        failures = []
+
+        def worker(arg):
+            try:
+                module.emit_error(arg)
+                failures.append(True)
+            except Exception as exc:
+                if str(exc) != str(arg):
+                    failures.append(exc)
+
+        threads = [threading.Thread(target=worker, args=(j,))
+                   for j in (0, 1)]
+        for t in threads:
+            t.start()
+        for t in threads:
+            t.join()
+
+        assert not failures
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
@@ -796,6 +796,36 @@
         assert module.tp_init(list, x, ("hi",)) is None
         assert x == ["h", "i"]
 
+    def test_mp_subscript(self):
+        module = self.import_extension('foo', [
+           ("new_obj", "METH_NOARGS",
+            '''
+                PyObject *obj;
+                obj = PyObject_New(PyObject, &Foo_Type);
+                return obj;
+            '''
+            )], prologue='''
+            static PyObject*
+            mp_subscript(PyObject *self, PyObject *key)
+            {
+                return Py_BuildValue("i", 42);
+            }
+            PyMappingMethods tp_as_mapping;
+            static PyTypeObject Foo_Type = {
+                PyVarObject_HEAD_INIT(NULL, 0)
+                "foo.foo",
+            };
+            ''', more_init = '''
+                Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT;
+                Foo_Type.tp_as_mapping = &tp_as_mapping;
+                tp_as_mapping.mp_subscript = (binaryfunc)mp_subscript;
+                if (PyType_Ready(&Foo_Type) < 0) INITERROR;
+            ''')
+        obj = module.new_obj()
+        assert obj[100] == 42
+        raises(TypeError, "obj.__getitem__(100, 101)")
+        raises(TypeError, "obj.__getitem__(100, a=42)")
+
     def test_mp_ass_subscript(self):
         module = self.import_extension('foo', [
            ("new_obj", "METH_NOARGS",
@@ -859,6 +889,84 @@
         res = "foo" in obj
         assert res is True
 
+    def test_sq_ass_slice(self):
+        module = self.import_extension('foo', [
+           ("new_obj", "METH_NOARGS",
+            '''
+                PyObject *obj;
+                obj = PyObject_New(PyObject, &Foo_Type);
+                return obj;
+            '''
+            )], prologue='''
+            static int
+            sq_ass_slice(PyObject *self, Py_ssize_t a, Py_ssize_t b, PyObject *o)
+            {
+                int expected = (a == 10 && b == 20 &&
+                                PyInt_Check(o) && PyInt_AsLong(o) == 42);
+                if (!expected) {
+                    PyErr_SetString(PyExc_ValueError, "test failed");
+                    return -1;
+                }
+                return 0;
+            }
+            PySequenceMethods tp_as_sequence;
+            static PyTypeObject Foo_Type = {
+                PyVarObject_HEAD_INIT(NULL, 0)
+                "foo.foo",
+            };
+            ''', more_init='''
+                Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT;
+                Foo_Type.tp_as_sequence = &tp_as_sequence;
+                tp_as_sequence.sq_ass_slice = sq_ass_slice;
+                if (PyType_Ready(&Foo_Type) < 0) INITERROR;
+            ''')
+        obj = module.new_obj()
+        obj[10:20] = 42
+        raises(ValueError, "obj[10:20] = 43")
+        raises(ValueError, "obj[11:20] = 42")
+        raises(ValueError, "obj[10:21] = 42")
+
+    def test_sq_ass_item(self):
+        module = self.import_extension('foo', [
+           ("new_obj", "METH_NOARGS",
+            '''
+                PyObject *obj;
+                obj = PyObject_New(PyObject, &Foo_Type);
+                return obj;
+            '''
+            )], prologue='''
+            static int
+            sq_ass_item(PyObject *self, Py_ssize_t i, PyObject *o)
+            {
+                int expected;
+                if (o == NULL)              // delitem
+                    expected = (i == 12);
+                else                        // setitem
+                    expected = (i == 10 && PyInt_Check(o) && PyInt_AsLong(o) == 42);
+                if (!expected) {
+                    PyErr_SetString(PyExc_ValueError, "test failed");
+                    return -1;
+                }
+                return 0;
+            }
+            PySequenceMethods tp_as_sequence;
+            static PyTypeObject Foo_Type = {
+                PyVarObject_HEAD_INIT(NULL, 0)
+                "foo.foo",
+            };
+            ''', more_init='''
+                Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT;
+                Foo_Type.tp_as_sequence = &tp_as_sequence;
+                tp_as_sequence.sq_ass_item = sq_ass_item;
+                if (PyType_Ready(&Foo_Type) < 0) INITERROR;
+            ''')
+        obj = module.new_obj()
+        obj[10] = 42
+        raises(ValueError, "obj[10] = 43")
+        raises(ValueError, "obj[11] = 42")
+        del obj[12]
+        raises(ValueError, "del obj[13]")
+
     def test_tp_iter(self):
         module = self.import_extension('foo', [
            ("tp_iter", "METH_VARARGS",
@@ -1537,4 +1645,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,10 +22,11 @@
     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,
-    W_PyCMethodObject, W_PyCFunctionObject)
+    PyCFunction_NewEx, PyCFunction, PyMethodDef,
+    W_PyCMethodObject, W_PyCFunctionObject, W_PyCWrapperObject)
 from pypy.module.cpyext.modsupport import convert_method_defs
 from pypy.module.cpyext.pyobject import (
     PyObject, make_ref, from_ref, get_typedescr, make_typedescr,
@@ -310,7 +311,7 @@
 def add_operators(space, dict_w, pto):
     from pypy.module.cpyext.object import PyObject_HashNotImplemented
     hash_not_impl = llslot(space, PyObject_HashNotImplemented)
-    for method_name, slot_names, wrapper_func, wrapper_func_kwds, doc in slotdefs_for_wrappers:
+    for method_name, slot_names, wrapper_class, doc in slotdefs_for_wrappers:
         if method_name in dict_w:
             continue
         offset = [rffi.offsetof(lltype.typeOf(pto).TO, slot_names[0])]
@@ -335,10 +336,11 @@
         func_voidp = rffi.cast(rffi.VOIDP, func)
         if not func:
             continue
-        if wrapper_func is None and wrapper_func_kwds is None:
+        if wrapper_class is None:
             continue
-        w_obj = W_PyCWrapperObject(space, pto, method_name, wrapper_func,
-                wrapper_func_kwds, doc, func_voidp, offset=offset)
+
+        assert issubclass(wrapper_class, W_PyCWrapperObject)
+        w_obj = wrapper_class(space, pto, method_name, doc, func_voidp, offset=offset[:])
         dict_w[method_name] = w_obj
     if pto.c_tp_doc:
         dict_w['__doc__'] = space.newtext(
@@ -426,6 +428,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):
diff --git a/pypy/module/unicodedata/test/test_hyp.py b/pypy/module/unicodedata/test/test_hyp.py
--- a/pypy/module/unicodedata/test/test_hyp.py
+++ b/pypy/module/unicodedata/test/test_hyp.py
@@ -1,7 +1,7 @@
 import sys
 import pytest
 try:
-    from hypothesis import given, strategies as st, example, settings
+    from hypothesis import given, strategies as st, example, settings, assume
 except ImportError:
     pytest.skip("hypothesis required")
 
@@ -43,11 +43,14 @@
 
 @pytest.mark.parametrize('NF1, NF2, NF3', compositions)
 @example(s=u'---\uafb8\u11a7---')  # issue 2289
- at example(s=u'\ufacf')
 @settings(max_examples=1000)
 @given(s=st.text())
 def test_composition(s, space, NF1, NF2, NF3):
-    if s == u'\ufacf' and sys.maxunicode == 65535:
-        pytest.skip('chr(0xfacf) normalizes to chr(0x2284a), which is too big')
+    # 'chr(0xfacf) normalizes to chr(0x2284a), which is too big')
+    assume(not (s == u'\ufacf' and sys.maxunicode == 65535))
     norm1, norm2, norm3 = [make_normalization(space, form) for form in [NF1, NF2, NF3]]
     assert norm2(norm1(s)) == norm3(s)
+
+if sys.maxunicode != 65535:
+    # conditionally generate the example via an unwrapped decorator    
+    test_composition = example(s=u'\ufacf')(test_composition)
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -15,6 +15,7 @@
 from rpython.rlib.listsort import make_timsort_class
 from rpython.rlib.objectmodel import (
     import_from_mixin, instantiate, newlist_hint, resizelist_hint, specialize)
+from rpython.rlib.rarithmetic import ovfcheck
 from rpython.rlib import longlong2float
 from rpython.tool.sourcetools import func_with_new_name
 
@@ -872,7 +873,12 @@
         """Extend w_list from a generic iterable"""
         length_hint = self.space.length_hint(w_iterable, 0)
         if length_hint:
-            w_list._resize_hint(w_list.length() + length_hint)
+            try:
+                newsize_hint = ovfcheck(w_list.length() + length_hint)
+            except OverflowError:
+                pass
+            else:
+                w_list._resize_hint(newsize_hint)
 
         extended = _do_extend_from_iterable(self.space, w_list, w_iterable)
 
diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py
--- a/pypy/objspace/std/test/test_listobject.py
+++ b/pypy/objspace/std/test/test_listobject.py
@@ -619,6 +619,18 @@
         assert l == [1.2, 2.3, 3.4, 4.5]
         assert l is l0
 
+    def test_extend_iterable_length_hint_overflow(self):
+        import sys
+        class CustomIterable(object):
+            def __iter__(self):
+                if False:
+                    yield
+            def __length_hint__(self):
+                return sys.maxsize
+        a = [1, 2, 3, 4]
+        a.extend(CustomIterable())
+        assert a == [1, 2, 3, 4]
+
     def test_sort(self):
         l = l0 = [1, 5, 3, 0]
         l.sort()
diff --git a/rpython/annotator/signature.py b/rpython/annotator/signature.py
--- a/rpython/annotator/signature.py
+++ b/rpython/annotator/signature.py
@@ -14,16 +14,16 @@
 
 def _annotation_key(t):
     from rpython.rtyper import extregistry
-    if type(t) is list:
+    if isinstance(t, list):
         assert len(t) == 1
         return ('list', _annotation_key(t[0]))
-    elif type(t) is dict:
+    elif isinstance(t, dict):
         assert len(t.keys()) == 1
         return ('dict', _annotation_key(t.items()[0]))
     elif isinstance(t, tuple):
         return tuple([_annotation_key(i) for i in t])
     elif extregistry.is_registered(t):
-        # XXX should it really be always different?
+        # XXX do we want to do something in this case?
         return t
     return t
 
@@ -38,24 +38,36 @@
             return t
     return _compute_annotation(t, bookkeeper)
 
+
+def _validate_annotation_size(t):
+    try:
+        _ = iter(t)
+    except TypeError:  # if it's not an iterable, just return
+        return t       # (size does not matter)
+    if isinstance(t, tuple):  # we accept tuples with any length, because
+        return t              # their in-memory representation is predictable
+    if len(t) > 1:
+        raise TypeError("Cannot specify multiple types in a %s (try using tuple)", type(t))
+
+
 def _compute_annotation(t, bookkeeper=None):
     from rpython.rtyper.lltypesystem import lltype
     from rpython.rtyper.llannotation import lltype_to_annotation
+    _validate_annotation_size(t)
     if isinstance(t, SomeObject):
         return t
     elif isinstance(t, lltype.LowLevelType):
         return lltype_to_annotation(t)
     elif isinstance(t, list):
-        assert len(t) == 1, "We do not support type joining in list"
-        listdef = ListDef(bookkeeper, annotation(t[0]), mutated=True, resized=True)
-        return SomeList(listdef)
+        return SomeList(
+                ListDef(bookkeeper, annotation(t[0]),
+                        mutated=True, resized=True))
     elif isinstance(t, tuple):
         return SomeTuple(tuple([annotation(i) for i in t]))
     elif isinstance(t, dict):
-        assert len(t) == 1, "We do not support type joining in dict"
-        result = SomeDict(DictDef(bookkeeper, annotation(t.keys()[0]),
-                                annotation(t.values()[0])))
-        return result
+        return SomeDict(
+                DictDef(bookkeeper,
+                        annotation(t.keys()[0]), annotation(t.values()[0])))
     elif type(t) is types.NoneType:
         return s_None
     elif extregistry.is_registered(t):
@@ -84,15 +96,14 @@
     elif t is types.NoneType:
         return s_None
     elif bookkeeper and extregistry.is_registered_type(t):
-        entry = extregistry.lookup_type(t)
-        return entry.compute_annotation_bk(bookkeeper)
+        return (extregistry.lookup_type(t)
+                .compute_annotation_bk(bookkeeper))
     elif t is type:
         return SomeType()
     elif bookkeeper and not hasattr(t, '_freeze_'):
-        classdef = bookkeeper.getuniqueclassdef(t)
-        return SomeInstance(classdef)
+        return SomeInstance(bookkeeper.getuniqueclassdef(t))
     else:
-        raise AssertionError("annotationoftype(%r)" % (t,))
+        raise TypeError("Annotation of type %r not supported" % (t,))
 
 class Sig(object):
 
diff --git a/rpython/doc/rpython.rst b/rpython/doc/rpython.rst
--- a/rpython/doc/rpython.rst
+++ b/rpython/doc/rpython.rst
@@ -259,6 +259,26 @@
   intmask().
 
 
+Type Enforcing and Checking
+---------------------------
+
+RPython provides a helper decorator to force RPython-level types on function
+arguments. The decorator, called ``enforceargs()``, accepts as parameters the
+types expected to match the arguments of the function.
+
+Functions decorated with ``enforceargs()`` have their function signature
+analyzed and their RPython-level type inferred at import time (for further
+details about the flavor of translation performed in RPython, see the
+`Annotation pass documentation`_). Encountering types not supported by RPython
+will raise a ``TypeError``.
+
+``enforceargs()`` by default also performs type checking of parameter types
+each time the function is invoked. To disable this behavior, it's possible to
+pass the ``typecheck=False`` parameter to the decorator.
+
+.. _Annotation pass documentation: http://rpython.readthedocs.io/en/latest/translation.html#annotator
+
+
 Exception rules
 ---------------
 
diff --git a/rpython/doc/translation.rst b/rpython/doc/translation.rst
--- a/rpython/doc/translation.rst
+++ b/rpython/doc/translation.rst


More information about the pypy-commit mailing list