Permalink
Browse files

Fixed #16360 -- Added WSGI entrypoint to startproject layout, and ena…

…bled internal servers (runserver and runfcgi) to use an externally-defined WSGI application. Thanks to Armin Ronacher, Jannis Leidel, Alex Gaynor, ptone, and Jacob Kaplan-Moss.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17022 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent dca81ad commit 145a77edc999fd5f1a53bc5bfd6b581386950074 @carljm carljm committed Oct 22, 2011
@@ -407,6 +407,13 @@
USE_X_FORWARDED_HOST = False
+# The Python dotted path to the WSGI application that Django's internal servers
+# (runserver, runfcgi) will use. If `None`, the return value of
+# 'django.core.wsgi.get_wsgi_application' is used, thus preserving the same
+# behavior as previous versions of Django. Otherwise this should point to an
+# actual WSGI application object.
+WSGI_APPLICATION = None
+
##############
# MIDDLEWARE #
##############
@@ -99,6 +99,9 @@
ROOT_URLCONF = '{{ project_name }}.urls'
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = '{{ project_name }}.wsgi.application'
+
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
@@ -0,0 +1,28 @@
+"""
+WSGI config for {{ project_name }} project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)
@@ -16,12 +16,14 @@ class Command(BaseRunserverCommand):
def get_handler(self, *args, **options):
"""
- Returns the static files serving handler.
+ Returns the static files serving handler wrapping the default handler,
+ if static files should be served. Otherwise just returns the default
+ handler.
+
"""
handler = super(Command, self).get_handler(*args, **options)
use_static_handler = options.get('use_static_handler', True)
insecure_serving = options.get('insecure_serving', False)
- if (settings.DEBUG and use_static_handler or
- (use_static_handler and insecure_serving)):
- handler = StaticFilesHandler(handler)
+ if use_static_handler and (settings.DEBUG or insecure_serving):
+ return StaticFilesHandler(handler)
return handler
@@ -242,8 +242,8 @@ def get_script_name(environ):
Returns the equivalent of the HTTP request's SCRIPT_NAME environment
variable. If Apache mod_rewrite has been used, returns what would have been
the script name prior to any rewriting (so it's the script name as seen
- from the client's perspective), unless FORCE_SCRIPT_NAME is set (to
- anything).
+ from the client's perspective), unless the FORCE_SCRIPT_NAME setting is
+ set (to anything).
"""
from django.conf import settings
if settings.FORCE_SCRIPT_NAME is not None:
@@ -124,6 +124,7 @@ def readline(self, size=None):
self.buffer = sio.read()
return line
+
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
script_name = base.get_script_name(environ)
@@ -202,13 +203,12 @@ def _get_files(self):
FILES = property(_get_files)
REQUEST = property(_get_request)
+
class WSGIHandler(base.BaseHandler):
initLock = Lock()
request_class = WSGIRequest
def __call__(self, environ, start_response):
- from django.conf import settings
-
# Set up middleware if needed. We couldn't do this earlier, because
# settings weren't available.
if self._request_middleware is None:
@@ -253,4 +253,3 @@ def __call__(self, environ, start_response):
response_headers.append(('Set-Cookie', str(c.output(header=''))))
start_response(status, response_headers)
return response
-
@@ -5,8 +5,7 @@
import socket
from django.core.management.base import BaseCommand, CommandError
-from django.core.handlers.wsgi import WSGIHandler
-from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException
+from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException, get_internal_wsgi_application
from django.utils import autoreload
naiveip_re = re.compile(r"""^(?:
@@ -37,7 +36,7 @@ def get_handler(self, *args, **options):
"""
Returns the default WSGI handler for the runner.
"""
- return WSGIHandler()
+ return get_internal_wsgi_application()
def handle(self, addrport='', *args, **options):
self.use_ipv6 = options.get('use_ipv6')
@@ -18,7 +18,10 @@
from wsgiref.util import FileWrapper # for backwards compatibility
import django
+from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
+from django.core.wsgi import get_wsgi_application
+from django.utils.importlib import import_module
from django.utils._os import safe_join
from django.views import static
@@ -27,6 +30,43 @@
__all__ = ['WSGIServer', 'WSGIRequestHandler']
+def get_internal_wsgi_application():
+ """
+ Loads and returns the WSGI application as configured by the user in
+ ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
+ this will be the ``application`` object in ``projectname/wsgi.py``.
+
+ This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
+ for Django's internal servers (runserver, runfcgi); external WSGI servers
+ should just be configured to point to the correct application object
+ directly.
+
+ If settings.WSGI_APPLICATION is not set (is ``None``), we just return
+ whatever ``django.core.wsgi.get_wsgi_application`` returns.
+
+ """
+ from django.conf import settings
+ app_path = getattr(settings, 'WSGI_APPLICATION')
+ if app_path is None:
+ return get_wsgi_application()
+ module_name, attr = app_path.rsplit('.', 1)
+ try:
+ mod = import_module(module_name)
+ except ImportError, e:
+ raise ImproperlyConfigured(
+ "WSGI application '%s' could not be loaded; "
+ "could not import module '%s': %s" % (app_path, module_name, e))
+ try:
+ app = getattr(mod, attr)
+ except AttributeError, e:
+ raise ImproperlyConfigured(
+ "WSGI application '%s' could not be loaded; "
+ "can't find '%s' in module '%s': %s"
+ % (app_path, attr, module_name, e))
+
+ return app
+
+
class WSGIServerException(Exception):
pass
@@ -139,7 +139,7 @@ def runfastcgi(argset=[], **kwargs):
return False
# Prep up and go
- from django.core.handlers.wsgi import WSGIHandler
+ from django.core.servers.basehttp import get_internal_wsgi_application
if options["host"] and options["port"] and not options["socket"]:
wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
@@ -178,7 +178,7 @@ def runfastcgi(argset=[], **kwargs):
fp.write("%d\n" % os.getpid())
fp.close()
- WSGIServer(WSGIHandler(), **wsgi_opts).run()
+ WSGIServer(get_internal_wsgi_application(), **wsgi_opts).run()
if __name__ == '__main__':
runfastcgi(sys.argv[1:])
View
@@ -0,0 +1,13 @@
+from django.core.handlers.wsgi import WSGIHandler
+
+
+def get_wsgi_application():
+ """
+ The public interface to Django's WSGI support. Should return a WSGI
+ callable.
+
+ Allows us to avoid making django.core.handlers.WSGIHandler public API, in
+ case the internal WSGI implementation changes or moves in the future.
+
+ """
+ return WSGIHandler()
View
@@ -23,7 +23,7 @@ usage.
For a development environment -- if you just want to experiment with Django --
you don't need to have a separate Web server installed; Django comes with its
own lightweight development server. For a production environment, Django follows
-the WSGI spec, :pep:`333`, which means it can run on a variety of server
+the WSGI spec, :pep:`3333`, which means it can run on a variety of server
platforms. See :doc:`Deploying Django </howto/deployment/index>` for some
popular alternatives. Also, the `server arrangements wiki page`_ contains
details for several deployment strategies.
@@ -4,10 +4,9 @@ How to use Django with FastCGI, SCGI, or AJP
.. highlight:: bash
-Although the current preferred setup for running Django is :doc:`Apache with
-mod_wsgi </howto/deployment/modwsgi>`, many people use shared hosting, on
-which protocols such as FastCGI, SCGI or AJP are the only viable options. In
-some setups, these protocols may provide better performance than mod_wsgi_.
+Although :doc:`WSGI</howto/deployment/wsgi/index>` is the preferred deployment
+platform for Django, many people use shared hosting, on which protocols such as
+FastCGI, SCGI or AJP are the only viable options.
.. admonition:: Note
@@ -20,13 +19,13 @@ serve pages to a Web server. The Web server delegates the incoming Web requests
(via a socket) to FastCGI, which executes the code and passes the response back
to the Web server, which, in turn, passes it back to the client's Web browser.
-Like mod_wsgi, FastCGI allows code to stay in memory, allowing requests to be
-served with no startup time. While mod_wsgi can either be configured embedded
-in the Apache Web server process or as a separate daemon process, a FastCGI
-process never runs inside the Web server process, always in a separate,
+Like WSGI, FastCGI allows code to stay in memory, allowing requests to be
+served with no startup time. While
+e.g. :doc:`mod_wsgi</howto/deployment/wsgi/modwsgi>` can either be configured
+embedded in the Apache Web server process or as a separate daemon process, a
+FastCGI process never runs inside the Web server process, always in a separate,
persistent process.
-.. _mod_wsgi: http://code.google.com/p/modwsgi/
.. _mod_perl: http://perl.apache.org/
.. admonition:: Why run code in a separate process?
@@ -9,13 +9,12 @@ ways to easily deploy Django:
.. toctree::
:maxdepth: 1
- modwsgi
- uwsgi
+ wsgi/index
fastcgi
mod_python (deprecated) <modpython>
If you're new to deploying Django and/or Python, we'd recommend you try
-:doc:`mod_wsgi </howto/deployment/modwsgi>` first. In most cases it'll be
+:doc:`mod_wsgi </howto/deployment/wsgi/modwsgi>` first. In most cases it'll be
the easiest, fastest, and most stable deployment choice.
.. seealso::
@@ -7,14 +7,14 @@ How to use Django with Apache and mod_python
Support for mod_python has been deprecated, and will be removed in
Django 1.5. If you are configuring a new deployment, you are
strongly encouraged to consider using :doc:`mod_wsgi
- </howto/deployment/modwsgi>` or any of the other :doc:`supported
- backends </howto/deployment/index>`.
+ </howto/deployment/wsgi/modwsgi>` or any of the other :doc:`supported
+ servers </howto/deployment/index>`.
.. highlight:: apache
The `mod_python`_ module for Apache_ can be used to deploy Django to a
production server, although it has been mostly superseded by the simpler
-:doc:`mod_wsgi deployment option </howto/deployment/modwsgi>`.
+:doc:`mod_wsgi deployment option </howto/deployment/wsgi/modwsgi>`.
mod_python is similar to (and inspired by) `mod_perl`_ : It embeds Python within
Apache and loads Python code into memory when the server starts. Code stays in
Oops, something went wrong.

0 comments on commit 145a77e

Please sign in to comment.