Falko Schindler пре 2 година
родитељ
комит
b1c4bacbc9

+ 2 - 0
mypy.ini

@@ -0,0 +1,2 @@
+[mypy]
+ignore_missing_imports = True

+ 1 - 1
nicegui/app.py

@@ -43,7 +43,7 @@ class App(FastAPI):
         """
         """
         globals.shutdown_handlers.append(handler)
         globals.shutdown_handlers.append(handler)
 
 
-    def on_exception(self, handler: Union[Callable, Awaitable]) -> None:
+    def on_exception(self, handler: Callable) -> None:
         """Called when an exception occurs.
         """Called when an exception occurs.
 
 
         The callback has an optional parameter of `Exception`.
         The callback has an optional parameter of `Exception`.

+ 2 - 1
nicegui/background_tasks.py

@@ -22,7 +22,8 @@ def create(coroutine: Awaitable[T], *, name: str = 'unnamed task') -> 'asyncio.T
     See https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task.
     See https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task.
     """
     """
     assert globals.loop is not None
     assert globals.loop is not None
-    task = globals.loop.create_task(coroutine, name=name) if name_supported else globals.loop.create_task(coroutine)
+    task: asyncio.Task = \
+        globals.loop.create_task(coroutine, name=name) if name_supported else globals.loop.create_task(coroutine)
     task.add_done_callback(_handle_task_result)
     task.add_done_callback(_handle_task_result)
     running_tasks.add(task)
     running_tasks.add(task)
     task.add_done_callback(running_tasks.discard)
     task.add_done_callback(running_tasks.discard)

+ 1 - 1
nicegui/binding.py

@@ -25,7 +25,7 @@ def set_attribute(obj: Union[object, Dict], name: str, value: Any) -> None:
         setattr(obj, name, value)
         setattr(obj, name, value)
 
 
 
 
-async def loop():
+async def loop() -> None:
     while True:
     while True:
         visited: Set[Tuple[int, str]] = set()
         visited: Set[Tuple[int, str]] = set()
         t = time.time()
         t = time.time()

+ 4 - 4
nicegui/elements/mixins/value_element.py

@@ -1,4 +1,4 @@
-from typing import Any, Callable, Dict, Optional
+from typing import Any, Callable, Dict, List, Optional
 
 
 from typing_extensions import Self
 from typing_extensions import Self
 
 
@@ -8,9 +8,9 @@ from ...events import ValueChangeEventArguments, handle_event
 
 
 
 
 class ValueElement(Element):
 class ValueElement(Element):
