diff -r 5fd1f8271e8a Lib/test/regrtest.py --- a/Lib/test/regrtest.py Wed Aug 13 09:35:21 2014 +0300 +++ b/Lib/test/regrtest.py Wed Aug 13 17:20:15 2014 +1000 @@ -1363,6 +1363,7 @@ # This code is hackish and inelegant, but it seems to do the job. import copyreg import collections.abc + import encodings if not hasattr(sys, 'gettotalrefcount'): raise Exception("Tracking reference leaks requires a debug build " @@ -1378,6 +1379,7 @@ zdc = None # Run unmodified on platforms without zipimport support else: zdc = zipimport._zip_directory_cache.copy() + codec_cache = encodings._cache.copy() abcs = {} for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: if not isabstract(abc): @@ -1394,12 +1396,23 @@ print("beginning", repcount, "repetitions", file=sys.stderr) print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr) sys.stderr.flush() + import tracemalloc for i in range(repcount): + tracemalloc.start(5) + trace_before = tracemalloc.take_snapshot() indirect_test() - alloc_after, rc_after = dash_R_cleanup(fs, ps, pic, zdc, abcs) + alloc_after, rc_after = dash_R_cleanup(fs, ps, pic, zdc, + codec_cache, abcs) + trace_after = tracemalloc.take_snapshot() + tracemalloc.stop() + sys.stderr.write('.') sys.stderr.flush() if i >= nwarmup: + top_stats = trace_after.compare_to(trace_before, 'traceback') + print("[ Top 20 differences ]") + for stat in top_stats[:20]: + print(stat) rc_deltas[i] = rc_after - rc_before alloc_deltas[i] = alloc_after - alloc_before alloc_before, rc_before = alloc_after, rc_after @@ -1430,11 +1443,13 @@ failed = True return failed -def dash_R_cleanup(fs, ps, pic, zdc, abcs): +def dash_R_cleanup(fs, ps, pic, zdc, codec_cache, abcs): import gc, copyreg import _strptime, linecache import urllib.parse, urllib.request, mimetypes, doctest - import struct, filecmp, collections.abc + import struct, filecmp + import encodings + import collections.abc from distutils.dir_util import _path_created from weakref import WeakSet @@ -1456,6 +1471,8 @@ else: zipimport._zip_directory_cache.clear() zipimport._zip_directory_cache.update(zdc) + encodings._cache.clear() + encodings._cache.update(codec_cache) # clear type cache sys._clear_type_cache() @@ -1498,6 +1515,8 @@ func1 = sys.getallocatedblocks func2 = sys.gettotalrefcount gc.collect() + gc.collect() + gc.collect() return func1(), func2() def warm_caches(): diff -r 5fd1f8271e8a Lib/test/test_codecs.py --- a/Lib/test/test_codecs.py Wed Aug 13 09:35:21 2014 +0300 +++ b/Lib/test/test_codecs.py Wed Aug 13 17:20:15 2014 +1000 @@ -2592,17 +2592,11 @@ unique_id = repr(self) + str(id(self)) self.codec_name = encodings.normalize_encoding(unique_id).lower() - # We store the object to raise on the instance because of a bad - # interaction between the codec caching (which means we can't - # recreate the codec entry) and regrtest refleak hunting (which - # runs the same test instance multiple times). This means we - # need to ensure the codecs call back in to the instance to find - # out which exception to raise rather than binding them in a - # closure to an object that may change on the next run - self.obj_to_raise = RuntimeError - def tearDown(self): - _TEST_CODECS.pop(self.codec_name, None) + entry = _TEST_CODECS.pop(self.codec_name, None) + if entry is not None: + # Forcibly break the cycle + entry.encode = entry.decode = None def set_codec(self, encode, decode): codec_info = codecs.CodecInfo(encode, decode, @@ -2618,13 +2612,10 @@ self.assertIsInstance(caught.exception.__cause__, exc_type) self.assertIsNotNone(caught.exception.__cause__.__traceback__) - def raise_obj(self, *args, **kwds): - # Helper to dynamically change the object raised by a test codec - raise self.obj_to_raise - def check_wrapped(self, obj_to_raise, msg, exc_type=RuntimeError): - self.obj_to_raise = obj_to_raise - self.set_codec(self.raise_obj, self.raise_obj) + def raise_obj(self, *args, **kwds): + raise obj_to_raise + self.set_codec(raise_obj, raise_obj) with self.assertWrapped("encoding", exc_type, msg): "str_input".encode(self.codec_name) with self.assertWrapped("encoding", exc_type, msg): @@ -2654,6 +2645,7 @@ self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError) def check_not_wrapped(self, obj_to_raise, msg): + # Assumes obj_to_raise is a subclass or instance of RuntimeError def raise_obj(*args, **kwds): raise obj_to_raise self.set_codec(raise_obj, raise_obj)