Browse Source

Fixed #32813 -- Made runserver display port after binding.

Thanks Florian Apolloner for the review.
Dhanush 2 years ago
parent
commit
a18d20ca97

+ 21 - 25
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,
+        )

+ 11 - 1
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

+ 13 - 10
tests/admin_scripts/tests.py

@@ -1585,21 +1585,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)