-    VALUE_PROP = 'model-value'
-    EVENT_ARGS = ['value']
-    LOOPBACK = True
+    VALUE_PROP: str = 'model-value'
+    EVENT_ARGS: Optional[List[str]] = ['value']
+    LOOPBACK: bool = True
     value = BindableProperty(on_change=lambda sender, value: sender.on_value_change(value))
     value = BindableProperty(on_change=lambda sender, value: sender.on_value_change(value))
 
 
     def __init__(self, *,
     def __init__(self, *,

+ 3 - 2
nicegui/elements/mixins/visibility.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Any, Callable
+from typing import TYPE_CHECKING, Any, Callable, cast
 
 
 from typing_extensions import Self
 from typing_extensions import Self
 
 
@@ -79,11 +79,12 @@ class Visibility:
         """
         """
         self.visible = visible
         self.visible = visible
 
 
-    def on_visibility_change(self: 'Element', visible: str) -> None:
+    def on_visibility_change(self, visible: str) -> None:
         """Called when the visibility of this element changes.
         """Called when the visibility of this element changes.
 
 
         :param visible: Whether the element should be visible.
         :param visible: Whether the element should be visible.
         """
         """
+        self = cast('Element', self)
         if visible and 'hidden' in self._classes:
         if visible and 'hidden' in self._classes:
             self._classes.remove('hidden')
             self._classes.remove('hidden')
             self.update()
             self.update()

+ 3 - 3
nicegui/elements/scene_object3d.py

@@ -1,12 +1,12 @@
 import uuid
 import uuid
-from typing import TYPE_CHECKING, Any, List, Optional, cast
+from typing import TYPE_CHECKING, Any, List, Optional, Union, cast
 
 
 import numpy as np
 import numpy as np
 
 
 from .. import globals
 from .. import globals
 
 
 if TYPE_CHECKING:
 if TYPE_CHECKING:
-    from .scene import Scene
+    from .scene import Scene, SceneObject
 
 
 
 
 class Object3D:
 class Object3D:
@@ -17,7 +17,7 @@ class Object3D:
         self.name: Optional[str] = None
         self.name: Optional[str] = None
         self.scene: 'Scene' = cast('Scene', globals.get_slot().parent)
         self.scene: 'Scene' = cast('Scene', globals.get_slot().parent)
         self.scene.objects[self.id] = self
         self.scene.objects[self.id] = self
-        self.parent: Object3D = self.scene.stack[-1]
+        self.parent: Union[Object3D, SceneObject] = self.scene.stack[-1]
         self.args: List = list(args)
         self.args: List = list(args)
         self.color: str = '#ffffff'
         self.color: str = '#ffffff'
         self.opacity: float = 1.0
         self.opacity: float = 1.0

+ 6 - 5
nicegui/elements/upload.py

@@ -1,6 +1,6 @@
-from typing import Any, Callable, Optional
+from typing import Any, Callable, Dict, Optional
 
 
-from fastapi import Request, Response
+from fastapi import Request, UploadFile
 
 
 from ..dependencies import register_component
 from ..dependencies import register_component
 from ..events import EventArguments, UploadEventArguments, handle_event
 from ..events import EventArguments, UploadEventArguments, handle_event
@@ -51,14 +51,15 @@ class Upload(DisableableElement):
             self._props['max-files'] = max_files
             self._props['max-files'] = max_files
 
 
         @app.post(self._props['url'])
         @app.post(self._props['url'])
-        async def upload_route(request: Request) -> Response:
+        async def upload_route(request: Request) -> Dict[str, str]:
             for data in (await request.form()).values():
             for data in (await request.form()).values():
+                assert isinstance(data, UploadFile)
                 args = UploadEventArguments(
                 args = UploadEventArguments(
                     sender=self,
                     sender=self,
                     client=self.client,
                     client=self.client,
                     content=data.file,
                     content=data.file,
-                    name=data.filename,
-                    type=data.content_type,
+                    name=data.filename or '',
+                    type=data.content_type or '',
                 )
                 )
                 handle_event(on_upload, args)
                 handle_event(on_upload, args)
             return {'upload': 'success'}
             return {'upload': 'success'}

+ 6 - 4
nicegui/functions/refreshable.py

@@ -1,5 +1,5 @@
 from dataclasses import dataclass
 from dataclasses import dataclass
-from typing import Any, Callable, Dict, List
+from typing import Any, Awaitable, Callable, Dict, List, Tuple, Union
 
 
 from typing_extensions import Self
 from typing_extensions import Self
 
 
@@ -15,10 +15,10 @@ register_component('refreshable', __file__, 'refreshable.js')
 class RefreshableTarget:
 class RefreshableTarget:
     container: Element
     container: Element
     instance: Any
     instance: Any
-    args: List[Any]
+    args: Tuple[Any, ...]
     kwargs: Dict[str, Any]
     kwargs: Dict[str, Any]
 
 
-    def run(self, func: Callable[..., Any]) -> None:
+    def run(self, func: Callable[..., Any]) -> Union[None, Awaitable]:
         if is_coroutine(func):
         if is_coroutine(func):
             async def wait_for_result() -> None:
             async def wait_for_result() -> None:
                 with self.container:
                 with self.container:
@@ -33,6 +33,7 @@ class RefreshableTarget:
                     func(*self.args, **self.kwargs)
                     func(*self.args, **self.kwargs)
                 else:
                 else:
                     func(self.instance, *self.args, **self.kwargs)
                     func(self.instance, *self.args, **self.kwargs)
+            return None  # required by mypy
 
 
 
 
 class refreshable:
 class refreshable:
@@ -51,7 +52,7 @@ class refreshable:
         self.instance = instance
         self.instance = instance
         return self
         return self
 
 
-    def __call__(self, *args: Any, **kwargs: Any) -> None:
+    def __call__(self, *args: Any, **kwargs: Any) -> Union[None, Awaitable]:
         self.prune()
         self.prune()
         target = RefreshableTarget(container=Element('refreshable'), instance=self.instance, args=args, kwargs=kwargs)
         target = RefreshableTarget(container=Element('refreshable'), instance=self.instance, args=args, kwargs=kwargs)
         self.targets.append(target)
         self.targets.append(target)
@@ -65,6 +66,7 @@ class refreshable:
             target.container.clear()
             target.container.clear()
             result = target.run(self.func)
             result = target.run(self.func)
             if is_coroutine(self.func):
             if is_coroutine(self.func):
+                assert result is not None
                 if globals.loop and globals.loop.is_running():
                 if globals.loop and globals.loop.is_running():
                     background_tasks.create(result)
                     background_tasks.create(result)
                 else:
                 else:

+ 8 - 3
nicegui/functions/timer.py

@@ -1,10 +1,11 @@
 import asyncio
 import asyncio
 import time
 import time
-from typing import Any, Callable
+from typing import Any, Callable, Optional
 
 
 from .. import background_tasks, globals
 from .. import background_tasks, globals
 from ..binding import BindableProperty
 from ..binding import BindableProperty
 from ..helpers import is_coroutine
 from ..helpers import is_coroutine
+from ..slot import Slot
 
 
 
 
 class Timer:
 class Timer:
@@ -29,9 +30,9 @@ class Timer:
         :param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
         :param once: whether the callback is only executed once after a delay specified by `interval` (default: `False`)
         """
         """
         self.interval = interval
         self.interval = interval
-        self.callback = callback
+        self.callback: Optional[Callable[..., Any]] = callback
         self.active = active
         self.active = active
-        self.slot = globals.get_slot()
+        self.slot: Optional[Slot] = globals.get_slot()
 
 
         coroutine = self._run_once if once else self._run_in_loop
         coroutine = self._run_once if once else self._run_in_loop
         if globals.state == globals.State.STARTED:
         if globals.state == globals.State.STARTED:
@@ -43,6 +44,7 @@ class Timer:
         try:
         try:
             if not await self._connected():
             if not await self._connected():
                 return
                 return
+            assert self.slot is not None
             with self.slot:
             with self.slot:
                 await asyncio.sleep(self.interval)
                 await asyncio.sleep(self.interval)
                 if globals.state not in {globals.State.STOPPING, globals.State.STOPPED}:
                 if globals.state not in {globals.State.STOPPING, globals.State.STOPPED}:
@@ -54,6 +56,7 @@ class Timer:
         try:
         try:
             if not await self._connected():
             if not await self._connected():
                 return
                 return
+            assert self.slot is not None
             with self.slot:
             with self.slot:
                 while True:
                 while True:
                     if self.slot.parent.client.id not in globals.clients:
                     if self.slot.parent.client.id not in globals.clients:
@@ -76,6 +79,7 @@ class Timer:
 
 
     async def _invoke_callback(self) -> None:
     async def _invoke_callback(self) -> None:
         try:
         try:
+            assert self.callback is not None
             result = self.callback()
             result = self.callback()
             if is_coroutine(self.callback):
             if is_coroutine(self.callback):
                 await result
                 await result
@@ -88,6 +92,7 @@ class Timer:
         See https://github.com/zauberzeug/nicegui/issues/206 for details.
         See https://github.com/zauberzeug/nicegui/issues/206 for details.
         Returns True if the client is connected, False if the client is not connected and the timer should be cancelled.
         Returns True if the client is connected, False if the client is not connected and the timer should be cancelled.
         """
         """
+        assert self.slot is not None
         if self.slot.parent.client.shared:
         if self.slot.parent.client.shared:
             return True
             return True
         else:
         else: