diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d5c1784185..566e911952 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -292,3 +292,5 @@ Contributors - Mikko Ohtamaa, 2016/12/6 - Martin Frlin, 2016/12/7 + +- Kirill Kuzminykh, 2017/03/01 diff --git a/pyramid/registry.py b/pyramid/registry.py index 20b3643e92..7589dfcac7 100644 --- a/pyramid/registry.py +++ b/pyramid/registry.py @@ -276,7 +276,9 @@ def __init__(self, func): @reify def value(self): - return self.func() + result = self.func() + del self.func + return result def resolve(self): return self.value diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index c2786c391c..b845947d19 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import datetime +import gc import locale import os import unittest @@ -8,6 +9,7 @@ from pyramid.wsgi import wsgiapp from pyramid.view import view_config from pyramid.static import static_view +from pyramid.testing import skip_on from pyramid.compat import ( text_, url_quote, @@ -741,3 +743,29 @@ def _assertBody(body, filename): data = data.replace(b'\r', b'') data = data.replace(b'\n', b'') assert(body == data) + + +class MemoryLeaksTest(unittest.TestCase): + + def tearDown(self): + import pyramid.config + pyramid.config.global_registries.empty() + + def get_gc_count(self): + last_collected = 0 + while True: + collected = gc.collect() + if collected == last_collected: + break + last_collected = collected + return len(gc.get_objects()) + + @skip_on('pypy') + def test_memory_leaks(self): + from pyramid.config import Configurator + Configurator().make_wsgi_app() # Initialize all global objects + + initial_count = self.get_gc_count() + Configurator().make_wsgi_app() + current_count = self.get_gc_count() + self.assertEqual(current_count, initial_count) diff --git a/pyramid/util.py b/pyramid/util.py index 3337d410db..2827884a34 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -231,17 +231,20 @@ def add(self, item): self._order.remove(oid) self._order.append(oid) return - ref = weakref.ref(item, lambda x: self.remove(item)) + ref = weakref.ref(item, lambda x: self._remove_by_id(oid)) self._items[oid] = ref self._order.append(oid) - def remove(self, item): + def _remove_by_id(self, oid): """ Remove an item from the set.""" - oid = id(item) if oid in self._items: del self._items[oid] self._order.remove(oid) + def remove(self, item): + """ Remove an item from the set.""" + self._remove_by_id(id(item)) + def empty(self): """ Clear all objects from the set.""" self._items = {}