nicegui.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import asyncio
  2. import urllib.parse
  3. from pathlib import Path
  4. from typing import Dict
  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, ErrorClient
  12. from .favicon import create_favicon_routes
  13. from .helpers import safe_invoke
  14. from .page import page
  15. from .task_logger import create_task
  16. globals.app = app = FastAPI()
  17. globals.sio = sio = SocketManager(app=app)._sio
  18. app.add_middleware(GZipMiddleware)
  19. app.mount("/static", StaticFiles(directory=Path(__file__).parent / 'static'), name='static')
  20. index_client = Client(page('/')).__enter__()
  21. error_client = ErrorClient(page(''))
  22. @app.get('/')
  23. def index():
  24. return 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. return error_client.build_response(404, str(exc))
  49. @app.exception_handler(Exception)
  50. async def exception_handler(_: Request, exc: Exception):
  51. return error_client.build_response(500, str(exc))
  52. @sio.on('connect')
  53. async def handle_connect(sid: str, _) -> None:
  54. client = get_client(sid)
  55. client.environ = sio.get_environ(sid)
  56. sio.enter_room(sid, str(client.id))
  57. @sio.on('disconnect')
  58. async def handle_disconnect(sid: str) -> None:
  59. client = get_client(sid)
  60. if client.id != 0:
  61. del globals.clients[client.id]
  62. @sio.on('event')
  63. def handle_event(sid: str, msg: Dict) -> None:
  64. client = get_client(sid)
  65. with client:
  66. sender = client.elements.get(msg['id'])
  67. if sender:
  68. sender.handle_event(msg)
  69. @sio.on('javascript_response')
  70. def handle_event(sid: str, msg: Dict) -> None:
  71. get_client(sid).waiting_javascript_commands[msg['request_id']] = msg['result']
  72. def get_client(sid: str) -> Client:
  73. query_bytes: bytearray = sio.get_environ(sid)['asgi.scope']['query_string']
  74. query = urllib.parse.parse_qs(query_bytes.decode())
  75. client_id = int(query['client_id'][0])
  76. return globals.clients[client_id]