summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBarry Warsaw <barry@python.org>2001-01-19 19:53:29 (GMT)
committerBarry Warsaw <barry@python.org>2001-01-19 19:53:29 (GMT)
commit0395fdd3a9249356f6efde5f8a5313df2d0c2b8c (patch)
tree912fe291023f2ac29672d0a74106327d26f21124
parent7c1e4bbe25d3f9d044daa7f24b182e8e40112ad3 (diff)
downloadcpython-0395fdd3a9249356f6efde5f8a5313df2d0c2b8c.zip
cpython-0395fdd3a9249356f6efde5f8a5313df2d0c2b8c.tar.gz
cpython-0395fdd3a9249356f6efde5f8a5313df2d0c2b8c.tar.bz2
Application and elaboration of patch #103305 to fix core dumps when
del'ing func.func_dict. I took the opportunity to also clean up some other nits with the code, namely core dumps when del'ing func_defaults and KeyError instead of AttributeError when del'ing a non-existant function attribute. Specifically, func_memberlist: Move func_dict and __dict__ into here instead of special casing them in the setattro and getattro methods. I don't remember why I took them out of here before I first uploaded the PEP 232 patch. :/ func_getattro(): No need to special case __dict__/func_dict since their now in the func_memberlist and PyMember_Get() should Do The Right Thing (i.e. transforms NULL values into Py_None). func_setattro(): Document the intended behavior of del'ing or setting to None one of the special func_* attributes. I.e.: func_code - can only be set to a code object. It can't be del'd or set to None. func_defaults - can be del'd. Can only be set to None or a tuple. func_dict - can be del'd. Can only be set to None or a dictionary. Fix core dumps and incorrect exceptions as described above. Also, if we're del'ing an arbitrary function attribute but func_dict is NULL, don't create func_dict before discovering that we'll get an AttributeError anyway.
-rw-r--r--Objects/funcobject.c70
1 files changed, 40 insertions, 30 deletions
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 027e97f..c45f318 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -94,14 +94,16 @@ PyFunction_SetDefaults(PyObject *op, PyObject *defaults)
#define OFF(x) offsetof(PyFunctionObject, x)
static struct memberlist func_memberlist[] = {
- {"func_code", T_OBJECT, OFF(func_code)},
- {"func_globals",T_OBJECT, OFF(func_globals), READONLY},
- {"func_name", T_OBJECT, OFF(func_name), READONLY},
- {"__name__", T_OBJECT, OFF(func_name), READONLY},
- {"func_defaults",T_OBJECT, OFF(func_defaults)},
- {"func_doc", T_OBJECT, OFF(func_doc)},
- {"__doc__", T_OBJECT, OFF(func_doc)},
- {NULL} /* Sentinel */
+ {"func_code", T_OBJECT, OFF(func_code)},
+ {"func_globals", T_OBJECT, OFF(func_globals), READONLY},
+ {"func_name", T_OBJECT, OFF(func_name), READONLY},
+ {"__name__", T_OBJECT, OFF(func_name), 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 *
@@ -116,16 +118,6 @@ func_getattro(PyFunctionObject *op, PyObject *name)
return NULL;
}
- if (!strcmp(sname, "__dict__") || !strcmp(sname, "func_dict")) {
- if (op->func_dict == NULL)
- rtn = Py_None;
- else
- rtn = op->func_dict;
-
- Py_INCREF(rtn);
- return rtn;
- }
-
/* no API for PyMember_HasAttr() */
rtn = PyMember_Get((char *)op, func_memberlist, sname);
@@ -153,6 +145,9 @@ func_setattro(PyFunctionObject *op, PyObject *name, PyObject *value)
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,
@@ -161,40 +156,55 @@ func_setattro(PyFunctionObject *op, PyObject *name, PyObject *value)
}
}
else if (strcmp(sname, "func_defaults") == 0) {
- if (value != Py_None && !PyTuple_Check(value)) {
+ /* 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;
}
- if (value == Py_None)
- value = NULL;
}
else if (!strcmp(sname, "func_dict") || !strcmp(sname, "__dict__")) {
- if (value != Py_None && !PyDict_Check(value)) {
+ /* legal to del f.func_dict. Can only set func_dict to
+ * NULL or a dictionary.
+ */
+ if (value == Py_None)
+ value = NULL;
+ if (value != NULL && !PyDict_Check(value)) {
PyErr_SetString(
PyExc_TypeError,
"func_dict must be set to a dict object");
return -1;
}
- if (value == Py_None)
- value = NULL;
-
- Py_XDECREF(op->func_dict);
- Py_XINCREF(value);
- op->func_dict = value;
- return 0;
}
rtn = PyMember_Set((char *)op, func_memberlist, sname, value);
if (rtn < 0 && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
if (op->func_dict == NULL) {
+ /* don't create the dict if we're deleting an
+ * attribute. In that case, we know we'll get an
+ * AttributeError.
+ */
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError, sname);
+ return -1;
+ }
op->func_dict = PyDict_New();
if (op->func_dict == NULL)
return -1;
}
- rtn = PyDict_SetItem(op->func_dict, name, value);
+ if (value == NULL)
+ rtn = PyDict_DelItem(op->func_dict, name);
+ else
+ rtn = PyDict_SetItem(op->func_dict, name, value);
+ /* transform KeyError into AttributeError */
+ if (rtn < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_AttributeError, sname);
}
return rtn;
}