From b8f81d48634ccee8a8a4089d04b81e1aadbc04fe Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 15 Apr 2006 23:27:28 +0000 Subject: [PATCH] Add missing DECREF to PyErr_WriteUnraisable(). That function reports exceptions that can't be raised any further, because (for instance) they occur in __del__ methods. The coroutine tests in test_generators was triggering this leak. Remove the leakers' testcase, and add a simpler testcase that explicitly tests this leak to test_generators. test_generators now no longer leaks at all, on my machine. This fix may also solve other leaks, but my full refleakhunting run is still busy, so who knows? --- Lib/test/leakers/test_gen1.py | 19 ------------------ Lib/test/test_generators.py | 36 +++++++++++++++++++++++++++++++++-- Python/errors.c | 1 + 3 files changed, 35 insertions(+), 21 deletions(-) delete mode 100644 Lib/test/leakers/test_gen1.py diff --git a/Lib/test/leakers/test_gen1.py b/Lib/test/leakers/test_gen1.py deleted file mode 100644 index 72f644d3453..00000000000 --- a/Lib/test/leakers/test_gen1.py +++ /dev/null @@ -1,19 +0,0 @@ -import gc - -# Taken from test_generators - -def f(): - try: - yield - except GeneratorExit: - yield "foo!" - -def inner_leak(): - g = f() - g.next() - -def leak(): - inner_leak() - gc.collect() - gc.collect() - gc.collect() diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 67781c981c4..a60a768be18 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -1745,8 +1745,40 @@ was removed. >>> leak() -There should be more test_generator-induced refleaks here, after they get -fixed. + +This test isn't really generator related, but rather exception-in-cleanup +related. The coroutine tests (above) just happen to cause an exception in +the generator's __del__ (tp_del) method. We can also test for this +explicitly, without generators. We do have to redirect stderr to avoid +printing warnings and to doublecheck that we actually tested what we wanted +to test. + +>>> import sys, StringIO +>>> old = sys.stderr +>>> try: +... sys.stderr = StringIO.StringIO() +... class Leaker: +... def __del__(self): +... raise RuntimeError +... +... l = Leaker() +... del l +... err = sys.stderr.getvalue().strip() +... err.startswith( +... "Exception exceptions.RuntimeError: RuntimeError() in <" +... ) +... err.endswith("> ignored") +... len(err.splitlines()) +... finally: +... sys.stderr = old +True +True +1 + + + +These refleak tests should perhaps be in a testfile of their own, +test_generators just happened to be the test that drew these out. """ diff --git a/Python/errors.c b/Python/errors.c index 25deaa69197..275a0656f6b 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -604,6 +604,7 @@ PyErr_WriteUnraisable(PyObject *obj) PyFile_WriteString(": ", f); PyFile_WriteObject(v, f, 0); } + Py_DECREF(moduleName); } PyFile_WriteString(" in ", f); PyFile_WriteObject(obj, f, 0);