├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.txt ├── django_wsgi.py ├── requirements.txt ├── setup.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Alex Gaynor 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of django-wsgi nor the names of its contributors may be 12 | used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ALEX GAYNOR BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | django-wsgi 2 | =========== 3 | 4 | ``django-wsgi`` is an application to make deploying Django views and URLconfs 5 | inside the WSGI world easier, as well as deploying WSGI application inside of 6 | Django URLconfs. 7 | -------------------------------------------------------------------------------- /django_wsgi.py: -------------------------------------------------------------------------------- 1 | from itertools import chain 2 | from traceback import format_exc 3 | 4 | from django.conf import settings 5 | from django.core.handlers.wsgi import WSGIRequest, STATUS_CODE_TEXT 6 | from django.core.urlresolvers import RegexURLResolver 7 | from django.http import Http404, HttpResponseNotFound, HttpResponse 8 | from django.utils.html import escape 9 | 10 | 11 | class wsgi_application(object): 12 | def __init__(self, function_or_urlconf): 13 | if not settings.configured: 14 | settings.configure() 15 | self.function_or_urlconf = function_or_urlconf 16 | 17 | def get_view(self, request): 18 | if isinstance(self.function_or_urlconf, list): 19 | return self.resolve_view(request) 20 | return self.function_or_urlconf, (), {} 21 | 22 | def resolve_view(self, request): 23 | urls = self.function_or_urlconf 24 | resolver = RegexURLResolver(r"^/", urls) 25 | return resolver.resolve(request.path_info) 26 | 27 | def __call__(self, environ, start_response): 28 | request = WSGIRequest(environ) 29 | try: 30 | view, args, kwargs = self.get_view(request) 31 | response = view(request, *args, **kwargs) 32 | except Http404: 33 | response = HttpResponseNotFound("Couldn't find %s" % escape(request.path_info)) 34 | except Exception, e: 35 | response = HttpResponse(format_exc(e), status=500, mimetype="text/plain") 36 | status_text = STATUS_CODE_TEXT.get(response.status_code, "UNKOWN STATUS CODE") 37 | status = "%s %s" % (response.status_code, status_text) 38 | response_headers = [(str(k), str(v)) for k, v in response.items()] 39 | for c in response.cookies.values(): 40 | response_headers.append(("Set-Cookie", str(c.output(header="")))) 41 | start_response(status, response_headers) 42 | return response 43 | 44 | 45 | class ClosingIterator(object): 46 | def __init__(self, iterator, close_callback): 47 | self.iterator = iter(iterator) 48 | self.close_callback = close_callback 49 | 50 | def __iter__(self): 51 | return self 52 | 53 | def next(self): 54 | return self.iterator.next() 55 | 56 | def close(self): 57 | self.close_callback() 58 | 59 | class django_view(object): 60 | def __init__(self, wsgi_app): 61 | self.wsgi_app = wsgi_app 62 | 63 | def __call__(self, request): 64 | environ = request.environ 65 | results = {} 66 | buffer = [] 67 | def start_response(status, response_headers, exc_info=None): 68 | if exc_info is not None: 69 | raise exc_info[0], exc_info[1], exc_info[2] 70 | results["status"] = status 71 | results["response_headers"] = response_headers 72 | return buffer.append 73 | response = self.wsgi_app(environ, start_response) 74 | while not results: 75 | buffer.append(response.next()) 76 | response_iter = chain(buffer, response) 77 | if hasattr(response, "close"): 78 | response_iter = ClosingIterator(response_iter, response.close) 79 | response = HttpResponse(response_iter, status=int(results["status"].split()[0])) 80 | for header, value in results["response_headers"]: 81 | response[header] = value 82 | return response 83 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e git+git://github.com/benoitc/gunicorn.git#egg=gunicorn 2 | Django 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='django-wsgi', 5 | version='0.1alpha1', 6 | description="A library for better integration between django and the WSGI world.", 7 | long_description=open('README.txt').read(), 8 | author='Alex Gaynor', 9 | author_email='alex.gaynor@gmail.com', 10 | license='BSD', 11 | url='http://github.com/alex/django-wsgi', 12 | py_modules=['django_wsgi'], 13 | classifiers=[ 14 | 'Development Status :: 3 - Alpha', 15 | 'Environment :: Web Environment', 16 | 'Intended Audience :: Developers', 17 | 'License :: OSI Approved :: MIT License', 18 | 'Operating System :: OS Independent', 19 | 'Programming Language :: Python', 20 | 'Framework :: Django', 21 | 'Topic :: Internet :: WWW/HTTP :: WSGI', 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import patterns 2 | from django.http import HttpResponse 3 | 4 | from django_wsgi import wsgi_application, django_view 5 | 6 | 7 | def test_app(request): 8 | return HttpResponse("Hello World!") 9 | 10 | def test_app2(request, name): 11 | return HttpResponse("Hello %s!" % name) 12 | 13 | def test_app3(request): 14 | return HttpResponse("wowa, meta") 15 | 16 | def test_app4(environ, start_response): 17 | start_response("200 OK", [("Content-type", "text/html")]) 18 | yield "i suck" 19 | 20 | 21 | urls = patterns("", 22 | (r"^$", test_app), 23 | (r"^meta/$", django_view(wsgi_application(test_app3))), 24 | (r"^test4/$", django_view(test_app4)), 25 | (r"^(?P.*?)/$", test_app2), 26 | ) 27 | 28 | application = wsgi_application(urls) 29 | --------------------------------------------------------------------------------