Bläddra i källkod

Merge branch 'main' into page_updates

# Conflicts:
#	nicegui/elements/button.py
#	nicegui/elements/color_picker.py
#	nicegui/elements/menu_item.py
#	nicegui/elements/upload.py
#	nicegui/elements/value_element.py
#	nicegui/events.py
Falko Schindler 2 år sedan
förälder
incheckning
3837944d6c

+ 0 - 1
README.md

@@ -80,7 +80,6 @@ You can call `ui.run()` with optional arguments:
 - `dark`: whether to use Quasar's dark mode (default: `False`, use `None` for "auto" mode)
 - `dark`: whether to use Quasar's dark mode (default: `False`, use `None` for "auto" mode)
 - `reload`: automatically reload the ui on file changes (default: `True`)
 - `reload`: automatically reload the ui on file changes (default: `True`)
 - `show`: automatically open the ui in a browser tab (default: `True`)
 - `show`: automatically open the ui in a browser tab (default: `True`)
-- `on_connect`: default function or coroutine which is called for each new client connection; the optional `request` argument provides session infos
 - `uvicorn_logging_level`: logging level for uvicorn server (default: `'warning'`)
 - `uvicorn_logging_level`: logging level for uvicorn server (default: `'warning'`)
 - `uvicorn_reload_dirs`: string with comma-separated list for directories to be monitored (default is current working directory only)
 - `uvicorn_reload_dirs`: string with comma-separated list for directories to be monitored (default is current working directory only)
 - `uvicorn_reload_includes`: string with comma-separated list of glob-patterns which trigger reload on modification (default: `'.py'`)
 - `uvicorn_reload_includes`: string with comma-separated list of glob-patterns which trigger reload on modification (default: `'.py'`)

+ 1 - 1
main.py

@@ -360,7 +360,7 @@ with example(async_dialog):
 
 
     async def show():
     async def show():
         result = await dialog
         result = await dialog
-        await ui.notify(f'You chose {result}')
+        ui.notify(f'You chose {result}')
 
 
     ui.button('Await a dialog', on_click=show)
     ui.button('Await a dialog', on_click=show)
 
 

+ 0 - 1
nicegui/binding.py

@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
 import asyncio
 import asyncio
 import logging
 import logging
 import time
 import time

+ 1 - 2
nicegui/config.py

@@ -2,7 +2,7 @@ import ast
 import inspect
 import inspect
 import os
 import os
 from dataclasses import dataclass
 from dataclasses import dataclass
-from typing import Callable, Optional
+from typing import Optional
 
 
 from . import globals
 from . import globals
 
 
@@ -17,7 +17,6 @@ class Config():
     dark: Optional[bool] = False
     dark: Optional[bool] = False
     reload: bool = True
     reload: bool = True
     show: bool = True
     show: bool = True
-    on_connect: Optional[Callable] = None
     uvicorn_logging_level: str = 'warning'
     uvicorn_logging_level: str = 'warning'
     uvicorn_reload_dirs: str = '.'
     uvicorn_reload_dirs: str = '.'
     uvicorn_reload_includes: str = '*.py'
     uvicorn_reload_includes: str = '*.py'

+ 1 - 2
nicegui/elements/button.py

@@ -24,8 +24,7 @@ class Button(Element):
         self.bind_text_to(self.view, 'label')
         self.bind_text_to(self.view, 'label')
 
 
         def process_event(view, event) -> Optional[bool]:
         def process_event(view, event) -> Optional[bool]:
-            socket = event.get('websocket')
-            return handle_event(on_click, ClickEventArguments(sender=self, socket=socket))
+            return handle_event(on_click, ClickEventArguments(sender=self, socket=event.get('websocket')))
 
 
         view.on('click', process_event)
         view.on('click', process_event)
 
 

+ 1 - 1
nicegui/elements/color_picker.py

@@ -21,7 +21,7 @@ class ColorPicker(Element):
             </q-popup-proxy>''')
             </q-popup-proxy>''')
 
 
         def handle_pick(sender, msg: Dict):
         def handle_pick(sender, msg: Dict):
-            return handle_event(on_pick, ColorPickEventArguments(sender=self, color=msg.value))
+            return handle_event(on_pick, ColorPickEventArguments(sender=self, socket=msg.websocket, color=msg.value))
         view.name_dict['color_input'].on('change', handle_pick)
         view.name_dict['color_input'].on('change', handle_pick)
         view.name_dict['color_input'].disable_input_event = True
         view.name_dict['color_input'].disable_input_event = True
         view.name_dict['popup'].value = value
         view.name_dict['popup'].value = value

