mirror of https://github.com/python/cpython.git
Rewrite function attributes to use the generic routines properly.
This uses the new "restricted" feature of structmember, and getset descriptors for some of the type checks.
This commit is contained in:
parent
5d1e34aa42
commit
d9d1d4ac6f
|
@ -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 @@ def temp():
|
|||
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()
|
||||
|
|
|
@ -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 PyObject *
|
||||
func_getattro(PyObject *op, PyObject *name)
|
||||
static int
|
||||
restricted(void)
|
||||
{
|
||||
char *sname = PyString_AsString(name);
|
||||
|
||||
if (sname[0] != '_' && PyEval_GetRestricted()) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"function attributes not accessible in restricted mode");
|
||||
if (!PyEval_GetRestricted())
|
||||
return 0;
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"function attributes not accessible in restricted mode");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
func_get_dict(PyFunctionObject *op)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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,
|
||||
"func_code must be set to a code object");
|
||||
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;
|
||||
}
|
||||
}
|
||||
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,
|
||||
/* 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;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return PyObject_GenericSetAttr(op, name, value);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
|
|
Loading…
Reference in New Issue