summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Hylton <jeremy@alum.mit.edu>2003-05-22 18:11:20 +0000
committerJeremy Hylton <jeremy@alum.mit.edu>2003-05-22 18:11:20 +0000
commit9b707ced5ead7d9d1969b036b4a39c058b72c833 (patch)
treea714c83a4dea55643914a2ffc532bdb68ba036ca
parent5a3d1bb547aefca348ad105c6d441e935559a301 (diff)
downloadcpython-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.py24
-rw-r--r--Modules/newmodule.c51
-rw-r--r--Objects/funcobject.c107
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 */
};