From 2ca00faa913754cd5860f6e1f23c8da2529c691a Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 22 Sep 2013 15:55:09 +0200 Subject: [PATCH] Fixed "Address already in use" from liveserver. Our WSGIServer rewrapped the socket errors from server_bind into WSGIServerExceptions, which is used later on to provide nicer error messages in runserver and used by the liveserver to see if the port is already in use. But wrapping server_bind isn't enough since it only binds to the socket, socket.listen (which is called from server_activate) could also raise "Address already in use". Instead of overriding server_activate too I chose to just catch socket errors, which seems to make more sense anyways and should be more robust against changes in wsgiref. --- django/core/management/commands/runserver.py | 15 ++++++++------- django/core/servers/basehttp.py | 9 +-------- django/test/testcases.py | 9 ++++----- tests/servers/tests.py | 4 ++-- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index f5a701cd23..402b3d342c 100644 --- a/django/core/management/commands/runserver.py +++ b/django/core/management/commands/runserver.py @@ -1,12 +1,13 @@ from optparse import make_option from datetime import datetime +import errno import os import re import sys import socket from django.core.management.base import BaseCommand, CommandError -from django.core.servers.basehttp import run, WSGIServerException, get_internal_wsgi_application +from django.core.servers.basehttp import run, get_internal_wsgi_application from django.utils import autoreload naiveip_re = re.compile(r"""^(?: @@ -117,16 +118,16 @@ class Command(BaseCommand): handler = self.get_handler(*args, **options) run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading) - except WSGIServerException as e: + except socket.error as 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.", + errno.EACCES: "You don't have permission to access that port.", + errno.EADDRINUSE: "That port is already in use.", + errno.EADDRNOTAVAIL: "That IP address can't be assigned-to.", } try: - error_text = ERRORS[e.args[0].args[0]] - except (AttributeError, KeyError): + error_text = ERRORS[e.errno] + except KeyError: error_text = str(e) self.stderr.write("Error: %s" % error_text) # Need to use an OS exit because sys.exit doesn't work in a thread diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index 6287b1a4f9..010b5e07dd 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -58,10 +58,6 @@ def get_internal_wsgi_application(): ) -class WSGIServerException(Exception): - pass - - class ServerHandler(simple_server.ServerHandler, object): error_status = str("500 INTERNAL SERVER ERROR") @@ -114,10 +110,7 @@ class WSGIServer(simple_server.WSGIServer, object): def server_bind(self): """Override server_bind to store the server name.""" - try: - super(WSGIServer, self).server_bind() - except Exception as e: - six.reraise(WSGIServerException, WSGIServerException(e), sys.exc_info()[2]) + super(WSGIServer, self).server_bind() self.setup_environ() diff --git a/django/test/testcases.py b/django/test/testcases.py index 0a61a21ccf..a657530f94 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -8,6 +8,7 @@ import json import os import posixpath import re +import socket import sys import threading import unittest @@ -21,8 +22,7 @@ from django.core.handlers.wsgi import get_path_info, WSGIHandler from django.core.management import call_command from django.core.management.color import no_style from django.core.management.commands import flush -from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer, - WSGIServerException) +from django.core.servers.basehttp import WSGIRequestHandler, WSGIServer from django.core.urlresolvers import clear_url_caches, set_urlconf from django.db import connection, connections, DEFAULT_DB_ALIAS, transaction from django.db.models.loading import cache @@ -1028,10 +1028,9 @@ class LiveServerThread(threading.Thread): try: self.httpd = WSGIServer( (self.host, port), QuietWSGIRequestHandler) - except WSGIServerException as e: + except socket.error as e: if (index + 1 < len(self.possible_ports) and - hasattr(e.args[0], 'errno') and - e.args[0].errno == errno.EADDRINUSE): + e.errno == errno.EADDRINUSE): # This port is already in use, so we go on and try with # the next one in the list. continue diff --git a/tests/servers/tests.py b/tests/servers/tests.py index f833570862..77c2e39521 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -5,10 +5,10 @@ Tests for django.core.servers. from __future__ import unicode_literals import os +import socket from django.core.exceptions import ImproperlyConfigured from django.test import LiveServerTestCase -from django.core.servers.basehttp import WSGIServerException from django.test.utils import override_settings from django.utils.http import urlencode from django.utils.six.moves.urllib.error import HTTPError @@ -71,7 +71,7 @@ class LiveServerAddress(LiveServerBase): cls.raises_exception('localhost', ImproperlyConfigured) # The host must be valid - cls.raises_exception('blahblahblah:8081', WSGIServerException) + cls.raises_exception('blahblahblah:8081', socket.error) # The list of ports must be in a valid format cls.raises_exception('localhost:8081,', ImproperlyConfigured)