summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--raven/middleware.py47
-rw-r--r--tests/middleware/tests.py34
2 files changed, 62 insertions, 19 deletions
diff --git a/raven/middleware.py b/raven/middleware.py
index 588bd6f..4e81a45 100644
--- a/raven/middleware.py
+++ b/raven/middleware.py
@@ -9,6 +9,37 @@ from __future__ import absolute_import
from raven.utils.wsgi import (
get_current_url, get_headers, get_environ)
+from raven.utils.six import Iterator, next
+
+
+class ClosingIterator(Iterator):
+ """
+ An iterator that is implements a ``close`` method as-per
+ WSGI recommendation.
+ """
+ def __init__(self, sentry, iterable, environ):
+ self.sentry = sentry
+ self.environ = environ
+ self.iterable = iter(iterable)
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ try:
+ return next(self.iterable)
+ except StopIteration:
+ # propagate up the normal StopIteration
+ raise
+ except Exception:
+ # but capture any other exception, then re-raise
+ self.sentry.handle_exception(self.environ)
+ raise
+
+ def close(self):
+ self.sentry.client.context.clear()
+ if hasattr(self.iterable, 'close') and callable(self.iterable.close):
+ self.iterable.close()
class Sentry(object):
@@ -37,21 +68,7 @@ class Sentry(object):
self.handle_exception(environ)
raise
- try:
- for event in iterable:
- yield event
- except Exception:
- self.handle_exception(environ)
- raise
- finally:
- # wsgi spec requires iterable to call close if it exists
- # see http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html
- if iterable and hasattr(iterable, 'close') and callable(iterable.close):
- try:
- iterable.close()
- except Exception:
- self.handle_exception(environ)
- self.client.context.clear()
+ return ClosingIterator(self, iterable, environ)
def get_http_context(self, environ):
return {
diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py
index 23e94ca..2a0beb7 100644
--- a/tests/middleware/tests.py
+++ b/tests/middleware/tests.py
@@ -7,6 +7,7 @@ from raven.utils.testutils import TestCase
from raven.base import Client
from raven.middleware import Sentry
+from raven.utils.six import Iterator, next
class TempStoreClient(Client):
@@ -21,17 +22,35 @@ class TempStoreClient(Client):
self.events.append(kwargs)
-class ErroringIterable(object):
+class ErroringIterable(Iterator):
def __init__(self):
self.closed = False
def __iter__(self):
+ return self
+
+ def __next__(self):
raise ValueError('hello world')
def close(self):
self.closed = True
+class SimpleIteratable(Iterator):
+ def __init__(self):
+ self.closed = False
+ self._iter = iter(['a'])
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return next(self._iter)
+
+ def close(self):
+ self.closed = True
+
+
class ExampleApp(object):
def __init__(self, iterable):
self.iterable = iterable
@@ -59,9 +78,6 @@ class MiddlewareTestCase(TestCase):
with self.assertRaises(ValueError):
response = list(response)
- # TODO: this should be a separate test
- self.assertTrue(iterable.closed, True)
-
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
@@ -86,3 +102,13 @@ class MiddlewareTestCase(TestCase):
self.assertEquals(env['SERVER_NAME'], 'localhost')
self.assertTrue('SERVER_PORT' in env, env.keys())
self.assertEquals(env['SERVER_PORT'], '80')
+
+ def test_close(self):
+ iterable = SimpleIteratable()
+ app = ExampleApp(iterable)
+ middleware = Sentry(app, client=self.client)
+
+ response = middleware(self.request.environ, lambda *args: None)
+ list(response) # exhaust iterator
+ response.close()
+ self.assertTrue(iterable.closed, True)