summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-07-19 23:45:57 +0000
committerChris McDonough <chrism@agendaless.com>2008-07-19 23:45:57 +0000
commit95b14bdd6c911c584d81a33e5ef42a5d67efdfe8 (patch)
treedf4cab465f8eaffa409e4df6c365be0ce2ff0bfa
parentab8048ab0ec363f552320280743323b1fd21ae3f (diff)
downloadpyramid-95b14bdd6c911c584d81a33e5ef42a5d67efdfe8.tar.gz
pyramid-95b14bdd6c911c584d81a33e5ef42a5d67efdfe8.tar.bz2
pyramid-95b14bdd6c911c584d81a33e5ef42a5d67efdfe8.zip
Add wsgiapp decorator.
-rw-r--r--CHANGES.txt2
-rw-r--r--docs/index.rst1
-rw-r--r--repoze/bfg/tests/test_wsgi.py33
-rw-r--r--repoze/bfg/wsgi.py44
4 files changed, 80 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 43eeb0689..3fa06320d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,7 @@
After 0.1
+ - Add wsgiapp decorator.
+
- The concept of "view factories" was removed in favor of always
calling a view, which is a callable that returns a response
directly (as opposed to returning a view). As a result, the
diff --git a/docs/index.rst b/docs/index.rst
index 380f9128a..ceb2e5fd9 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -35,6 +35,7 @@ and run a web application.
api/router
api/security
api/template
+ api/wsgi
Indices and tables
==================
diff --git a/repoze/bfg/tests/test_wsgi.py b/repoze/bfg/tests/test_wsgi.py
new file mode 100644
index 000000000..d905350d6
--- /dev/null
+++ b/repoze/bfg/tests/test_wsgi.py
@@ -0,0 +1,33 @@
+import unittest
+
+class WSGIAppTests(unittest.TestCase):
+ def test_decorator(self):
+ body = 'Unauthorized'
+ headerlist = [ ('Content-Type', 'text/plain'),
+ ('Content-Length', len(body)) ]
+ status = '401 Unauthorized'
+ def real_wsgiapp(environ, start_response):
+ start_response(status, headerlist)
+ return [body]
+ from repoze.bfg.wsgi import wsgiapp
+ wrapped = wsgiapp(real_wsgiapp)
+ context = DummyContext()
+ request = DummyRequest({})
+ response = wrapped(context, request)
+ self.assertEqual(response.status, status)
+ self.assertEqual(response.headerlist, headerlist)
+ self.assertEqual(response.app_iter, [body])
+
+class DummyContext:
+ pass
+
+class DummyRequest:
+ def __init__(self, environ):
+ self.environ = environ
+
+
+
+
+
+
+
diff --git a/repoze/bfg/wsgi.py b/repoze/bfg/wsgi.py
new file mode 100644
index 000000000..4efe40afc
--- /dev/null
+++ b/repoze/bfg/wsgi.py
@@ -0,0 +1,44 @@
+from webob import Response
+
+def wsgiapp(wrapped):
+ """ Decorator to turn a WSGI application into a repoze.bfg view callable.
+
+ E.g.::
+
+ @wsgiapp
+ def hello_world(environ, start_response):
+ body = 'Hello world'
+ start_response('200 OK', [ ('Content-Type', 'text/plain'),
+ ('Content-Length', len(body)) ] )
+ return [body]
+
+ Allows the following view declaration to be made::
+
+ <bfg:view
+ view=".views.hello_world"
+ name="hello_world.txt"
+ context="*"
+ />
+
+ The wsgiapp decorator will convert the result of the WSGI
+ application to a Response and return it to repoze.bfg as if the
+ WSGI app were a repoze.bfg view.
+ """
+ def _curried(context, request):
+ caught = []
+ def catch_start_response(status, headers, exc_info=None):
+ caught[:] = (status, headers, exc_info)
+ environ = request.environ
+ body = wrapped(environ, catch_start_response)
+ if caught:
+ status, headers, exc_info = caught
+ response = Response()
+ response.app_iter = body
+ response.status = status
+ response.headerlist = headers
+ return response
+ else:
+ raise RuntimeError('WSGI start_response not called')
+ _curried.__name__ = wrapped.__name__
+ return _curried
+