+ 1 - 2
nicegui/elements/menu_item.py

@@ -21,8 +21,7 @@ class MenuItem(Element):
         view = jp.QItem(text=text, clickable=True, temp=False)
         view = jp.QItem(text=text, clickable=True, temp=False)
 
 
         def handle_click(view, event) -> Optional[bool]:
         def handle_click(view, event) -> Optional[bool]:
-            socket = event.get('websocket')
-            result = handle_event(on_click, ClickEventArguments(sender=self, socket=socket))
+            result = handle_event(on_click, ClickEventArguments(sender=self, socket=event.get('websocket')))
             if auto_close:
             if auto_close:
                 assert isinstance(self.parent_view, jp.QMenu)
                 assert isinstance(self.parent_view, jp.QMenu)
                 self.parent_view.value = False
                 self.parent_view.value = False

+ 7 - 7
nicegui/elements/page.py

@@ -8,7 +8,7 @@ import justpy as jp
 from pygments.formatters import HtmlFormatter
 from pygments.formatters import HtmlFormatter
 from starlette.requests import Request
 from starlette.requests import Request
 
 
-from ..globals import config, page_stack, view_stack
+from ..globals import config, connect_handlers, page_stack, view_stack
 from ..helpers import is_coroutine
 from ..helpers import is_coroutine
 
 
 
 
@@ -43,7 +43,7 @@ class Page(jp.QuasarPage):
         self.dark = dark if dark is not ... else config.dark
         self.dark = dark if dark is not ... else config.dark
         self.tailwind = True  # use Tailwind classes instead of Quasars
         self.tailwind = True  # use Tailwind classes instead of Quasars
         self.css = css
         self.css = css
-        self.on_connect = on_connect or config.on_connect
+        self.on_connect = on_connect
 
 
         self.waiting_javascript_commands: dict[str, str] = {}
         self.waiting_javascript_commands: dict[str, str] = {}
         self.on('result_ready', self.handle_javascript_result)
         self.on('result_ready', self.handle_javascript_result)
@@ -55,13 +55,13 @@ class Page(jp.QuasarPage):
         jp.Route(route, self._route_function)
         jp.Route(route, self._route_function)
 
 
     async def _route_function(self, request: Request):
     async def _route_function(self, request: Request):
-        if self.on_connect:
-            arg_count = len(inspect.signature(self.on_connect).parameters)
-            is_coro = is_coroutine(self.on_connect)
+        for connect_handler in connect_handlers + ([self.on_connect] if self.on_connect else []):
+            arg_count = len(inspect.signature(connect_handler).parameters)
+            is_coro = is_coroutine(connect_handler)
             if arg_count == 1:
             if arg_count == 1:
-                await self.on_connect(request) if is_coro else self.on_connect(request)
+                await connect_handler(request) if is_coro else connect_handler(request)
             elif arg_count == 0:
             elif arg_count == 0:
-                await self.on_connect() if is_coro else self.on_connect()
+                await connect_handler() if is_coro else connect_handler()
             else:
             else:
                 raise ValueError(f'invalid number of arguments (0 or 1 allowed, got {arg_count})')
                 raise ValueError(f'invalid number of arguments (0 or 1 allowed, got {arg_count})')
         return self
         return self

+ 1 - 1
nicegui/elements/upload.py

@@ -34,7 +34,7 @@ class Upload(Element):
             for form_data in msg.form_data:
             for form_data in msg.form_data:
                 if form_data.type == 'file':
                 if form_data.type == 'file':
                     files = [base64.b64decode(f.file_content) for f in form_data.files]
                     files = [base64.b64decode(f.file_content) for f in form_data.files]
-                    arguments = UploadEventArguments(sender=self, files=files)
+                    arguments = UploadEventArguments(sender=self, socket=msg.websocket, files=files)
                     if handle_event(self.upload_handler, arguments):
                     if handle_event(self.upload_handler, arguments):
                         page_update = None
                         page_update = None
             return page_update
             return page_update

+ 2 - 3
nicegui/elements/value_element.py

@@ -9,9 +9,8 @@ from .element import Element
 
 
 
 
 class ValueElement(Element):
 class ValueElement(Element):
