native_mode.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. import multiprocessing
  2. import os
  3. import signal
  4. import socket
  5. import tempfile
  6. import time
  7. import warnings
  8. from threading import Thread
  9. with warnings.catch_warnings():
  10. # webview depends on bottle which uses the deprecated CGI function (https://github.com/bottlepy/bottle/issues/1403)
  11. warnings.filterwarnings('ignore', category=DeprecationWarning)
  12. import webview
  13. def open_window(url: str, title: str, width: int, height: int, fullscreen: bool, shutdown: multiprocessing.Event) -> None:
  14. window = webview.create_window(title, url=url, width=width, height=height, fullscreen=fullscreen)
  15. window.events.closing += shutdown.set # signal to the main process that the program should be closed
  16. webview.start(storage_path=tempfile.mkdtemp())
  17. def activate(url: str, title: str, width: int, height: int, fullscreen: bool) -> None:
  18. multiprocessing.freeze_support() # NOTE we need to activate freeze_support() before accessing multiprocessing.Event()
  19. shutdown = multiprocessing.Event()
  20. def check_shutdown() -> None:
  21. while True:
  22. if shutdown.is_set():
  23. os.kill(os.getpgid(os.getpid()), signal.SIGTERM)
  24. time.sleep(0.1)
  25. args = url, title, width, height, fullscreen, shutdown
  26. multiprocessing.Process(target=open_window, args=args, daemon=False).start()
  27. Thread(target=check_shutdown, daemon=True).start()
  28. def find_open_port(start_port: int = 8000, end_port: int = 8999) -> int:
  29. '''Reliably find an open port in a given range.
  30. This function will actually try to open the port to ensure no firewall blocks it.
  31. This is better than, e.g., passing port=0 to uvicorn.
  32. '''
  33. for port in range(start_port, end_port + 1):
  34. try:
  35. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  36. s.bind(('localhost', port))
  37. return port
  38. except OSError:
  39. pass
  40. raise OSError('No open port found')