Skip to content

Commit

Permalink
Merge pull request #2972 from mmerickel/backport-2967-to-1.7-branch
Browse files Browse the repository at this point in the history
Fixed several reference cycles to prevent memory leaks. Added simple …
  • Loading branch information
mmerickel committed Mar 4, 2017
2 parents def8703 + fb7b9bd commit 98517a9
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 8 deletions.
14 changes: 10 additions & 4 deletions CHANGES.txt
@@ -1,12 +1,18 @@
.. _changes_1.7.4:

Unreleased
==========
1.7.5 (unreleased)
==================

- HTTPException's accepts a detail kwarg that may be used to pass additional
details to the exception. You may now pass objects so long as they have a
valid __str__ method. See https://github.com/Pylons/pyramid/pull/2951

- Fix a reference cycle causing memory leaks in which the registry
would keep a ``Configurator`` instance alive even after the configurator
was discarded. Another fix was also added for the ``global_registries``
object in which the registry was stored in a closure preventing it from
being deallocated. See https://github.com/Pylons/pyramid/pull/2972

.. _changes_1.7.4:

1.7.4 (2017-01-24)
==================

Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Expand Up @@ -278,3 +278,5 @@ Contributors
- Jon Davidson, 2016/07/18

- Keith Yang, 2016/07/22

- Kirill Kuzminykh, 2017/03/01
4 changes: 3 additions & 1 deletion pyramid/registry.py
Expand Up @@ -257,7 +257,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
Expand Down
28 changes: 28 additions & 0 deletions pyramid/tests/test_integration.py
@@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-

import datetime
import gc
import locale
import os
import unittest

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,
Expand Down Expand Up @@ -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)
9 changes: 6 additions & 3 deletions pyramid/util.py
Expand Up @@ -220,17 +220,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 = {}
Expand Down

0 comments on commit 98517a9

Please sign in to comment.