globals.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. from __future__ import annotations
  2. import asyncio
  3. import inspect
  4. import logging
  5. import os
  6. from contextlib import contextmanager
  7. from enum import Enum
  8. from pathlib import Path
  9. from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Iterator, List, Literal, Optional, Set, Union
  10. from socketio import AsyncServer
  11. from uvicorn import Server
  12. from . import background_tasks
  13. from .helpers import is_coroutine_function
  14. if TYPE_CHECKING:
  15. from .air import Air
  16. from .app import App
  17. from .client import Client
  18. from .language import Language
  19. from .slot import Slot
  20. class State(Enum):
  21. STOPPED = 0
  22. STARTING = 1
  23. STARTED = 2
  24. STOPPING = 3
  25. app: App
  26. sio: AsyncServer
  27. server: Server
  28. loop: Optional[asyncio.AbstractEventLoop] = None
  29. log: logging.Logger = logging.getLogger('nicegui')
  30. state: State = State.STOPPED
  31. ui_run_has_been_called: bool = False
  32. optional_features: Set[str] = set()
  33. reload: bool
  34. title: str
  35. viewport: str
  36. favicon: Optional[Union[str, Path]]
  37. dark: Optional[bool]
  38. language: Language
  39. binding_refresh_interval: float
  40. reconnect_timeout: float
  41. tailwind: bool
  42. prod_js: bool
  43. endpoint_documentation: Literal['none', 'internal', 'page', 'all'] = 'none'
  44. air: Optional[Air] = None
  45. storage_path: Path = Path(os.environ.get('NICEGUI_STORAGE_PATH', '.nicegui')).resolve()
  46. socket_io_js_query_params: Dict = {}
  47. socket_io_js_extra_headers: Dict = {}
  48. socket_io_js_transports: List[Literal['websocket', 'polling']] = ['websocket', 'polling'] # NOTE: we favor websocket
  49. _socket_id: Optional[str] = None
  50. slot_stacks: Dict[int, List[Slot]] = {}
  51. clients: Dict[str, Client] = {}
  52. index_client: Client
  53. quasar_config: Dict = {
  54. 'brand': {
  55. 'primary': '#5898d4',
  56. },
  57. 'loadingBar': {
  58. 'color': 'primary',
  59. 'skipHijack': False,
  60. },
  61. }
  62. page_routes: Dict[Callable[..., Any], str] = {}
  63. startup_handlers: List[Union[Callable[..., Any], Awaitable]] = []
  64. shutdown_handlers: List[Union[Callable[..., Any], Awaitable]] = []
  65. connect_handlers: List[Union[Callable[..., Any], Awaitable]] = []
  66. disconnect_handlers: List[Union[Callable[..., Any], Awaitable]] = []
  67. exception_handlers: List[Callable[..., Any]] = [log.exception]
  68. def get_task_id() -> int:
  69. """Return the ID of the current asyncio task."""
  70. try:
  71. return id(asyncio.current_task())
  72. except RuntimeError:
  73. return 0
  74. def get_slot_stack() -> List[Slot]:
  75. """Return the slot stack of the current asyncio task."""
  76. task_id = get_task_id()
  77. if task_id not in slot_stacks:
  78. slot_stacks[task_id] = []
  79. return slot_stacks[task_id]
  80. def prune_slot_stack() -> None:
  81. """Remove the current slot stack if it is empty."""
  82. task_id = get_task_id()
  83. if not slot_stacks[task_id]:
  84. del slot_stacks[task_id]
  85. def get_slot() -> Slot:
  86. """Return the current slot."""
  87. return get_slot_stack()[-1]
  88. def get_client() -> Client:
  89. """Return the current client."""
  90. return get_slot().parent.client
  91. @contextmanager
  92. def socket_id(id_: str) -> Iterator[None]:
  93. """Enter a context with a specific socket ID."""
  94. global _socket_id # pylint: disable=global-statement
  95. _socket_id = id_
  96. yield
  97. _socket_id = None
  98. def handle_exception(exception: Exception) -> None:
  99. """Handle an exception by invoking all registered exception handlers."""
  100. for handler in exception_handlers:
  101. result = handler() if not inspect.signature(handler).parameters else handler(exception)
  102. if is_coroutine_function(handler):
  103. background_tasks.create(result)