Selaa lähdekoodia

Merge pull request #558 from zauberzeug/auto_port

automatically discover the port
Falko Schindler 2 vuotta sitten
vanhempi
säilyke
613b256ec5
4 muutettua tiedostoa jossa 27 lisäystä ja 8 poistoa
  1. 0 2
      nicegui/globals.py
  2. 2 1
      nicegui/nicegui.py
  3. 4 2
      nicegui/run.py
  4. 21 3
      nicegui/standalone_mode.py

+ 0 - 2
nicegui/globals.py

@@ -31,8 +31,6 @@ log: logging.Logger = logging.getLogger('nicegui')
 state: State = State.STOPPED
 ui_run_has_been_called: bool = False
 
-host: str
-port: int
 reload: bool
 title: str
 viewport: str

+ 2 - 1
nicegui/nicegui.py

@@ -1,4 +1,5 @@
 import asyncio
+import os
 import time
 import urllib.parse
 from pathlib import Path
@@ -71,7 +72,7 @@ def handle_startup(with_welcome_message: bool = True) -> None:
     background_tasks.create(prune_slot_stacks())
     globals.state = globals.State.STARTED
     if with_welcome_message:
-        print(f'NiceGUI ready to go on http://{globals.host}:{globals.port}')
+        print(f'NiceGUI ready to go on {os.environ["NICEGUI_URL"]}')
 
 
 @app.on_event('shutdown')

+ 4 - 2
nicegui/run.py

@@ -57,8 +57,6 @@ def run(*,
     :param kwargs: additional keyword arguments are passed to `uvicorn.run`
     '''
     globals.ui_run_has_been_called = True
-    globals.host = host
-    globals.port = port
     globals.reload = reload
     globals.title = title
     globals.viewport = viewport
@@ -75,9 +73,13 @@ def run(*,
         standalone = True
     if standalone:
         show = False
+        port = standalone_mode.find_open_port()
         width, height = (800, 600) if standalone is True else standalone
         standalone_mode.activate(f'http://localhost:{port}', title, width, height, fullscreen)
 
+    # NOTE: We save the URL in an environment variable so the subprocess started in reload mode can access it.
+    os.environ['NICEGUI_URL'] = f'http://localhost:{port}'
+
     if show:
         webbrowser.open(f'http://{host if host != "0.0.0.0" else "127.0.0.1"}:{port}/')
 

+ 21 - 3
nicegui/standalone_mode.py

@@ -1,13 +1,15 @@
 import multiprocessing
 import os
 import signal
+import socket
 import tempfile
 import time
 import warnings
 from threading import Thread
 
-with warnings.catch_warnings():  # webview depends on bottle which uses the deprecated cgi function (see https://github.com/bottlepy/bottle/issues/1403)
-    warnings.filterwarnings("ignore", category=DeprecationWarning)
+with warnings.catch_warnings():
+    # webview depends on bottle which uses the deprecated CGI function (https://github.com/bottlepy/bottle/issues/1403)
+    warnings.filterwarnings('ignore', category=DeprecationWarning)
     import webview
 
 shutdown = multiprocessing.Event()
@@ -15,7 +17,7 @@ shutdown = multiprocessing.Event()
 
 def open_window(url: str, title: str, width: int, height: int, fullscreen: bool, shutdown: multiprocessing.Event) -> None:
     window = webview.create_window(title, url=url, width=width, height=height, fullscreen=fullscreen)
-    window.events.closing += shutdown.set  # signal that the program should be closed to the main process
+    window.events.closing += shutdown.set  # signal to the main process that the program should be closed
     webview.start(storage_path=tempfile.mkdtemp())
 
 
@@ -30,3 +32,19 @@ def activate(url: str, title: str, width: int, height: int, fullscreen: bool) ->
     args = url, title, width, height, fullscreen, shutdown
     multiprocessing.Process(target=open_window, args=args, daemon=False).start()
     Thread(target=check_shutdown, daemon=True).start()
+
+
+def find_open_port(start_port: int = 8000, end_port: int = 8999) -> int:
+    '''Reliably find an open port in a given range.
+
+    This function will actually try to open the port to ensure no firewall blocks it.
+    This is better than, e.g., passing port=0 to uvicorn.
+    '''
+    for port in range(start_port, end_port + 1):
+        try:
+            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+                s.bind(('localhost', port))
+                return port
+        except OSError:
+            pass
+    raise OSError('No open port found')