-    value = BindableProperty(
-        on_change=lambda sender, value: handle_event(sender.change_handler,
-                                                     ValueChangeEventArguments(sender=sender, value=value)))
+    value = BindableProperty(on_change=lambda sender, value: handle_event(
+        sender.change_handler, ValueChangeEventArguments(sender=sender, socket=None, value=value)))
 
 
     def __init__(self, view: jp.HTMLBaseComponent, *, value: Any, on_change: Optional[Callable]):
     def __init__(self, view: jp.HTMLBaseComponent, *, value: Any, on_change: Optional[Callable]):
         super().__init__(view)
         super().__init__(view)

+ 15 - 9
nicegui/events.py

@@ -1,8 +1,8 @@
 import traceback
 import traceback
+from dataclasses import dataclass
 from inspect import signature
 from inspect import signature
 from typing import Any, Callable, List, Optional
 from typing import Any, Callable, List, Optional
 
 
-from pydantic import BaseModel
 from starlette.websockets import WebSocket
 from starlette.websockets import WebSocket
 
 
 from .elements.element import Element
 from .elements.element import Element
@@ -10,50 +10,57 @@ from .helpers import is_coroutine
 from .task_logger import create_task
 from .task_logger import create_task
 
 
 
 
-class EventArguments(BaseModel):
-    class Config:
-        arbitrary_types_allowed = True
+@dataclass
+class EventArguments:
     sender: Element
     sender: Element
     socket: Optional[WebSocket]
     socket: Optional[WebSocket]
 
 
 
 
+@dataclass
 class ClickEventArguments(EventArguments):
 class ClickEventArguments(EventArguments):
     pass
     pass
 
 
 
 
+@dataclass
 class ColorPickEventArguments(EventArguments):
 class ColorPickEventArguments(EventArguments):
     color: str
     color: str
 
 
 
 
+@dataclass
 class MouseEventArguments(EventArguments):
 class MouseEventArguments(EventArguments):
     type: str
     type: str
     image_x: float
     image_x: float
     image_y: float
     image_y: float
 
 
 
 
+@dataclass
 class UploadEventArguments(EventArguments):
 class UploadEventArguments(EventArguments):
     files: List[bytes]
     files: List[bytes]
 
 
 
 
+@dataclass
 class ValueChangeEventArguments(EventArguments):
 class ValueChangeEventArguments(EventArguments):
     value: Any
     value: Any
 
 
 
 
-class KeyboardAction(BaseModel):
+@dataclass
+class KeyboardAction:
     keypress: bool
     keypress: bool
     keydown: bool
     keydown: bool
     keyup: bool
     keyup: bool
     repeat: bool
     repeat: bool
 
 
 
 
-class KeyboardModifiers(BaseModel):
+@dataclass
+class KeyboardModifiers:
     alt: bool
     alt: bool
     ctrl: bool
     ctrl: bool
     meta: bool
     meta: bool
     shift: bool
     shift: bool
 
 
 
 
-class KeyboardKey(BaseModel):
+@dataclass
+class KeyboardKey:
     name: str
     name: str
     code: str
     code: str
     location: int
     location: int
@@ -210,9 +217,8 @@ class KeyboardKey(BaseModel):
         return self.name == 'F12'
         return self.name == 'F12'
 
 
 
 
+@dataclass
 class KeyEventArguments(EventArguments):
 class KeyEventArguments(EventArguments):
-    class Config:
-        arbitrary_types_allowed = True
     action: KeyboardAction
     action: KeyboardAction
     key: KeyboardKey
     key: KeyboardKey
     modifiers: KeyboardModifiers
     modifiers: KeyboardModifiers

+ 4 - 1
nicegui/globals.py

@@ -2,7 +2,7 @@ from __future__ import annotations
 
 
 import asyncio
 import asyncio
 import logging
 import logging
-from typing import TYPE_CHECKING, List
+from typing import TYPE_CHECKING, Awaitable, Callable, List, Union
 
 
 if TYPE_CHECKING:
 if TYPE_CHECKING:
     import justpy as jp
     import justpy as jp
@@ -17,3 +17,6 @@ page_stack: List['Page'] = []
 view_stack: List['jp.HTMLBaseComponent'] = []
 view_stack: List['jp.HTMLBaseComponent'] = []
 tasks: List[asyncio.tasks.Task] = []
 tasks: List[asyncio.tasks.Task] = []
 log: logging.Logger = logging.getLogger('nicegui')
 log: logging.Logger = logging.getLogger('nicegui')
