Refactored runserver command and moved code related to staticfiles to a subclass that is enabled if staticfiles app is installed.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14553 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-11-13 18:41:55 +00:00
parent 1ed62706e7
commit e9f3899b20
4 changed files with 125 additions and 73 deletions

View File

@ -0,0 +1,27 @@
from optparse import make_option
from django.conf import settings
from django.core.management.commands.runserver import BaseRunserverCommand
from django.contrib.staticfiles.handlers import StaticFilesHandler
class Command(BaseRunserverCommand):
option_list = BaseRunserverCommand.option_list + (
make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'),
make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
help='Allows serving static files even if DEBUG is False.'),
)
help = "Starts a lightweight Web server for development, including static files serving."
def get_handler(self, *args, **options):
"""
Returns the static files serving 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)
return handler

View File

@ -1,20 +1,16 @@
from optparse import make_option from optparse import make_option
import os import os
import sys import sys
import warnings
from django.core.management.base import BaseCommand, CommandError 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.utils import autoreload
class Command(BaseCommand): class BaseRunserverCommand(BaseCommand):
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('--noreload', action='store_false', dest='use_reloader', default=True, make_option('--noreload', action='store_false', dest='use_reloader', default=True,
help='Tells Django to NOT use the auto-reloader.'), help='Tells Django to NOT use the auto-reloader.'),
make_option('--nostatic', action="store_false", dest='use_static_handler', default=True,
help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'),
make_option('--insecure', action="store_true", dest='insecure_serving', default=False,
help='Allows serving static files even if DEBUG is False.'),
make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.'),
) )
help = "Starts a lightweight Web server for development." help = "Starts a lightweight Web server for development."
args = '[optional port number, or ipaddr:port]' args = '[optional port number, or ipaddr:port]'
@ -22,79 +18,98 @@ class Command(BaseCommand):
# Validation is called explicitly each time the server is reloaded. # Validation is called explicitly each time the server is reloaded.
requires_model_validation = False requires_model_validation = False
def get_handler(self, *args, **options):
"""
Returns the default WSGI handler for the runner.
"""
return WSGIHandler()
def handle(self, addrport='', *args, **options): def handle(self, addrport='', *args, **options):
import django
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
from django.core.handlers.wsgi import WSGIHandler
from django.contrib.staticfiles.handlers import StaticFilesHandler
if args: if args:
raise CommandError('Usage is runserver %s' % self.args) raise CommandError('Usage is runserver %s' % self.args)
if not addrport: if not addrport:
addr = '' self.addr = ''
port = '8000' self.port = '8000'
else: else:
try: try:
addr, port = addrport.split(':') self.addr, self.port = addrport.split(':')
except ValueError: except ValueError:
addr, port = '', addrport self.addr, self.port = '', addrport
if not addr: if not self.addr:
addr = '127.0.0.1' self.addr = '127.0.0.1'
if not port.isdigit(): if not self.port.isdigit():
raise CommandError("%r is not a valid port number." % port) raise CommandError("%r is not a valid port number." % self.port)
self.run(*args, **options)
def run(self, *args, **options):
"""
Runs the server, using the autoreloader if needed
"""
use_reloader = options.get('use_reloader', True) use_reloader = options.get('use_reloader', True)
admin_media_path = options.get('admin_media_path', '')
shutdown_message = options.get('shutdown_message', '')
use_static_handler = options.get('use_static_handler', True)
insecure_serving = options.get('insecure_serving', False)
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
def inner_run():
from django.conf import settings
from django.utils import translation
print "Validating models..."
self.validate(display_num_errors=True)
print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with %s." % quit_command
# django.core.management.base forces the locale to en-us. We should
# set it up correctly for the first request (particularly important
# in the "--noreload" case).
translation.activate(settings.LANGUAGE_CODE)
try:
handler = WSGIHandler()
allow_serving = (settings.DEBUG and use_static_handler or
(use_static_handler and insecure_serving))
if (allow_serving and
"django.contrib.staticfiles" in settings.INSTALLED_APPS):
handler = StaticFilesHandler(handler)
# serve admin media like old-school (deprecation pending)
handler = AdminMediaHandler(handler, admin_media_path)
run(addr, int(port), handler)
except WSGIServerException, e:
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
13: "You don't have permission to access that port.",
98: "That port is already in use.",
99: "That IP address can't be assigned-to.",
}
try:
error_text = ERRORS[e.args[0].args[0]]
except (AttributeError, KeyError):
error_text = str(e)
sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
print shutdown_message
sys.exit(0)
if use_reloader: if use_reloader:
from django.utils import autoreload autoreload.main(self.inner_run, args, options)
autoreload.main(inner_run)
else: else:
inner_run() self.inner_run(*args, **options)
def inner_run(self, *args, **options):
from django.conf import settings
from django.utils import translation
shutdown_message = options.get('shutdown_message', '')
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
self.stdout.write("Validating models...\n\n")
self.validate(display_num_errors=True)
self.stdout.write((
"Django version %(version)s, using settings %(settings)r\n"
"Development server is running at http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": self.addr,
"port": self.port,
"quit_command": quit_command,
})
# django.core.management.base forces the locale to en-us. We should
# set it up correctly for the first request (particularly important
# in the "--noreload" case).
translation.activate(settings.LANGUAGE_CODE)
try:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler)
except WSGIServerException, e:
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
13: "You don't have permission to access that port.",
98: "That port is already in use.",
99: "That IP address can't be assigned-to.",
}
try:
error_text = ERRORS[e.args[0].args[0]]
except (AttributeError, KeyError):
error_text = str(e)
sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
self.stdout.write("%s\n" % shutdown_message)
sys.exit(0)
class Command(BaseRunserverCommand):
option_list = BaseRunserverCommand.option_list + (
make_option('--adminmedia', dest='admin_media_path', default='',
help='Specifies the directory from which to serve admin media.'),
)
def get_handler(self, *args, **options):
"""
Serves admin media like old-school (deprecation pending).
"""
handler = super(Command, self).get_handler(*args, **options)
return AdminMediaHandler(handler, options.get('admin_media_path', ''))

View File

@ -164,6 +164,12 @@ Do not prompt the user for input.
.I \-\-noreload .I \-\-noreload
Disable the development server's auto\-reloader. Disable the development server's auto\-reloader.
.TP .TP
.I \-\-nostatic
Disable automatic serving of static files from STATICFILES_URL.
.TP
.I \-\-insecure
Enables serving of static files even if DEBUG is False.
.TP
.I \-\-verbosity=VERBOSITY .I \-\-verbosity=VERBOSITY
Verbosity level: 0=minimal output, 1=normal output, 2=all output. Verbosity level: 0=minimal output, 1=normal output, 2=all output.
.TP .TP

View File

@ -684,7 +684,9 @@ Example usage::
.. django-admin-option:: --nostatic .. django-admin-option:: --nostatic
Use the ``--nostatic`` option to disable serving of static files with the Use the ``--nostatic`` option to disable serving of static files with the
:doc:`staticfiles </ref/contrib/staticfiles>` app entirely. :doc:`staticfiles </ref/contrib/staticfiles>` app entirely. This option is
only available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
in your project's :setting:`INSTALLED_APPS` setting.
Example usage:: Example usage::
@ -696,7 +698,9 @@ Use the ``--insecure`` option to force serving of static files with the
:doc:`staticfiles </ref/contrib/staticfiles>` app even if the :setting:`DEBUG` :doc:`staticfiles </ref/contrib/staticfiles>` app even if the :setting:`DEBUG`
setting is ``False``. By using this you acknowledge the fact that it's setting is ``False``. By using this you acknowledge the fact that it's
**grossly inefficient** and probably **insecure**. This is only intended for **grossly inefficient** and probably **insecure**. This is only intended for
local development, and should **never be used in production**. local development, should **never be used in production** and is only
available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
in your project's :setting:`INSTALLED_APPS` setting.
See the :doc:`reference documentation of the app </ref/contrib/staticfiles>` See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details and learn how to :doc:`manage and deploy static files for more details and learn how to :doc:`manage and deploy static files