Skip to content

Commit

Permalink
Break up a circular dependency on shutdown
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Nov 5, 2011
1 parent 11c7b1d commit 7f4c12b
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Relase date to be decided, codename to be chosen.
URL rules specific to a given HTTP method.
- Logger now only returns the debug log setting if it was not set
explicitly.
- Unregister a circular dependency between the WSGI environment and
the request object when shutting down the request. This means that
environ ``werkzeug.request`` will be `None` after the response was
returned to the WSGI server but has the advantage that the garbage
collector is not needed on CPython to tear down the request unless
the user created circular dependencies themselves.

Version 0.8.1
-------------
Expand Down
4 changes: 4 additions & 0 deletions flask/ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ def pop(self):
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
% (rv, self)

# get rid of circular dependencies at the end of the request
# so that we don't require the GC to be active.
rv.request.environ['werkzeug.request'] = None

def __enter__(self):
self.push()
return self
Expand Down
83 changes: 83 additions & 0 deletions flask/testsuite/regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
"""
flask.testsuite.regression
~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests regressions.
:copyright: (c) 2011 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""

from __future__ import with_statement

import gc
import sys
import flask
import threading
import unittest
from werkzeug.test import run_wsgi_app, create_environ
from flask.testsuite import FlaskTestCase


_gc_lock = threading.Lock()


class _NoLeakAsserter(object):

def __init__(self, testcase):
self.testcase = testcase

def __enter__(self):
gc.disable()
_gc_lock.acquire()
loc = flask._request_ctx_stack._local

# Force Python to track this dictionary at all times.
# This is necessary since Python only starts tracking
# dicts if they contain mutable objects. It's a horrible,
# horrible hack but makes this kinda testable.
loc.__storage__['FOOO'] = [1, 2, 3]

gc.collect()
self.old_objects = len(gc.get_objects())

def __exit__(self, exc_type, exc_value, tb):
if not hasattr(sys, 'getrefcount'):
gc.collect()
new_objects = len(gc.get_objects())
if new_objects > self.old_objects:
self.testcase.fail('Example code leaked')
_gc_lock.release()
gc.enable()


class MemoryTestCase(FlaskTestCase):

def assert_no_leak(self):
return _NoLeakAsserter(self)

def test_memory_consumption(self):
app = flask.Flask(__name__)
@app.route('/')
def index():
return flask.render_template('simple_template.html', whiskey=42)

def fire():
with app.test_client() as c:
rv = c.get('/')
self.assert_equal(rv.status_code, 200)
self.assert_equal(rv.data, '<h1>42</h1>')

# Trigger caches
fire()

with self.assert_no_leak():
for x in xrange(10):
fire()


def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MemoryTestCase))
return suite

0 comments on commit 7f4c12b

Please sign in to comment.