[pypy-commit] pypy default: cpyext: implement built-in classmethods: METH_CLASS flag.

amauryfa noreply at buildbot.pypy.org
Mon Oct 22 00:33:25 CEST 2012


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: 
Changeset: r58331:297db88c1327
Date: 2012-10-21 15:12 +0200
http://bitbucket.org/pypy/pypy/changeset/297db88c1327/

Log:	cpyext: implement built-in classmethods: METH_CLASS flag.

diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -116,10 +116,6 @@
     assert isinstance(w_method, Method)
     return borrow_from(w_method, w_method.w_class)
 
- at cpython_api([PyObject], PyObject)
-def PyClassMethod_New(space, w_function):
-    return space.call_method(space.builtin, "classmethod", w_function)
-
 def unwrap_list_of_strings(space, w_list):
     return [space.str_w(w_item) for w_item in space.fixedview(w_list)]
 
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
@@ -4,7 +4,8 @@
 from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
 from pypy.interpreter.gateway import interp2app
 from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.function import BuiltinFunction, Method, StaticMethod
+from pypy.interpreter.function import (
+    BuiltinFunction, Method, StaticMethod, ClassMethod)
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.pyobject import (PyObject, from_ref, make_ref,
                                          make_typedescr, Py_DecRef)
@@ -128,6 +129,21 @@
 
 PyCFunction_Check, PyCFunction_CheckExact = build_type_checkers("CFunction", W_PyCFunctionObject)
 
+class W_PyCClassMethodObject(W_PyCFunctionObject):
+    w_self = None
+    def __init__(self, space, ml, w_type):
+        self.space = space
+        self.ml = ml
+        self.name = rffi.charp2str(ml.c_ml_name)
+        self.w_objclass = w_type
+
+    def __repr__(self):
+        return self.space.unwrap(self.descr_method_repr())
+
+    def descr_method_repr(self):
+        return self.getrepr(self.space, "built-in method '%s' of '%s' object" % (self.name, self.w_objclass.getname(self.space)))
+
+
 class W_PyCWrapperObject(Wrappable):
     def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds,
             doc, func):
@@ -196,6 +212,11 @@
     else:
         return w_function
 
+def cclassmethod_descr_get(space, w_function, w_obj, w_cls=None):
+    if not w_cls:
+        w_cls = space.type(w_obj)
+    return space.wrap(Method(space, w_function, w_cls, space.w_None))
+
 
 W_PyCFunctionObject.typedef = TypeDef(
     'builtin_function_or_method',
@@ -216,6 +237,16 @@
     )
 W_PyCMethodObject.typedef.acceptable_as_base_class = False
 
+W_PyCClassMethodObject.typedef = TypeDef(
+    'classmethod',
+    __get__ = interp2app(cclassmethod_descr_get),
+    __call__ = interp2app(cmethod_descr_call),
+    __name__ = interp_attrproperty('name', cls=W_PyCClassMethodObject),
+    __objclass__ = interp_attrproperty_w('w_objclass', cls=W_PyCClassMethodObject),
+    __repr__ = interp2app(W_PyCClassMethodObject.descr_method_repr),
+    )
+W_PyCClassMethodObject.typedef.acceptable_as_base_class = False
+
 
 W_PyCWrapperObject.typedef = TypeDef(
     'wrapper_descriptor',
@@ -243,10 +274,18 @@
 def PyStaticMethod_New(space, w_func):
     return space.wrap(StaticMethod(w_func))
 
+ at cpython_api([PyObject], PyObject)
+def PyClassMethod_New(space, w_func):
+    return space.wrap(ClassMethod(w_func))
+
 @cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject)
 def PyDescr_NewMethod(space, w_type, method):
     return space.wrap(W_PyCMethodObject(space, method, w_type))
 
+ at cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject)
+def PyDescr_NewClassMethod(space, w_type, method):
+    return space.wrap(W_PyCClassMethodObject(space, method, w_type))
+
 def PyDescr_NewWrapper(space, pto, method_name, wrapper_func, wrapper_func_kwds,
                        doc, func):
     # not exactly the API sig
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -5,7 +5,7 @@
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import (
     W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
-    PyMethodDef, PyStaticMethod_New)
+    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import OperationError
@@ -97,8 +97,7 @@
                     if flags & METH_STATIC:
                         raise OperationError(space.w_ValueError,
                                 space.wrap("method cannot be both class and static"))
-                    #w_obj = PyDescr_NewClassMethod(space, w_type, method)
-                    w_obj = space.w_Ellipsis # XXX
+                    w_obj = PyDescr_NewClassMethod(space, w_type, method)
                 elif flags & METH_STATIC:
                     w_func = PyCFunction_NewEx(space, method, None, None)
                     w_obj = PyStaticMethod_New(space, w_func)
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -434,10 +434,6 @@
 def PyDescr_NewWrapper(space, type, wrapper, wrapped):
     raise NotImplementedError
 
- at cpython_api([PyTypeObjectPtr, PyMethodDef], PyObject)
-def PyDescr_NewClassMethod(space, type, method):
-    raise NotImplementedError
-
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyDescr_IsData(space, descr):
     """Return true if the descriptor objects descr describes a data attribute, or
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -72,6 +72,13 @@
 }
 
 static PyObject *
+foo_classmeth(PyObject *cls)
+{
+    Py_INCREF(cls);
+    return cls;
+}
+
+static PyObject *
 foo_unset(fooobject *self)
 {
     self->foo_string = NULL;
@@ -82,6 +89,7 @@
 static PyMethodDef foo_methods[] = {
     {"copy",      (PyCFunction)foo_copy,      METH_NOARGS,  NULL},
     {"create",    (PyCFunction)foo_create,    METH_NOARGS|METH_STATIC,  NULL},
+    {"classmeth", (PyCFunction)foo_classmeth, METH_NOARGS|METH_CLASS,  NULL},
     {"unset_string_member", (PyCFunction)foo_unset, METH_NOARGS, NULL},
     {NULL, NULL}                 /* sentinel */
 };
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
@@ -117,6 +117,11 @@
         obj2 = obj.create()
         assert obj2.foo == 42
 
+    def test_classmethod(self):
+        module = self.import_module(name="foo")
+        obj = module.fooType.classmeth()
+        assert obj is module.fooType
+
     def test_new(self):
         module = self.import_module(name='foo')
         obj = module.new()


More information about the pypy-commit mailing list