diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index 2b91feb3753..26bbf29d680 100644 --- a/django/core/management/commands/runserver.py +++ b/django/core/management/commands/runserver.py @@ -127,14 +127,6 @@ class Command(BaseCommand): threading = options["use_threading"] # 'shutdown_message' is a stealth option. shutdown_message = options.get("shutdown_message", "") - quit_command = "CTRL-BREAK" if sys.platform == "win32" else "CONTROL-C" - - if self._raw_ipv6: - addr = f"[{self.addr}]" - elif self.addr == "0": - addr = "0.0.0.0" - else: - addr = self.addr if not options["skip_checks"]: self.stdout.write("Performing system checks...\n\n") @@ -142,23 +134,6 @@ class Command(BaseCommand): # Need to check migrations here, so can't use the # requires_migrations_check attribute. self.check_migrations() - now = datetime.now().strftime("%B %d, %Y - %X") - self.stdout.write(now) - self.stdout.write( - ( - "Django version %(version)s, using settings %(settings)r\n" - "Starting development server at %(protocol)s://%(addr)s:%(port)s/\n" - "Quit the server with %(quit_command)s." - ) - % { - "version": self.get_version(), - "settings": settings.SETTINGS_MODULE, - "protocol": self.protocol, - "addr": addr, - "port": self.port, - "quit_command": quit_command, - } - ) try: handler = self.get_handler(*args, **options) @@ -168,6 +143,7 @@ class Command(BaseCommand): handler, ipv6=self.use_ipv6, threading=threading, + on_bind=self.on_bind, server_cls=self.server_cls, ) except OSError as e: @@ -188,3 +164,23 @@ class Command(BaseCommand): if shutdown_message: self.stdout.write(shutdown_message) sys.exit(0) + + def on_bind(self, server_port): + quit_command = "CTRL-BREAK" if sys.platform == "win32" else "CONTROL-C" + + if self._raw_ipv6: + addr = f"[{self.addr}]" + elif self.addr == "0": + addr = "0.0.0.0" + else: + addr = self.addr + + now = datetime.now().strftime("%B %d, %Y - %X") + version = self.get_version() + print( + f"{now}\n" + f"Django version {version}, using settings {settings.SETTINGS_MODULE!r}\n" + f"Starting development server at {self.protocol}://{addr}:{server_port}/\n" + f"Quit the server with {quit_command}.", + file=self.stdout, + ) diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index d08fb77a475..6afe17cec47 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -252,13 +252,23 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler): handler.run(self.server.get_app()) -def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): +def run( + addr, + port, + wsgi_handler, + ipv6=False, + threading=False, + on_bind=None, + server_cls=WSGIServer, +): server_address = (addr, port) if threading: httpd_cls = type("WSGIServer", (socketserver.ThreadingMixIn, server_cls), {}) else: httpd_cls = server_cls httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) + if on_bind is not None: + on_bind(getattr(httpd, "server_port", port)) if threading: # ThreadingMixIn.daemon_threads indicates how threads will behave on an # abrupt shutdown; like quitting the server by the user or restarting diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index cfb7f21b958..6d67c2931a5 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -1587,21 +1587,24 @@ class ManageRunserver(SimpleTestCase): call_command(self.cmd, addrport="7000") self.assertServerSettings("127.0.0.1", "7000") - @mock.patch("django.core.management.commands.runserver.run") - @mock.patch("django.core.management.base.BaseCommand.check_migrations") - def test_zero_ip_addr(self, *mocked_objects): - call_command( - "runserver", - addrport="0:8000", - use_reloader=False, - skip_checks=True, - stdout=self.output, - ) + def test_zero_ip_addr(self): + self.cmd.addr = "0" + self.cmd._raw_ipv6 = False + self.cmd.on_bind("8000") self.assertIn( "Starting development server at http://0.0.0.0:8000/", self.output.getvalue(), ) + def test_on_bind(self): + self.cmd.addr = "127.0.0.1" + self.cmd._raw_ipv6 = False + self.cmd.on_bind("14437") + self.assertIn( + "Starting development server at http://127.0.0.1:14437/", + self.output.getvalue(), + ) + @unittest.skipUnless(socket.has_ipv6, "platform doesn't support IPv6") def test_runner_addrport_ipv6(self): call_command(self.cmd, addrport="", use_ipv6=True)