nicegui.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import asyncio
  2. import urllib.parse
  3. from pathlib import Path
  4. from typing import Dict, Optional
  5. from fastapi import FastAPI, Request
  6. from fastapi.middleware.gzip import GZipMiddleware
  7. from fastapi.responses import FileResponse
  8. from fastapi.staticfiles import StaticFiles
  9. from fastapi_socketio import SocketManager
  10. from . import binding, globals, vue
  11. from .client import Client
  12. from .error import error_content
  13. from .favicon import create_favicon_routes
  14. from .helpers import safe_invoke
  15. from .page import page
  16. from .task_logger import create_task
  17. globals.app = app = FastAPI()
  18. globals.sio = sio = SocketManager(app=app)._sio
  19. app.add_middleware(GZipMiddleware)
  20. app.mount("/static", StaticFiles(directory=Path(__file__).parent / 'static'), name='static')
  21. globals.index_client = Client(page('/'), shared=True).__enter__()
  22. @app.get('/')
  23. def index():
  24. return globals.index_client.build_response()
  25. @app.get('/_vue/dependencies/{path:path}')
  26. def vue_dependencies(path: str):
  27. return FileResponse(path, media_type='text/javascript')
  28. @app.get('/_vue/components/{name}')
  29. def vue_dependencies(name: str):
  30. return FileResponse(vue.js_components[name], media_type='text/javascript')
  31. @app.on_event('startup')
  32. def on_startup() -> None:
  33. globals.state = globals.State.STARTING
  34. globals.loop = asyncio.get_running_loop()
  35. create_favicon_routes()
  36. [safe_invoke(t) for t in globals.startup_handlers]
  37. create_task(binding.loop())
  38. globals.state = globals.State.STARTED
  39. print(f'NiceGUI ready to go on http://{globals.host}:{globals.port}')
  40. @app.on_event('shutdown')
  41. def shutdown() -> None:
  42. globals.state = globals.State.STOPPING
  43. [safe_invoke(t) for t in globals.shutdown_handlers]
  44. [t.cancel() for t in globals.tasks]
  45. globals.state = globals.State.STOPPED
  46. @app.exception_handler(404)
  47. async def exception_handler(_: Request, exc: Exception):
  48. with Client(page('')) as client:
  49. error_content(404, f'{exc.__class__.__name__}: {str(exc)}')
  50. return client.build_response()
  51. @app.exception_handler(Exception)
  52. async def exception_handler(_: Request, exc: Exception):
  53. with Client(page('')) as client:
  54. error_content(500, f'{exc.__class__.__name__}: {str(exc)}')
  55. return client.build_response()
  56. @sio.on('connect')
  57. async def handle_connect(sid: str, _) -> None:
  58. client = get_client(sid)
  59. if not client:
  60. return
  61. client.environ = sio.get_environ(sid)
  62. sio.enter_room(sid, str(client.id))
  63. with client:
  64. [safe_invoke(t) for t in client.connect_handlers]
  65. @sio.on('disconnect')
  66. async def handle_disconnect(sid: str) -> None:
  67. client = get_client(sid)
  68. if not client:
  69. return
  70. if not client.shared:
  71. del globals.clients[client.id]
  72. with client:
  73. [safe_invoke(t) for t in client.disconnect_handlers]
  74. @sio.on('event')
  75. def handle_event(sid: str, msg: Dict) -> None:
  76. client = get_client(sid)
  77. if not client:
  78. return
  79. with client:
  80. sender = client.elements.get(msg['id'])
  81. if sender:
  82. sender.handle_event(msg)
  83. @sio.on('javascript_response')
  84. def handle_event(sid: str, msg: Dict) -> None:
  85. client = get_client(sid)
  86. if not client:
  87. return
  88. client.waiting_javascript_commands[msg['request_id']] = msg['result']
  89. def get_client(sid: str) -> Optional[Client]:
  90. query_bytes: bytearray = sio.get_environ(sid)['asgi.scope']['query_string']
  91. query = urllib.parse.parse_qs(query_bytes.decode())
  92. client_id = int(query['client_id'][0])
  93. return globals.clients.get(client_id)