Leak pluggin', bug fixin' and better documentin'. Specifically,

module__doc__: Document the Warning subclass heirarchy.

make_class(): Added a "goto finally" so that if populate_methods()
fails, the return status will be -1 (failure) instead of 0 (success).

fini_exceptions(): When decref'ing the static pointers to the
exception classes, clear out their dictionaries too.  This breaks a
cycle from class->dict->method->class and allows the classes with
unbound methods to be reclaimed.  This plugs a large memory leak in a
common Py_Initialize()/dosomething/Py_Finalize() loop.
This commit is contained in:
Barry Warsaw 2001-01-23 16:08:34 +00:00
parent 5bb3be2ffe
commit 9667ed23c5

View file

@ -26,7 +26,7 @@
* compile-time literal concatenation. * compile-time literal concatenation.
*/ */
static char static char
module__doc__[] = module__doc__[] =
"Python's standard exception class hierarchy.\n\ "Python's standard exception class hierarchy.\n\
\n\ \n\
Before Python 1.5, the standard exceptions were all simple string objects.\n\ Before Python 1.5, the standard exceptions were all simple string objects.\n\
@ -53,51 +53,58 @@ Exception\n\
|\n\ |\n\
+-- SystemExit\n\ +-- SystemExit\n\
+-- StandardError\n\ +-- StandardError\n\
| |\n\
| +-- KeyboardInterrupt\n\
| +-- ImportError\n\
| +-- EnvironmentError\n\
| | |\n\
| | +-- IOError\n\
| | +-- OSError\n\
| | |\n\
| | +-- WindowsError\n\
| |\n\
| +-- EOFError\n\
| +-- RuntimeError\n\
| | |\n\
| | +-- NotImplementedError\n\
| |\n\
| +-- NameError\n\
| | |\n\
| | +-- UnboundLocalError\n\
| |\n\
| +-- AttributeError\n\
| +-- SyntaxError\n\
| | |\n\
| | +-- IndentationError\n\
| | |\n\
| | +-- TabError\n\
| |\n\
| +-- TypeError\n\
| +-- AssertionError\n\
| +-- LookupError\n\
| | |\n\
| | +-- IndexError\n\
| | +-- KeyError\n\
| |\n\
| +-- ArithmeticError\n\
| | |\n\
| | +-- OverflowError\n\
| | +-- ZeroDivisionError\n\
| | +-- FloatingPointError\n\
| |\n\
| +-- ValueError\n\
| | |\n\
| | +-- UnicodeError\n\
| |\n\
| +-- SystemError\n\
| +-- MemoryError\n\
|\n\
+---Warning\n\
|\n\ |\n\
+-- KeyboardInterrupt\n\ +-- UserWarning\n\
+-- ImportError\n\ +-- DeprecationWarning\n\
+-- EnvironmentError\n\ +-- SyntaxWarning\n\
| |\n\ +-- RuntimeWarning";
| +-- IOError\n\
| +-- OSError\n\
| |\n\
| +-- WindowsError\n\
|\n\
+-- EOFError\n\
+-- RuntimeError\n\
| |\n\
| +-- NotImplementedError\n\
|\n\
+-- NameError\n\
| |\n\
| +-- UnboundLocalError\n\
|\n\
+-- AttributeError\n\
+-- SyntaxError\n\
| |\n\
| +-- IndentationError\n\
| |\n\
| +-- TabError\n\
|\n\
+-- TypeError\n\
+-- AssertionError\n\
+-- LookupError\n\
| |\n\
| +-- IndexError\n\
| +-- KeyError\n\
|\n\
+-- ArithmeticError\n\
| |\n\
| +-- OverflowError\n\
| +-- ZeroDivisionError\n\
| +-- FloatingPointError\n\
|\n\
+-- ValueError\n\
| |\n\
| +-- UnicodeError\n\
|\n\
+-- SystemError\n\
+-- MemoryError";
/* Helper function for populating a dictionary with method wrappers. */ /* Helper function for populating a dictionary with method wrappers. */
@ -121,7 +128,7 @@ populate_methods(PyObject *klass, PyObject *dict, PyMethodDef *methods)
Py_DECREF(func); Py_DECREF(func);
return -1; return -1;
} }
/* add method to dictionary */ /* add method to dictionary */
status = PyDict_SetItemString(dict, methods->ml_name, meth); status = PyDict_SetItemString(dict, methods->ml_name, meth);
Py_DECREF(meth); Py_DECREF(meth);
@ -136,7 +143,7 @@ populate_methods(PyObject *klass, PyObject *dict, PyMethodDef *methods)
return 0; return 0;
} }
/* This function is used to create all subsequent exception classes. */ /* This function is used to create all subsequent exception classes. */
static int static int
@ -168,6 +175,7 @@ make_class(PyObject **klass, PyObject *base,
if (populate_methods(*klass, dict, methods)) { if (populate_methods(*klass, dict, methods)) {
Py_DECREF(*klass); Py_DECREF(*klass);
*klass = NULL; *klass = NULL;
goto finally;
} }
status = 0; status = 0;
@ -333,7 +341,7 @@ make_Exception(char *modulename)
if (!(name = PyString_FromString("Exception"))) if (!(name = PyString_FromString("Exception")))
goto finally; goto finally;
if (!(PyExc_Exception = PyClass_New(NULL, dict, name))) if (!(PyExc_Exception = PyClass_New(NULL, dict, name)))
goto finally; goto finally;
@ -379,7 +387,7 @@ SystemExit__init__(PyObject *self, PyObject *args)
/* Set args attribute. */ /* Set args attribute. */
if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args))))
return NULL; return NULL;
status = PyObject_SetAttrString(self, "args", args); status = PyObject_SetAttrString(self, "args", args);
if (status < 0) { if (status < 0) {
Py_DECREF(args); Py_DECREF(args);
@ -461,9 +469,9 @@ EnvironmentError__init__(PyObject *self, PyObject *args)
* of the os module functions, PyErr_SetFromErrnoWithFilename() is * of the os module functions, PyErr_SetFromErrnoWithFilename() is
* called, giving a third argument which is the filename. But, so * called, giving a third argument which is the filename. But, so
* that old code using in-place unpacking doesn't break, e.g.: * that old code using in-place unpacking doesn't break, e.g.:
* *
* except IOError, (errno, strerror): * except IOError, (errno, strerror):
* *
* we hack args so that it only contains two items. This also * we hack args so that it only contains two items. This also
* means we need our own __str__() which prints out the filename * means we need our own __str__() which prints out the filename
* when it was supplied. * when it was supplied.
@ -473,7 +481,7 @@ EnvironmentError__init__(PyObject *self, PyObject *args)
item2 = PySequence_GetItem(args, 2); item2 = PySequence_GetItem(args, 2);
if (!item0 || !item1 || !item2) if (!item0 || !item1 || !item2)
goto finally; goto finally;
if (PyObject_SetAttrString(self, "errno", item0) || if (PyObject_SetAttrString(self, "errno", item0) ||
PyObject_SetAttrString(self, "strerror", item1) || PyObject_SetAttrString(self, "strerror", item1) ||
PyObject_SetAttrString(self, "filename", item2)) PyObject_SetAttrString(self, "filename", item2))
@ -494,7 +502,7 @@ EnvironmentError__init__(PyObject *self, PyObject *args)
item1 = PySequence_GetItem(args, 1); item1 = PySequence_GetItem(args, 1);
if (!item0 || !item1) if (!item0 || !item1)
goto finally; goto finally;
if (PyObject_SetAttrString(self, "errno", item0) || if (PyObject_SetAttrString(self, "errno", item0) ||
PyObject_SetAttrString(self, "strerror", item1)) PyObject_SetAttrString(self, "strerror", item1))
{ {
@ -527,7 +535,7 @@ EnvironmentError__str__(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "O:__str__", &self)) if (!PyArg_ParseTuple(args, "O:__str__", &self))
return NULL; return NULL;
filename = PyObject_GetAttrString(self, "filename"); filename = PyObject_GetAttrString(self, "filename");
serrno = PyObject_GetAttrString(self, "errno"); serrno = PyObject_GetAttrString(self, "errno");
strerror = PyObject_GetAttrString(self, "strerror"); strerror = PyObject_GetAttrString(self, "strerror");
@ -570,7 +578,7 @@ EnvironmentError__str__(PyObject *self, PyObject *args)
PyTuple_SET_ITEM(tuple, 0, serrno); PyTuple_SET_ITEM(tuple, 0, serrno);
PyTuple_SET_ITEM(tuple, 1, strerror); PyTuple_SET_ITEM(tuple, 1, strerror);
rtnval = PyString_Format(fmt, tuple); rtnval = PyString_Format(fmt, tuple);
Py_DECREF(fmt); Py_DECREF(fmt);
@ -1063,7 +1071,7 @@ init_exceptions(void)
{ {
Py_FatalError("Base class `Exception' could not be created."); Py_FatalError("Base class `Exception' could not be created.");
} }
/* Now we can programmatically create all the remaining exceptions. /* Now we can programmatically create all the remaining exceptions.
* Remember to start the loop at 1 to skip Exceptions. * Remember to start the loop at 1 to skip Exceptions.
*/ */
@ -1132,6 +1140,14 @@ fini_exceptions(void)
PyExc_MemoryErrorInst = NULL; PyExc_MemoryErrorInst = NULL;
for (i=0; exctable[i].name; i++) { for (i=0; exctable[i].name; i++) {
/* clear the class's dictionary, freeing up circular references
* between the class and its methods.
*/
PyObject* cdict = PyObject_GetAttrString(*exctable[i].exc, "__dict__");
PyDict_Clear(cdict);
Py_DECREF(cdict);
/* Now decref the exception class */
Py_XDECREF(*exctable[i].exc); Py_XDECREF(*exctable[i].exc);
*exctable[i].exc = NULL; *exctable[i].exc = NULL;
} }