+connect_handlers: List[Union[Callable, Awaitable]] = []
+startup_handlers: List[Union[Callable, Awaitable]] = []
+shutdown_handlers: List[Union[Callable, Awaitable]] = []

+ 8 - 7
nicegui/lifecycle.py

@@ -1,14 +1,15 @@
-from typing import Awaitable, Callable, List, Union
+from typing import Awaitable, Callable, Union
 
 
-startup_tasks: List[Union[Callable, Awaitable]] = []
+from .globals import connect_handlers, shutdown_handlers, startup_handlers
 
 
 
 
-def on_startup(self, task: Union[Callable, Awaitable]):
-    self.startup_tasks.append(task)
+def on_connect(self, handler: Union[Callable, Awaitable]):
+    connect_handlers.append(handler)
 
 
 
 
-shutdown_tasks: List[Union[Callable, Awaitable]] = []
+def on_startup(self, handler: Union[Callable, Awaitable]):
+    startup_handlers.append(handler)
 
 
 
 
-def on_shutdown(self, task: Union[Callable, Awaitable]):
-    self.shutdown_tasks.append(task)
+def on_shutdown(self, handler: Union[Callable, Awaitable]):
+    shutdown_handlers.append(handler)

+ 25 - 8
nicegui/nicegui.py

@@ -1,34 +1,51 @@
-#!/usr/bin/env python3
 # isort:skip_file
 # isort:skip_file
 from typing import Awaitable, Callable
 from typing import Awaitable, Callable
 
 
-from .ui import Ui  # NOTE: before justpy
-import justpy as jp
+if True:  # NOTE: prevent formatter from mixing up these lines
+    import builtins
+    print_backup = builtins.print
+    builtins.print = lambda *_, **__: None
+    from .ui import Ui  # NOTE: before justpy
+    import justpy as jp
+    builtins.print = print_backup
 
 
 from . import binding, globals
 from . import binding, globals
 from .task_logger import create_task
 from .task_logger import create_task
 from .timer import Timer
 from .timer import Timer
 
 
+jp.app.router.on_startup.clear()  # NOTE: remove JustPy's original startup function
+
+
+@jp.app.on_event('startup')
+async def patched_justpy_startup():
+    jp.WebPage.loop = jp.asyncio.get_event_loop()
+    jp.JustPy.loop = jp.WebPage.loop
+    jp.JustPy.STATIC_DIRECTORY = jp.os.environ["STATIC_DIRECTORY"]
+    print(f'NiceGUI ready to go on {"https" if jp.SSL_KEYFILE else "http"}://{jp.HOST}:{jp.PORT}')
+
 
 
 @jp.app.on_event('startup')
 @jp.app.on_event('startup')
 def startup():
 def startup():
     globals.tasks.extend(create_task(t.coro, name=t.name) for t in Timer.prepared_coroutines)
     globals.tasks.extend(create_task(t.coro, name=t.name) for t in Timer.prepared_coroutines)
     Timer.prepared_coroutines.clear()
     Timer.prepared_coroutines.clear()
-    globals.tasks.extend(create_task(t, name='startup task') for t in Ui.startup_tasks if isinstance(t, Awaitable))
-    [safe_invoke(t) for t in Ui.startup_tasks if isinstance(t, Callable)]
+    globals.tasks.extend(create_task(t, name='startup task')
+                         for t in globals.startup_handlers if isinstance(t, Awaitable))
+    [safe_invoke(t) for t in globals.startup_handlers if isinstance(t, Callable)]
     jp.run_task(binding.loop())
     jp.run_task(binding.loop())
 
 
 
 
 @jp.app.on_event('shutdown')
 @jp.app.on_event('shutdown')
 def shutdown():
 def shutdown():
-    [create_task(t, name='shutdown task') for t in Ui.shutdown_tasks if isinstance(t, Awaitable)]
-    [safe_invoke(t) for t in Ui.shutdown_tasks if isinstance(t, Callable)]
+    [create_task(t, name='shutdown task') for t in globals.shutdown_handlers if isinstance(t, Awaitable)]
+    [safe_invoke(t) for t in globals.shutdown_handlers if isinstance(t, Callable)]
     [t.cancel() for t in globals.tasks]
     [t.cancel() for t in globals.tasks]
 
 
 
 
 def safe_invoke(func: Callable):
 def safe_invoke(func: Callable):
     try:
     try:
