]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport fix for SF bug 692776.
authorJeremy Hylton <jeremy@alum.mit.edu>
Thu, 22 May 2003 18:11:20 +0000 (18:11 +0000)
committerJeremy Hylton <jeremy@alum.mit.edu>
Thu, 22 May 2003 18:11:20 +0000 (18:11 +0000)
Add a tp_new slot to function objects that handles the case of a
function requiring a closure.  Put the function type in the new
module, rather than having a function new.function().  Add tests.

Lib/test/test_new.py
Modules/newmodule.c
Objects/funcobject.c

index 641d9c2b0d19097a2fb9e763ca1953dcaa16f1e0..eb762f7dec4ae19440a6cdb0ff57702079c2f732 100644 (file)
@@ -71,6 +71,30 @@ func()
 verify(g['c'] == 3,
        'Could not create a proper function object')
 
+# test the various extended flavors of function.new
+def f(x):
+    def g(y):
+        return x + y
+    return g
+g = f(4)
+new.function(f.func_code, {}, "blah")
+g2 = new.function(g.func_code, {}, "blah", (2,), g.func_closure)
+verify(g2() == 6)
+g3 = new.function(g.func_code, {}, "blah", None, g.func_closure)
+verify(g3(5) == 9)
+def test_closure(func, closure, exc):
+    try:
+        new.function(func.func_code, {}, "", None, closure)
+    except exc:
+        pass
+    else:
+        print "corrupt closure accepted"
+
+test_closure(g, None, TypeError) # invalid closure
+test_closure(g, (1,), TypeError) # non-cell in closure
+test_closure(g, (1, 1), ValueError) # closure is wrong size
+test_closure(f, g.func_closure, ValueError) # no closure needed
+
 print 'new.code()'
 # bogus test of new.code()
 # Note: Jython will never have new.code()
index 0a48926da65ef1809b3323ebfe5fcdaf77ca5cf2..673020aae92c29f6250d6ce52e6a0866e6bc178b 100644 (file)
@@ -53,48 +53,6 @@ new_instancemethod(PyObject* unused, PyObject* args)
        return PyMethod_New(func, self, classObj);
 }
 
-static char new_function_doc[] =
-"Create a function object from (CODE, GLOBALS, [NAME [, ARGDEFS]]).";
-
-static PyObject *
-new_function(PyObject* unused, PyObject* args)
-{
-       PyObject* code;
-       PyObject* globals;
-       PyObject* name = Py_None;
-       PyObject* defaults = Py_None;
-       PyFunctionObject* newfunc;
-
-       if (!PyArg_ParseTuple(args, "O!O!|OO!:function",
-                             &PyCode_Type, &code,
-                             &PyDict_Type, &globals,
-                             &name,
-                             &PyTuple_Type, &defaults))
-               return NULL;
-       if (name != Py_None && !PyString_Check(name)) {
-               PyErr_SetString(PyExc_TypeError,
-                               "arg 3 (name) must be None or string");
-               return NULL;
-       }
-
-       newfunc = (PyFunctionObject *)PyFunction_New(code, globals);
-       if (newfunc == NULL)
-               return NULL;
-
-       if (name != Py_None) {
-               Py_XINCREF(name);
-               Py_XDECREF(newfunc->func_name);
-               newfunc->func_name = name;
-       }
-       if (defaults != Py_None) {
-               Py_XINCREF(defaults);
-               Py_XDECREF(newfunc->func_defaults);
-               newfunc->func_defaults  = defaults;
-       }
-
-       return (PyObject *)newfunc;
-}
-
 static char new_code_doc[] =
 "Create a code object from (ARGCOUNT, NLOCALS, STACKSIZE, FLAGS, CODESTRING,\n"
 "CONSTANTS, NAMES, VARNAMES, FILENAME, NAME, FIRSTLINENO, LNOTAB, FREEVARS,\n"
@@ -191,8 +149,6 @@ static PyMethodDef new_methods[] = {
         METH_VARARGS, new_instance_doc},
        {"instancemethod",      new_instancemethod,     
         METH_VARARGS, new_im_doc},
