Browse Source

Break up a circular dependency on shutdown

pull/346/head
Armin Ronacher 13 years ago
parent
commit
7f4c12b335
  1. 6
      CHANGES
  2. 4
      flask/ctx.py
  3. 83
      flask/testsuite/regression.py

6
CHANGES

@ -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
-------------

4
flask/ctx.py

@ -150,6 +150,10 @@ class RequestContext(object):
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

83
flask/testsuite/regression.py

@ -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
Loading…
Cancel
Save