-        func()
+        result = func()
+        if isinstance(result, Awaitable):
+            create_task(result)
     except:
     except:
         globals.log.exception(f'could not invoke {func}')
         globals.log.exception(f'could not invoke {func}')
 
 

+ 4 - 2
nicegui/run.py

@@ -2,7 +2,7 @@ import inspect
 import os
 import os
 import sys
 import sys
 import webbrowser
 import webbrowser
-from typing import Callable, Optional
+from typing import Optional
 
 
 import uvicorn
 import uvicorn
 
 
@@ -34,7 +34,6 @@ def run(self, *,
         dark: Optional[bool] = False,
         dark: Optional[bool] = False,
         reload: bool = True,
         reload: bool = True,
         show: bool = True,
         show: bool = True,
-        on_connect: Optional[Callable] = None,
         uvicorn_logging_level: str = 'warning',
         uvicorn_logging_level: str = 'warning',
         uvicorn_reload_dirs: str = '.',
         uvicorn_reload_dirs: str = '.',
         uvicorn_reload_includes: str = '*.py',
         uvicorn_reload_includes: str = '*.py',
@@ -43,6 +42,9 @@ def run(self, *,
         binding_refresh_interval: float = 0.1,
         binding_refresh_interval: float = 0.1,
         exclude: str = '',
         exclude: str = '',
         ):
         ):
+    if globals.config.interactive:
+        print('Error: Unexpected ui.run() in interactive mode.', flush=True)
+        sys.exit()
 
 
     if globals.config.interactive or reload == False:  # NOTE: if reload == True we already started uvicorn above
     if globals.config.interactive or reload == False:  # NOTE: if reload == True we already started uvicorn above
         if show:
         if show:

+ 1 - 1
nicegui/static/templates/js/event_handler.js