-       {"function",            new_function,           
-        METH_VARARGS, new_function_doc},
        {"code",                new_code,               
         METH_VARARGS, new_code_doc},
        {"module",              new_module,             
@@ -210,6 +166,9 @@ You need to know a great deal about the interpreter to use this!";
 DL_EXPORT(void)
 initnew(void)
 {
-       Py_InitModule4("new", new_methods, new_doc, (PyObject *)NULL,
-                      PYTHON_API_VERSION);
+       PyObject *m;
+       m = Py_InitModule4("new", new_methods, new_doc, (PyObject *)NULL,
+                          PYTHON_API_VERSION);
+       if (m)
+               PyModule_AddObject(m, "function", &PyFunction_Type);
 }
index e05a93afd647fb290fcae6ebf65b14f03988313c..0952d348f4aa8b165c2007dedb1b2ae4b340021c 100644 (file)
@@ -266,6 +266,108 @@ static PyGetSetDef func_getsetlist[] = {
        {NULL} /* Sentinel */
 };
 
+static char func_doc[] =
+"function(code, globals[, name[, argdefs[, closure]]])\n\
+\n\
+Create a function object from a code object and a dictionary.\n\
+The optional name string overrides the name from the code object.\n\
+The optional argdefs tuple specifies the default argument values.\n\
+The optional closure tuple supplies the bindings for free variables.";
+
+/* func_new() maintains the following invariants for closures.  The
+   closure must correspond to the free variables of the code object.
+   
+   if len(code.co_freevars) == 0: 
+           closure = NULL
+   else:
+           len(closure) == len(code.co_freevars)
+   for every elt in closure, type(elt) == cell
+*/
+
+static PyObject *
+func_new(PyTypeObject* type, PyObject* args, PyObject* kw)
+{
+       PyCodeObject *code;
+       PyObject *globals;
+       PyObject *name = Py_None;
+       PyObject *defaults = Py_None;
+       PyObject *closure = Py_None;
+       PyFunctionObject *newfunc;
+       int nfree, nclosure;
+       static char *kwlist[] = {"code", "globals", "name",
+                                "argdefs", "closure", 0};
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "O!O!|OOO:function",
+                             kwlist,
+                             &PyCode_Type, &code,
+                             &PyDict_Type, &globals,
+                             &name, &defaults, &closure))
+               return NULL;
+       if (name != Py_None && !PyString_Check(name)) {
+               PyErr_SetString(PyExc_TypeError,
+                               "arg 3 (name) must be None or string");
+               return NULL;
+       }
+       if (defaults != Py_None && !PyTuple_Check(defaults)) {
+               PyErr_SetString(PyExc_TypeError,
+                               "arg 4 (defaults) must be None or tuple");
+               return NULL;
+       }
+       nfree = PyTuple_GET_SIZE(code->co_freevars);
+       if (!PyTuple_Check(closure)) {
+               if (nfree && closure == Py_None) {
+                       PyErr_SetString(PyExc_TypeError,
+                                       "arg 5 (closure) must be tuple");
+                       return NULL;
+               }
+               else if (closure != Py_None) {
+                       PyErr_SetString(PyExc_TypeError,
+                               "arg 5 (closure) must be None or tuple");
+                       return NULL;
+               }
+       }
+
+       /* check that the closure is well-formed */
+       nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure);
+       if (nfree != nclosure)
+               return PyErr_Format(PyExc_ValueError,
+                                   "%s requires closure of length %d, not %d",
+                                   PyString_AS_STRING(code->co_name),
+                                   nfree, nclosure);
+       if (nclosure) {
+               int i;
+               for (i = 0; i < nclosure; i++) {
+                       PyObject *o = PyTuple_GET_ITEM(closure, i);
+                       if (!PyCell_Check(o)) {
+                               return PyErr_Format(PyExc_TypeError,
+                                   "arg 5 (closure) expected cell, found %s",
+                                                   o->ob_type->tp_name);
+                       }
+               }
+       }
+       
+       newfunc = (PyFunctionObject *)PyFunction_New((PyObject *)code, 
+                                                    globals);
+       if (newfunc == NULL)
+               return NULL;
+       
+       if (name != Py_None) {
+               Py_INCREF(name);
+               Py_DECREF(newfunc->func_name);
+               newfunc->func_name = name;
+       }
+       if (defaults != Py_None) {
+               Py_INCREF(defaults);
+               newfunc->func_defaults  = defaults;
+       }
+       if (closure != Py_None) {
+               Py_INCREF(closure);
+               newfunc->func_closure = closure;
+       }
+
+       return (PyObject *)newfunc;
+}
+
 static void
 func_dealloc(PyFunctionObject *op)
 {
@@ -415,7 +517,7 @@ PyTypeObject PyFunction_Type = {
        PyObject_GenericSetAttr,                /* tp_setattro */
        0,                                      /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
-       0,                                      /* tp_doc */
+       func_doc,                               /* tp_doc */
        (traverseproc)func_traverse,            /* tp_traverse */
        0,                                      /* tp_clear */
        0,                                      /* tp_richcompare */
@@ -430,6 +532,9 @@ PyTypeObject PyFunction_Type = {
        func_descr_get,                         /* tp_descr_get */
        0,                                      /* tp_descr_set */
        offsetof(PyFunctionObject, func_dict),  /* tp_dictoffset */
+       0,                                      /* tp_init */
+       0,                                      /* tp_alloc */
+       func_new,                               /* tp_new */
 };