run.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import logging
  2. import multiprocessing
  3. import os
  4. import sys
  5. from typing import List, Optional, Tuple
  6. import uvicorn
  7. from uvicorn.main import STARTUP_FAILURE
  8. from uvicorn.supervisors import ChangeReload, Multiprocess
  9. from . import globals, helpers, native_mode
  10. def run(*,
  11. host: Optional[str] = None,
  12. port: int = 8080,
  13. title: str = 'NiceGUI',
  14. viewport: str = 'width=device-width, initial-scale=1',
  15. favicon: Optional[str] = None,
  16. dark: Optional[bool] = False,
  17. binding_refresh_interval: float = 0.1,
  18. show: bool = True,
  19. native: bool = False,
  20. window_size: Optional[Tuple[int, int]] = None,
  21. fullscreen: bool = False,
  22. reload: bool = True,
  23. uvicorn_logging_level: str = 'warning',
  24. uvicorn_reload_dirs: str = '.',
  25. uvicorn_reload_includes: str = '*.py',
  26. uvicorn_reload_excludes: str = '.*, .py[cod], .sw.*, ~*',
  27. exclude: str = '',
  28. tailwind: bool = True,
  29. **kwargs,
  30. ) -> None:
  31. '''ui.run
  32. You can call `ui.run()` with optional arguments:
  33. :param host: start server with this host (defaults to `'127.0.0.1` in native mode, otherwise `'0.0.0.0'`)
  34. :param port: use this port (default: `8080`)
  35. :param title: page title (default: `'NiceGUI'`, can be overwritten per page)
  36. :param viewport: page meta viewport content (default: `'width=device-width, initial-scale=1'`, can be overwritten per page)
  37. :param favicon: relative filepath or absolute URL to a favicon (default: `None`, NiceGUI icon will be used)
  38. :param dark: whether to use Quasar's dark mode (default: `False`, use `None` for "auto" mode)
  39. :param binding_refresh_interval: time between binding updates (default: `0.1` seconds, bigger is more CPU friendly)
  40. :param show: automatically open the UI in a browser tab (default: `True`)
  41. :param native: open the UI in a native window of size 800x600 (default: `False`, deactivates `show`, automatically finds an open port)
  42. :param window_size: open the UI in a native window with the provided size (e.g. `(1024, 786)`, default: `None`, also activates `native`)
  43. :param fullscreen: open the UI in a fullscreen window (default: `False`, also activates `native`)
  44. :param reload: automatically reload the UI on file changes (default: `True`)
  45. :param uvicorn_logging_level: logging level for uvicorn server (default: `'warning'`)
  46. :param uvicorn_reload_dirs: string with comma-separated list for directories to be monitored (default is current working directory only)
  47. :param uvicorn_reload_includes: string with comma-separated list of glob-patterns which trigger reload on modification (default: `'.py'`)
  48. :param uvicorn_reload_excludes: string with comma-separated list of glob-patterns which should be ignored for reload (default: `'.*, .py[cod], .sw.*, ~*'`)
  49. :param exclude: comma-separated string to exclude elements (with corresponding JavaScript libraries) to save bandwidth
  50. (possible entries: aggrid, audio, chart, colors, interactive_image, joystick, keyboard, log, markdown, mermaid, plotly, scene, video)
  51. :param tailwind: whether to use Tailwind (experimental, default: `True`)
  52. :param kwargs: additional keyword arguments are passed to `uvicorn.run`
  53. '''
  54. globals.ui_run_has_been_called = True
  55. globals.reload = reload
  56. globals.title = title
  57. globals.viewport = viewport
  58. globals.favicon = favicon
  59. globals.dark = dark
  60. globals.binding_refresh_interval = binding_refresh_interval
  61. globals.excludes = [e.strip() for e in exclude.split(',')]
  62. globals.tailwind = tailwind
  63. if multiprocessing.current_process().name != 'MainProcess':
  64. return
  65. if fullscreen:
  66. native = True
  67. if window_size:
  68. native = True
  69. if native:
  70. show = False
  71. host = host or '127.0.0.1'
  72. port = native_mode.find_open_port()
  73. width, height = window_size or (800, 600)
  74. native_mode.activate(f'http://{host}:{port}', title, width, height, fullscreen)
  75. else:
  76. host = host or '0.0.0.0'
  77. # NOTE: We save the URL in an environment variable so the subprocess started in reload mode can access it.
  78. os.environ['NICEGUI_URL'] = f'http://{host}:{port}'
  79. if show:
  80. helpers.schedule_browser(host, port)
  81. def split_args(args: str) -> List[str]:
  82. return [a.strip() for a in args.split(',')]
  83. # NOTE: The following lines are basically a copy of `uvicorn.run`, but keep a reference to the `server`.
  84. config = uvicorn.Config(
  85. 'nicegui:app' if reload else globals.app,
  86. host=host,
  87. port=port,
  88. reload=reload,
  89. reload_includes=split_args(uvicorn_reload_includes) if reload else None,
  90. reload_excludes=split_args(uvicorn_reload_excludes) if reload else None,
  91. reload_dirs=split_args(uvicorn_reload_dirs) if reload else None,
  92. log_level=uvicorn_logging_level,
  93. **kwargs,
  94. )
  95. globals.server = uvicorn.Server(config=config)
  96. if (reload or config.workers > 1) and not isinstance(config.app, str):
  97. logging.warning('You must pass the application as an import string to enable "reload" or "workers".')
  98. sys.exit(1)
  99. if config.should_reload:
  100. sock = config.bind_socket()
  101. ChangeReload(config, target=globals.server.run, sockets=[sock]).run()
  102. elif config.workers > 1:
  103. sock = config.bind_socket()
  104. Multiprocess(config, target=globals.server.run, sockets=[sock]).run()
  105. else:
  106. globals.server.run()
  107. if config.uds:
  108. os.remove(config.uds) # pragma: py-win32
  109. if not globals.server.started and not config.should_reload and config.workers == 1:
  110. sys.exit(STARTUP_FAILURE)