@@ -133,7 +133,7 @@ function send_to_server(e, event_type, debug_flag) {
     }
     }
     if (use_websockets) {
     if (use_websockets) {
         if (web_socket_closed) {
         if (web_socket_closed) {
-            window.location.reload();
+            setTimeout(function(){ window.location.reload(); }, 100);
             return;
             return;
         }
         }
         if (websocket_ready) {
         if (websocket_ready) {

+ 2 - 3
nicegui/static/templates/main.html

@@ -86,15 +86,14 @@
 
 
         socket.addEventListener('error', function (event) {
         socket.addEventListener('error', function (event) {
             console.log('Websocket closed');
             console.log('Websocket closed');
-            window.location.reload();
-            // setTimeout(function(){ window.location.reload(); }, 3000);
+            setTimeout(function(){ window.location.reload(); }, 100);
         });
         });
 
 
         var web_socket_closed = false;
         var web_socket_closed = false;
         socket.addEventListener('close', function (event) {
         socket.addEventListener('close', function (event) {
             console.log('Websocket closed');
             console.log('Websocket closed');
             web_socket_closed = true;
             web_socket_closed = true;
-            window.location.reload()
+            setTimeout(function(){ window.location.reload(); }, 100);
         });
         });
 
 
         socket.addEventListener('message', function (event) {
         socket.addEventListener('message', function (event) {

+ 1 - 1
nicegui/ui.py

@@ -45,7 +45,7 @@ class Ui:
     from .elements.tree import Tree as tree
     from .elements.tree import Tree as tree
     from .elements.update import update
     from .elements.update import update
     from .elements.upload import Upload as upload
     from .elements.upload import Upload as upload
-    from .lifecycle import on_shutdown, on_startup, shutdown_tasks, startup_tasks
+    from .lifecycle import on_connect, on_shutdown, on_startup
     from .routes import add_route, add_static_files, get
     from .routes import add_route, add_static_files, get
     from .timer import Timer as timer
     from .timer import Timer as timer
 
 

+ 50 - 102
poetry.lock

@@ -491,21 +491,6 @@ category = "main"
 optional = false
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 
-[[package]]
-name = "pydantic"
-version = "1.9.1"
-description = "Data validation and settings management using python type hints"
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-typing-extensions = ">=3.7.4.3"
-
-[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
-
 [[package]]
 [[package]]
 name = "pygments"
 name = "pygments"
 version = "2.12.0"
 version = "2.12.0"
@@ -769,7 +754,7 @@ python-versions = "*"
 
 
 [[package]]
 [[package]]
 name = "websockets"
 name = "websockets"
-version = "10.2"
+version = "10.3"
 description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
 description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
 category = "main"
 category = "main"
 optional = false
 optional = false
@@ -790,7 +775,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
 [metadata]
 [metadata]
 lock-version = "1.1"
 lock-version = "1.1"
 python-versions = "^3.7"
 python-versions = "^3.7"
-content-hash = "09a221174fd9d25750a59ba0b96662c0e18d50bdf80c8f02dc5a8b50eaa9c44c"
+content-hash = "24a06d3451f0eb1de85b434d014f84ddee5b86ed21b1f0d5478132309e8af0ac"
 
 
 [metadata.files]
 [metadata.files]
 addict = [
 addict = [
@@ -1214,43 +1199,6 @@ pycparser = [
     {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
     {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
     {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
     {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
 ]
 ]
-pydantic = [
-    {file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"},
-    {file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"},
-    {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"},
-    {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"},
-    {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"},
-    {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"},
-    {file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"},
-    {file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"},
-    {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"},
-    {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"},
-    {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"},
-    {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"},
-    {file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"},
-    {file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"},
-    {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"},
-    {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"},
-    {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"},
-    {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"},
-    {file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"},
-    {file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"},
-    {file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"},
-    {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"},
-    {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"},
-    {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"},
-    {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"},
-    {file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"},
-    {file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"},
-    {file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"},
-    {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"},
-    {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"},
-    {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"},
-    {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"},
-    {file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"},
-    {file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"},
-    {file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"},
-]
 pygments = [
 pygments = [
     {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
     {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
     {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
     {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
@@ -1341,54 +1289,54 @@ webencodings = [
     {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
     {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
 ]
 ]
 websockets = [
 websockets = [
-    {file = "websockets-10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5396710f86a306cf52f87fd8ea594a0e894ba0cc5a36059eaca3a477dc332aa"},
-    {file = "websockets-10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b22bdc795e62e71118b63e14a08bacfa4f262fd2877de7e5b950f5ac16b0348f"},
-    {file = "websockets-10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b04270b5613f245ec84bb2c6a482a9d009aefad37c0575f6cda8499125d5d5c"},
-    {file = "websockets-10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5c335dc0e7dc271ef36df3f439868b3c790775f345338c2f61a562f1074187b"},
-    {file = "websockets-10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a009eb551c46fd79737791c0c833fc0e5b56bcd1c3057498b262d660b92e9cd"},
-    {file = "websockets-10.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a10c0c1ee02164246f90053273a42d72a3b2452a7e7486fdae781138cf7fbe2d"},
-    {file = "websockets-10.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b38a5c9112e3dbbe45540f7b60c5204f49b3cb501b40950d6ab34cd202ab1d0"},
-    {file = "websockets-10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2aa9b91347ecd0412683f28aabe27f6bad502d89bd363b76e0a3508b1596402e"},
-    {file = "websockets-10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b7fe45ae43ac814beb8ca09d6995b56800676f2cfa8e23f42839dc69bba34a42"},
-    {file = "websockets-10.2-cp310-cp310-win32.whl", hash = "sha256:cef40a1b183dcf39d23b392e9dd1d9b07ab9c46aadf294fff1350fb79146e72b"},
-    {file = "websockets-10.2-cp310-cp310-win_amd64.whl", hash = "sha256:c21a67ab9a94bd53e10bba21912556027fea944648a09e6508415ad14e37c325"},
-    {file = "websockets-10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb316b87cbe3c0791c2ad92a5a36bf6adc87c457654335810b25048c1daa6fd5"},
-    {file = "websockets-10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f14bd10e170abc01682a9f8b28b16e6f20acf6175945ef38db6ffe31b0c72c3f"},
-    {file = "websockets-10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fa35c5d1830d0fb7b810324e9eeab9aa92e8f273f11fdbdc0741dcded6d72b9f"},
-    {file = "websockets-10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:71a4491cfe7a9f18ee57d41163cb6a8a3fa591e0f0564ca8b0ed86b2a30cced4"},
-    {file = "websockets-10.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6193bbc1ee63aadeb9a4d81de0e19477401d150d506aee772d8380943f118186"},
-    {file = "websockets-10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8beac786a388bb99a66c3be4ab0fb38273c0e3bc17f612a4e0a47c4fc8b9c045"},
-    {file = "websockets-10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c67d9cacb3f6537ca21e9b224d4fd08481538e43bcac08b3d93181b0816def39"},
-    {file = "websockets-10.2-cp37-cp37m-win32.whl", hash = "sha256:a03a25d95cc7400bd4d61a63460b5d85a7761c12075ee2f51de1ffe73aa593d3"},
-    {file = "websockets-10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f8296b8408ec6853b26771599990721a26403e62b9de7e50ac0a056772ac0b5e"},
-    {file = "websockets-10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7bb9d8a6beca478c7e9bdde0159bd810cc1006ad6a7cb460533bae39da692ca2"},
-    {file = "websockets-10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:05f6e9757017270e7a92a2975e2ae88a9a582ffc4629086fd6039aa80e99cd86"},
-    {file = "websockets-10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1c9031e90ebfc486e9cdad532b94004ade3aa39a31d3c46c105bb0b579cd2490"},
-    {file = "websockets-10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82bc33db6d8309dc27a3bee11f7da2288ad925fcbabc2a4bb78f7e9c56249baf"},
-    {file = "websockets-10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:24b879ba7db12bb525d4e58089fcbe6a3df3ce4666523183654170e86d372cbe"},
-    {file = "websockets-10.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf931c33db9c87c53d009856045dd524e4a378445693382a920fa1e0eb77c36c"},
-    {file = "websockets-10.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:669e54228a4d9457abafed27cbf0e2b9f401445c4dfefc12bf8e4db9751703b8"},
-    {file = "websockets-10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bffc65442dd35c473ca9790a3fa3ba06396102a950794f536783f4b8060af8dd"},
-    {file = "websockets-10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4d110a84b63c5cfdd22485acc97b8b919aefeecd6300c0c9d551e055b9a88ea"},
-    {file = "websockets-10.2-cp38-cp38-win32.whl", hash = "sha256:117383d0a17a0dda349f7a8790763dde75c1508ff8e4d6e8328b898b7df48397"},
-    {file = "websockets-10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b66421f9f13d4df60cd48ab977ed2c2b6c9147ae1a33caf5a9f46294422fda1"},
-    {file = "websockets-10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ac081aa0307f263d63c5ff0727935c736c8dad51ddf2dc9f5d0c4759842aefaa"},
-    {file = "websockets-10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4059e2ccbe6587b6dc9a01db5fc49ead9a884faa4076eea96c5ec62cb32f42a"},
-    {file = "websockets-10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9ca2ca05a4c29179f06cf6727b45dba5d228da62623ec9df4184413d8aae6cb9"},
-    {file = "websockets-10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97950c7c844ec6f8d292440953ae18b99e3a6a09885e09d20d5e7ecd9b914cf8"},
-    {file = "websockets-10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:98f57b3120f8331cd7440dbe0e776474f5e3632fdaa474af1f6b754955a47d71"},
-    {file = "websockets-10.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a72b92f96e5e540d5dda99ee3346e199ade8df63152fa3c737260da1730c411f"},
-    {file = "websockets-10.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:038afef2a05893578d10dadbdbb5f112bd115c46347e1efe99f6a356ff062138"},
-    {file = "websockets-10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f09f46b1ff6d09b01c7816c50bd1903cf7d02ebbdb63726132717c2fcda835d5"},
-    {file = "websockets-10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2349fa81b6b959484bb2bda556ccb9eb70ba68987646a0f8a537a1a18319fb03"},
-    {file = "websockets-10.2-cp39-cp39-win32.whl", hash = "sha256:bef03a51f9657fb03d8da6ccd233fe96e04101a852f0ffd35f5b725b28221ff3"},
-    {file = "websockets-10.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c1f3b18c8162e3b09761d0c6a0305fd642934202541cc511ef972cb9463261e"},
-    {file = "websockets-10.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5a38a0175ae82e4a8c4bac29fc01b9ee26d7d5a614e5ee11e7813c68a7d938ce"},
-    {file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e56606842bb24e16e36ae7eb308d866b4249cf0be8f63b212f287eeb76b124"},
-    {file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0f73cb2526d6da268e86977b2c4b58f2195994e53070fe567d5487c6436047e6"},
-    {file = "websockets-10.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cd02f36d37e503aca88ab23cc0a1a0e92a263d37acf6331521eb38040dcf77b"},
-    {file = "websockets-10.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:56d48eebe9e39ce0d68701bce3b21df923aa05dcc00f9fd8300de1df31a7c07c"},
-    {file = "websockets-10.2.tar.gz", hash = "sha256:8351c3c86b08156337b0e4ece0e3c5ec3e01fcd14e8950996832a23c99416098"},
+    {file = "websockets-10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978"},
+    {file = "websockets-10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500"},
+    {file = "websockets-10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b"},
+    {file = "websockets-10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c"},
+    {file = "websockets-10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8"},
+    {file = "websockets-10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677"},
+    {file = "websockets-10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e"},
+    {file = "websockets-10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f"},
+    {file = "websockets-10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47"},
+    {file = "websockets-10.3-cp310-cp310-win32.whl", hash = "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae"},
+    {file = "websockets-10.3-cp310-cp310-win_amd64.whl", hash = "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079"},
+    {file = "websockets-10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916"},
+    {file = "websockets-10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb"},
+    {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79"},
+    {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d"},
+    {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98"},
+    {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e"},
+    {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6"},
+    {file = "websockets-10.3-cp37-cp37m-win32.whl", hash = "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1"},
+    {file = "websockets-10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4"},
+    {file = "websockets-10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36"},
+    {file = "websockets-10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69"},
+    {file = "websockets-10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd"},
+    {file = "websockets-10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2"},
+    {file = "websockets-10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c"},
+    {file = "websockets-10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e"},
+    {file = "websockets-10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991"},
+    {file = "websockets-10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442"},
+    {file = "websockets-10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76"},
+    {file = "websockets-10.3-cp38-cp38-win32.whl", hash = "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559"},
+    {file = "websockets-10.3-cp38-cp38-win_amd64.whl", hash = "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d"},
+    {file = "websockets-10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094"},
+    {file = "websockets-10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667"},
+    {file = "websockets-10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731"},
+    {file = "websockets-10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9"},
+    {file = "websockets-10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680"},
+    {file = "websockets-10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247"},
+    {file = "websockets-10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af"},
+    {file = "websockets-10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3"},
+    {file = "websockets-10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8"},
+    {file = "websockets-10.3-cp39-cp39-win32.whl", hash = "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582"},
+    {file = "websockets-10.3-cp39-cp39-win_amd64.whl", hash = "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02"},
+    {file = "websockets-10.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7"},
+    {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f"},
+    {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4"},
+    {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755"},
+    {file = "websockets-10.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55"},
+    {file = "websockets-10.3.tar.gz", hash = "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"},
 ]
 ]
 zipp = [
 zipp = [
     {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
     {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},

+ 1 - 2
pyproject.toml

@@ -19,8 +19,7 @@ docutils = "^0.17.1"
 asttokens = "^2.0.5"
 asttokens = "^2.0.5"
 uvicorn = "0.17.6"
 uvicorn = "0.17.6"
 watchgod = "^0.7"
 watchgod = "^0.7"
-pydantic = "^1.8.2"
-websockets = "10.2"
+websockets = "10.3"
 httpx = "^0.23.0"
 httpx = "^0.23.0"
 
 
 [tool.poetry.dev-dependencies]
 [tool.poetry.dev-dependencies]

+ 1 - 1
test_startup.sh

@@ -4,7 +4,7 @@ run() {
     output=`{ timeout 10 python3 $1; } 2>&1`
     output=`{ timeout 10 python3 $1; } 2>&1`
     exitcode=$?
     exitcode=$?
     test $exitcode -eq 124 && exitcode=0 # exitcode 124 is comming from "timeout command above"
     test $exitcode -eq 124 && exitcode=0 # exitcode 124 is comming from "timeout command above"
-    echo $output | grep "JustPy ready to go" > /dev/null || exitcode=1
+    echo $output | grep "NiceGUI ready to go" > /dev/null || exitcode=1
     echo $output | grep "Traceback" > /dev/null && exitcode=1
     echo $output | grep "Traceback" > /dev/null && exitcode=1
     echo $output | grep "Error" > /dev/null && exitcode=1
     echo $output | grep "Error" > /dev/null && exitcode=1
     if test $exitcode -ne 0; then
     if test $exitcode -ne 0; then