Binding PyCFunction type.
authorRobert Bradshaw <robertwb@math.washington.edu>
Sun, 31 Jan 2010 09:42:42 +0000 (01:42 -0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sun, 31 Jan 2010 09:42:42 +0000 (01:42 -0800)
This is a bit hackish, but creating a wrapper type would be as well, but
would also have a performance penalty.

Cython/Compiler/ExprNodes.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Naming.py
Cython/Compiler/Nodes.py

index ec82051107cb49b93653c063912e7613ee2498ab..cf3edfa7220838aef111c764beb2ceb48b7af14c 100644 (file)
@@ -4053,15 +4053,18 @@ class PyCFunctionNode(ExprNode):
     #
     #  pymethdef_cname   string   PyMethodDef structure
     #  self_object       ExprNode or None
+    #  binding           bool
 
     subexprs = []
     self_object = None
+    binding = False
     
     type = py_object_type
     is_temp = 1
     
     def analyse_types(self, env):
-        pass
+        if self.binding:
+            env.use_utility_code(binding_cfunc_utility_code)
     
     gil_message = "Constructing Python function"
 
@@ -4073,9 +4076,14 @@ class PyCFunctionNode(ExprNode):
         return self_result
 
     def generate_result_code(self, code):
+        if self.binding:
+            constructor = "%s_New" % Naming.binding_cfunc
+        else:
+            constructor = "PyCFunction_New"
         code.putln(
-            "%s = PyCFunction_New(&%s, %s); %s" % (
+            "%s = %s(&%s, %s); %s" % (
                 self.result(),
+                constructor,
                 self.pymethdef_cname,
                 self.self_result_code(),
                 code.error_goto_if_null(self.result(), self.pos)))
@@ -4084,6 +4092,8 @@ class PyCFunctionNode(ExprNode):
 class InnerFunctionNode(PyCFunctionNode):
     # Special PyCFunctionNode that depends on a closure class
     #
+    binding = True
+    
     def self_result_code(self):
         return "((PyObject*)%s)" % Naming.cur_scope_cname
 
@@ -6652,3 +6662,62 @@ proto="""
 #define UNARY_NEG_WOULD_OVERFLOW(x)    \
        (((x) < 0) & ((unsigned long)(x) == 0-(unsigned long)(x)))
 """)
+
+
+binding_cfunc_utility_code = UtilityCode(
+proto="""
+#define %(binding_cfunc)s_USED 1
+
+typedef struct {
+    PyCFunctionObject func;
+} %(binding_cfunc)s_object;
+
+PyTypeObject %(binding_cfunc)s_type;
+PyTypeObject *%(binding_cfunc)s = NULL;
+
+PyObject *%(binding_cfunc)s_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module); /* proto */
+#define %(binding_cfunc)s_New(ml, self) %(binding_cfunc)s_NewEx(ml, self, NULL)
+
+int %(binding_cfunc)s_init(void); /* proto */
+""" % Naming.__dict__,
+impl="""
+
+PyObject *%(binding_cfunc)s_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) {
+       %(binding_cfunc)s_object *op = PyObject_GC_New(%(binding_cfunc)s_object, %(binding_cfunc)s);
+    if (op == NULL)
+        return NULL;
+       op->func.m_ml = ml;
+       Py_XINCREF(self);
+       op->func.m_self = self;
+       Py_XINCREF(module);
+       op->func.m_module = module;
+       _PyObject_GC_TRACK(op);
+       return (PyObject *)op;
+}
+
+static void %(binding_cfunc)s_dealloc(%(binding_cfunc)s_object *m) {
+       _PyObject_GC_UNTRACK(m);
+       Py_XDECREF(m->func.m_self);
+       Py_XDECREF(m->func.m_module);
+    PyObject_GC_Del(m);
+}
+
+static PyObject *%(binding_cfunc)s_descr_get(PyObject *func, PyObject *obj, PyObject *type) {
+       if (obj == Py_None)
+               obj = NULL;
+       return PyMethod_New(func, obj, type);
+}
+
+int %(binding_cfunc)s_init(void) {
+    %(binding_cfunc)s_type = PyCFunction_Type;
+    %(binding_cfunc)s_type.tp_name = "cython_binding_builtin_function_or_method";
+    %(binding_cfunc)s_type.tp_dealloc = (destructor)%(binding_cfunc)s_dealloc;
+    %(binding_cfunc)s_type.tp_descr_get = %(binding_cfunc)s_descr_get;
+    if (PyType_Ready(&%(binding_cfunc)s_type) < 0) {
+        return -1;
+    }
+    %(binding_cfunc)s = &%(binding_cfunc)s_type;
+    return 0;
+
+}
+""" % Naming.__dict__)
index a716d4311c43e4473bca8cf0fe00c954b1f47c5d..fd00e831064f2e83f55926b5fd85ebe883a22a9b 100644 (file)
@@ -1674,6 +1674,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         code.putln("#else");
         code.putln("%s = PyBytes_FromStringAndSize(\"\", 0); %s" % (Naming.empty_bytes, code.error_goto_if_null(Naming.empty_bytes, self.pos)));
         code.putln("#endif");
+        
+        code.putln("#ifdef %s_USED" % Naming.binding_cfunc)
+        code.putln("if (%s_init() < 0) %s" % (Naming.binding_cfunc, code.error_goto(self.pos)))
+        code.putln("#endif")
 
         code.putln("/*--- Library function declarations ---*/")
         env.generate_library_function_declarations(code)
index f9f710f1c71c96cf8b89d9de568e057543ba99f9..2967ceba1747d19c4c035fcabec6cb5bb3572f8a 100644 (file)
@@ -89,6 +89,7 @@ cur_scope_cname  = pyrex_prefix + "cur_scope"
 enc_scope_cname  = pyrex_prefix + "enc_scope"
 frame_cname      = pyrex_prefix + "frame"
 frame_code_cname = pyrex_prefix + "frame_code"
+binding_cfunc    = pyrex_prefix + "binding_PyCFunctionType"
 
 line_c_macro = "__LINE__"
 
index a87d1af0fde21fa009dbfedd9b625a9082a5533e..3c6b16447466d02a6f7dc8ea1a1999df9e9e2330 100644 (file)
@@ -1635,6 +1635,7 @@ class DefNode(FuncDefNode):
     # name          string                 the Python name of the function
     # lambda_name   string                 the internal name of a lambda 'function'
     # decorators    [DecoratorNode]        list of decorators
+    # binding       bool                   bind like a Python function
     # args          [CArgDeclNode]         formal arguments
     # star_arg      PyArgDeclNode or None  * argument
     # starstar_arg  PyArgDeclNode or None  ** argument
@@ -1661,6 +1662,7 @@ class DefNode(FuncDefNode):
     entry = None
     acquire_gil = 0
     self_in_stararg = 0
+    binding = False
 
     def __init__(self, pos, **kwds):
         FuncDefNode.__init__(self, pos, **kwds)
@@ -1973,6 +1975,9 @@ class DefNode(FuncDefNode):
         elif env.is_closure_scope:
             rhs = ExprNodes.InnerFunctionNode(
                 self.pos, pymethdef_cname = self.entry.pymethdef_cname)
+        else:
+            rhs = ExprNodes.PyCFunctionNode(
+                self.pos, pymethdef_cname = self.entry.pymethdef_cname, binding = self.binding)
         self.assmt = SingleAssignmentNode(self.pos,
             lhs = ExprNodes.NameNode(self.pos, name = self.name),
             rhs = rhs)