Kirill Kuzminykh
2017-03-01 9e94bbf80320734985e786864116fa984a0ec62c
Fixed several reference cycles to prevent memory leaks. Added simple test for detect memory leaks after application closing.

Backport #2967 to 1.8-branch.
4 files modified
43 ■■■■■ changed files
CONTRIBUTORS.txt 2 ●●●●● patch | view | raw | blame | history
pyramid/registry.py 4 ●●● patch | view | raw | blame | history
pyramid/tests/test_integration.py 28 ●●●●● patch | view | raw | blame | history
pyramid/util.py 9 ●●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt
@@ -292,3 +292,5 @@
- Mikko Ohtamaa, 2016/12/6
- Martin Frlin, 2016/12/7
- Kirill Kuzminykh, 2017/03/01
pyramid/registry.py
@@ -276,7 +276,9 @@
    @reify
    def value(self):
        return self.func()
        result = self.func()
        del self.func
        return result
    def resolve(self):
        return self.value
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 @@
    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)
pyramid/util.py
@@ -231,17 +231,20 @@
            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 = {}