diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2003-05-22 18:11:20 +0000 |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2003-05-22 18:11:20 +0000 |
commit | 9b707ced5ead7d9d1969b036b4a39c058b72c833 (patch) | |
tree | a714c83a4dea55643914a2ffc532bdb68ba036ca | |
parent | 5a3d1bb547aefca348ad105c6d441e935559a301 (diff) | |
download | cpython-9b707ced5ead7d9d1969b036b4a39c058b72c833.tar.gz |
Backport fix for SF bug 692776.
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.
-rw-r--r-- | Lib/test/test_new.py | 24 | ||||
-rw-r--r-- | Modules/newmodule.c | 51 | ||||
-rw-r--r-- | Objects/funcobject.c | 107 |
3 files changed, 135 insertions, 47 deletions
diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py index 641d9c2b0d..eb762f7dec 100644 --- a/Lib/test/test_new.py +++ b/Lib/test/test_new.py @@ -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() diff --git a/Modules/newmodule.c b/Modules/newmodule.c index 0a48926da6..673020aae9 100644 --- a/Modules/newmodule.c +++ b/Modules/newmodule.c @@ -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); } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e05a93afd6..0952d348f4 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -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 */ }; |