summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_funcattrs.py159
-rw-r--r--Objects/funcobject.c190
2 files changed, 276 insertions, 73 deletions
diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py
index 746c91e..f4ee329 100644
--- a/Lib/test/test_funcattrs.py
+++ b/Lib/test/test_funcattrs.py
@@ -1,4 +1,5 @@
-from test_support import verbose, TestFailed
+from test_support import verbose, TestFailed, verify
+import types
class F:
def a(self):
@@ -210,3 +211,159 @@ d[foo] = 1
foo.func_code = temp.func_code
d[foo]
+
+# Test all predefined function attributes systematically
+
+def test_func_closure():
+ a = 12
+ def f(): print a
+ c = f.func_closure
+ verify(isinstance(c, tuple))
+ verify(len(c) == 1)
+ verify(c[0].__class__.__name__ == "cell") # don't have a type object handy
+ try:
+ f.func_closure = c
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to set func_closure"
+ try:
+ del a.func_closure
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to del func_closure"
+
+def test_func_doc():
+ def f(): pass
+ verify(f.__doc__ is None)
+ verify(f.func_doc is None)
+ f.__doc__ = "hello"
+ verify(f.__doc__ == "hello")
+ verify(f.func_doc == "hello")
+ del f.__doc__
+ verify(f.__doc__ is None)
+ verify(f.func_doc is None)
+ f.func_doc = "world"
+ verify(f.__doc__ == "world")
+ verify(f.func_doc == "world")
+ del f.func_doc
+ verify(f.func_doc is None)
+ verify(f.__doc__ is None)
+
+def test_func_globals():
+ def f(): pass
+ verify(f.func_globals is globals())
+ try:
+ f.func_globals = globals()
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to set func_globals"
+ try:
+ del f.func_globals
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to del func_globals"
+
+def test_func_name():
+ def f(): pass
+ verify(f.__name__ == "f")
+ verify(f.func_name == "f")
+ try:
+ f.func_name = "f"
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to set func_name"
+ try:
+ f.__name__ = "f"
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to set __name__"
+ try:
+ del f.func_name
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to del func_name"
+ try:
+ del f.__name__
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to del __name__"
+
+def test_func_code():
+ def f(): pass
+ def g(): print 12
+ verify(type(f.func_code) is types.CodeType)
+ f.func_code = g.func_code
+ try:
+ del f.func_code
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to del func_code"
+
+def test_func_defaults():
+ def f(a, b): return (a, b)
+ verify(f.func_defaults is None)
+ f.func_defaults = (1, 2)
+ verify(f.func_defaults == (1, 2))
+ verify(f(10) == (10, 2))
+ def g(a=1, b=2): return (a, b)
+ verify(g.func_defaults == (1, 2))
+ del g.func_defaults
+ verify(g.func_defaults is None)
+ try:
+ g()
+ except TypeError:
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to call g() w/o defaults"
+
+def test_func_dict():
+ def f(): pass
+ a = f.__dict__
+ b = f.func_dict
+ verify(a == {})
+ verify(a is b)
+ f.hello = 'world'
+ verify(a == {'hello': 'world'})
+ verify(f.func_dict is a is f.__dict__)
+ f.func_dict = {}
+ try:
+ f.hello
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "hello attribute should have disappeared"
+ f.__dict__ = {'world': 'hello'}
+ verify(f.world == "hello")
+ verify(f.__dict__ is f.func_dict == {'world': 'hello'})
+ try:
+ del f.func_dict
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to delete func_dict"
+ try:
+ del f.__dict__
+ except (AttributeError, TypeError):
+ pass
+ else:
+ raise TestFailed, "shouldn't be allowed to delete __dict__"
+
+def testmore():
+ test_func_closure()
+ test_func_doc()
+ test_func_globals()
+ test_func_name()
+ test_func_code()
+ test_func_defaults()
+ test_func_dict()
+
+testmore()
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 57d02fe..91a3127 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -127,99 +127,145 @@ PyFunction_SetClosure(PyObject *op, PyObject *closure)
#define OFF(x) offsetof(PyFunctionObject, x)
+#define RR ()
+
static struct memberlist func_memberlist[] = {
- {"func_code", T_OBJECT, OFF(func_code)},
- {"func_globals", T_OBJECT, OFF(func_globals), READONLY},
+ {"func_closure", T_OBJECT, OFF(func_closure),
+ RESTRICTED|READONLY},
+ {"func_doc", T_OBJECT, OFF(func_doc), WRITE_RESTRICTED},
+ {"__doc__", T_OBJECT, OFF(func_doc), WRITE_RESTRICTED},
+ {"func_globals", T_OBJECT, OFF(func_globals),
+ RESTRICTED|READONLY},
{"func_name", T_OBJECT, OFF(func_name), READONLY},
{"__name__", T_OBJECT, OFF(func_name), READONLY},
- {"func_closure", T_OBJECT, OFF(func_closure), READONLY},
- {"func_defaults", T_OBJECT, OFF(func_defaults)},
- {"func_doc", T_OBJECT, OFF(func_doc)},
- {"__doc__", T_OBJECT, OFF(func_doc)},
- {"func_dict", T_OBJECT, OFF(func_dict)},
- {"__dict__", T_OBJECT, OFF(func_dict)},
{NULL} /* Sentinel */
};
+static int
+restricted(void)
+{
+ if (!PyEval_GetRestricted())
+ return 0;
+ PyErr_SetString(PyExc_RuntimeError,
+ "function attributes not accessible in restricted mode");
+ return 1;
+}
+
static PyObject *
-func_getattro(PyObject *op, PyObject *name)
+func_get_dict(PyFunctionObject *op)
{
- char *sname = PyString_AsString(name);
-
- if (sname[0] != '_' && PyEval_GetRestricted()) {
- PyErr_SetString(PyExc_RuntimeError,
- "function attributes not accessible in restricted mode");
+ if (restricted())
return NULL;
- }
- /* If func_dict is being accessed but no attribute has been set
- * yet, then initialize it to the empty dictionary.
- */
- if ((!strcmp(sname, "func_dict") || !strcmp(sname, "__dict__"))
- && ((PyFunctionObject*)op)->func_dict == NULL)
- {
- PyFunctionObject* funcop = (PyFunctionObject*)op;
-
- funcop->func_dict = PyDict_New();
- if (funcop->func_dict == NULL)
+ if (op->func_dict == NULL) {
+ op->func_dict = PyDict_New();
+ if (op->func_dict == NULL)
return NULL;
}
- return PyObject_GenericGetAttr(op, name);
+ Py_INCREF(op->func_dict);
+ return op->func_dict;
}
static int
-func_setattro(PyObject *op, PyObject *name, PyObject *value)
+func_set_dict(PyFunctionObject *op, PyObject *value)
{
- char *sname = PyString_AsString(name);
+ PyObject *tmp;
- if (PyEval_GetRestricted()) {
- PyErr_SetString(PyExc_RuntimeError,
- "function attributes not settable in restricted mode");
+ if (restricted())
+ return -1;
+ /* It is illegal to del f.func_dict */
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "function's dictionary may not be deleted");
+ return -1;
+ }
+ /* Can only set func_dict to a dictionary */
+ if (!PyDict_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "setting function's dictionary to a non-dict");
return -1;
}
- if (strcmp(sname, "func_code") == 0) {
- /* not legal to del f.func_code or to set it to anything
- * other than a code object.
- */
- if (value == NULL || !PyCode_Check(value)) {
- PyErr_SetString(
- PyExc_TypeError,
+ tmp = op->func_dict;
+ Py_INCREF(value);
+ op->func_dict = value;
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static PyObject *
+func_get_code(PyFunctionObject *op)
+{
+ if (restricted())
+ return NULL;
+ Py_INCREF(op->func_code);
+ return op->func_code;
+}
+
+static int
+func_set_code(PyFunctionObject *op, PyObject *value)
+{
+ PyObject *tmp;
+
+ if (restricted())
+ return -1;
+ /* Not legal to del f.func_code or to set it to anything
+ * other than a code object. */
+ if (value == NULL || !PyCode_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
"func_code must be set to a code object");
- return -1;
- }
+ return -1;
}
- else if (strcmp(sname, "func_defaults") == 0) {
- /* legal to del f.func_defaults. Can only set
- * func_defaults to NULL or a tuple.
- */
- if (value == Py_None)
- value = NULL;
- if (value != NULL && !PyTuple_Check(value)) {
- PyErr_SetString(
- PyExc_TypeError,
- "func_defaults must be set to a tuple object");
- return -1;
- }
+ tmp = op->func_code;
+ Py_INCREF(value);
+ op->func_code = value;
+ Py_DECREF(tmp);
+ return 0;
+}
+
+static PyObject *
+func_get_defaults(PyFunctionObject *op)
+{
+ if (restricted())
+ return NULL;
+ if (op->func_defaults == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
}
- else if (!strcmp(sname, "func_dict") || !strcmp(sname, "__dict__")) {
- /* It is illegal to del f.func_dict. Can only set
- * func_dict to a dictionary.
- */
- if (value == NULL) {
- PyErr_SetString(
- PyExc_TypeError,
- "function's dictionary may not be deleted");
- return -1;
- }
- if (!PyDict_Check(value)) {
- PyErr_SetString(
- PyExc_TypeError,
- "setting function's dictionary to a non-dict");
- return -1;
- }
+ Py_INCREF(op->func_defaults);
+ return op->func_defaults;
+}
+
+static int
+func_set_defaults(PyFunctionObject *op, PyObject *value)
+{
+ PyObject *tmp;
+
+ if (restricted())
+ return -1;
+ /* Legal to del f.func_defaults.
+ * Can only set func_defaults to NULL or a tuple. */
+ if (value == Py_None)
+ value = NULL;
+ if (value != NULL && !PyTuple_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "func_defaults must be set to a tuple object");
+ return -1;
}
- return PyObject_GenericSetAttr(op, name, value);
+ tmp = op->func_defaults;
+ Py_XINCREF(value);
+ op->func_defaults = value;
+ Py_XDECREF(tmp);
+ return 0;
}
+static struct getsetlist func_getsetlist[] = {
+ {"func_code", (getter)func_get_code, (setter)func_set_code},
+ {"func_defaults", (getter)func_get_defaults,
+ (setter)func_set_defaults},
+ {"func_dict", (getter)func_get_dict, (setter)func_set_dict},
+ {"__dict__", (getter)func_get_dict, (setter)func_set_dict},
+ {NULL} /* Sentinel */
+};
+
static void
func_dealloc(PyFunctionObject *op)
{
@@ -365,8 +411,8 @@ PyTypeObject PyFunction_Type = {
0, /* tp_hash */
function_call, /* tp_call */
0, /* tp_str */
- func_getattro, /* tp_getattro */
- func_setattro, /* tp_setattro */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
@@ -378,7 +424,7 @@ PyTypeObject PyFunction_Type = {
0, /* tp_iternext */
0, /* tp_methods */
func_memberlist, /* tp_members */
- 0, /* tp_getset */
+ func_getsetlist, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
func_descr_get, /* tp_descr_get */