Python: Fix mypy also on the Slint module implementation

This commit is contained in:
Simon Hausmann 2025-02-12 23:02:31 +01:00 committed by Simon Hausmann
parent 607d70b3fa
commit 9001966dc9
6 changed files with 113 additions and 83 deletions

View file

@ -199,7 +199,7 @@ impl CompilationResult {
#[gen_stub_pyclass]
#[pyclass(unsendable)]
struct ComponentDefinition {
pub struct ComponentDefinition {
definition: slint_interpreter::ComponentDefinition,
}

View file

@ -6,7 +6,8 @@ use pyo3_stub_gen::{define_stub_info_gatherer, derive::gen_stub_pyfunction};
mod image;
mod interpreter;
use interpreter::{
CompilationResult, Compiler, ComponentInstance, PyDiagnostic, PyDiagnosticLevel, PyValueType,
CompilationResult, Compiler, ComponentDefinition, ComponentInstance, PyDiagnostic,
PyDiagnosticLevel, PyValueType,
};
mod brush;
mod errors;
@ -45,6 +46,7 @@ fn slint(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Compiler>()?;
m.add_class::<CompilationResult>()?;
m.add_class::<ComponentInstance>()?;
m.add_class::<ComponentDefinition>()?;
m.add_class::<image::PyImage>()?;
m.add_class::<PyValueType>()?;
m.add_class::<PyDiagnosticLevel>()?;

View file

@ -14,7 +14,7 @@ import logging
import importlib
import copy
import typing
import builtins
from typing import Any
import pathlib
from .models import ListModel, Model
from .slint import Image, Color, Brush, Timer, TimerMode
@ -22,27 +22,29 @@ from .slint import Image, Color, Brush, Timer, TimerMode
Struct = native.PyStruct
class CompileError(Exception):
def __init__(self, message, diagnostics):
def __init__(self, message: str, diagnostics: list[native.PyDiagnostic]):
self.message = message
self.diagnostics = diagnostics
class Component:
def show(self):
__instance__: native.ComponentInstance
def show(self) -> None:
self.__instance__.show()
def hide(self):
def hide(self) -> None:
self.__instance__.hide()
def run(self):
def run(self) -> None:
self.__instance__.run()
def _normalize_prop(name):
def _normalize_prop(name: str) -> str:
return name.replace("-", "_")
def _build_global_class(compdef, global_name):
def _build_global_class(compdef: native.ComponentDefinition, global_name: str) -> Any:
properties_and_callbacks = {}
for prop_name in compdef.global_properties(global_name).keys():
@ -51,12 +53,12 @@ def _build_global_class(compdef, global_name):
logging.warning(f"Duplicated property {prop_name}")
continue
def mk_setter_getter(prop_name):
def getter(self):
def mk_setter_getter(prop_name: str): # type: ignore
def getter(self): # type: ignore
return self.__instance__.get_global_property(
global_name, prop_name)
def setter(self, value):
def setter(self, value): # type: ignore
return self.__instance__.set_global_property(
global_name, prop_name, value)
@ -70,13 +72,13 @@ def _build_global_class(compdef, global_name):
logging.warning(f"Duplicated property {prop_name}")
continue
def mk_setter_getter(callback_name):
def getter(self):
def call(*args):
def mk_setter_getter(callback_name: str): # type: ignore
def getter(self): # type: ignore
def call(*args: Any) -> Any:
return self.__instance__.invoke_global(global_name, callback_name, *args)
return call
def setter(self, value):
def setter(self, value): # type: ignore
return self.__instance__.set_global_callback(
global_name, callback_name, value)
@ -90,9 +92,9 @@ def _build_global_class(compdef, global_name):
logging.warning(f"Duplicated function {prop_name}")
continue
def mk_getter(function_name):
def getter(self):
def call(*args):
def mk_getter(function_name: str): # type: ignore
def getter(self): # type: ignore
def call(*args: Any) -> Any:
return self.__instance__.invoke_global(global_name, function_name, *args)
return call
@ -103,17 +105,17 @@ def _build_global_class(compdef, global_name):
return type("SlintGlobalClassWrapper", (), properties_and_callbacks)
def _build_class(compdef):
def _build_class(compdef: native.ComponentDefinition): # type: ignore
def cls_init(self, **kwargs):
def cls_init(self: Any, **kwargs) -> Any: # type: ignore
self.__instance__ = compdef.create()
for name, value in self.__class__.__dict__.items():
if hasattr(value, "slint.callback"):
callback_info = getattr(value, "slint.callback")
name = callback_info["name"]
def mk_callback(self, callback):
def invoke(*args, **kwargs):
def mk_callback(self: Any, callback: typing.Callable[..., Any]) -> typing.Callable[..., Any]:
def invoke(*args: Any, **kwargs: Any) -> Any:
return callback(self, *args, **kwargs)
return invoke
@ -137,12 +139,12 @@ def _build_class(compdef):
logging.warning(f"Duplicated property {prop_name}")
continue
def mk_setter_getter(prop_name):
def getter(self):
def mk_setter_getter(prop_name: str) -> Any:
def getter(self) -> Any: # type: ignore
return self.__instance__.get_property(prop_name)
def setter(self, value):
return self.__instance__.set_property(
def setter(self, value: Any) -> None: # type: ignore
self.__instance__.set_property(
prop_name, value)
return property(getter, setter)
@ -155,15 +157,14 @@ def _build_class(compdef):
logging.warning(f"Duplicated property {prop_name}")
continue
def mk_setter_getter(callback_name):
def getter(self):
def call(*args):
def mk_setter_getter(callback_name: str) -> Any: # type: ignore
def getter(self): # type: ignore
def call(*args: Any) -> Any:
return self.__instance__.invoke(callback_name, *args)
return call
def setter(self, value):
return self.__instance__.set_callback(
callback_name, value)
def setter(self, value: any): # type: ignore
self.__instance__.set_callback(callback_name, value)
return property(getter, setter)
@ -175,9 +176,9 @@ def _build_class(compdef):
logging.warning(f"Duplicated function {prop_name}")
continue
def mk_getter(function_name):
def getter(self):
def call(*args):
def mk_getter(function_name: str): # type: ignore
def getter(self) -> Any: # type: ignore
def call(*args: Any) -> Any:
return self.__instance__.invoke(function_name, *args)
return call
@ -188,8 +189,8 @@ def _build_class(compdef):
for global_name in compdef.globals:
global_class = _build_global_class(compdef, global_name)
def mk_global(global_class):
def global_getter(self):
def mk_global(global_class: typing.Callable[..., Any]): # type: ignore
def global_getter(self) -> Any: # type: ignore
wrapper = global_class()
setattr(wrapper, "__instance__", self.__instance__)
return wrapper
@ -201,9 +202,9 @@ def _build_class(compdef):
return type("SlintClassWrapper", (Component,), properties_and_callbacks)
def _build_struct(name, struct_prototype):
def _build_struct(name: str, struct_prototype: native.PyStruct) -> type:
def new_struct(cls, *args, **kwargs):
def new_struct(cls: Any, *args: Any, **kwargs: Any) -> native.PyStruct:
inst = copy.copy(struct_prototype)
for prop, val in kwargs.items():
@ -218,7 +219,7 @@ def _build_struct(name, struct_prototype):
return type(name, (), type_dict)
def load_file(path: builtins.str | os.PathLike | pathlib.Path, quiet:bool=False, style:typing.Optional[str]=None, include_paths:typing.Optional[typing.List[builtins.str | os.PathLike | pathlib.Path]]=None, library_paths:typing.Optional[typing.List[builtins.str | os.PathLike | pathlib.Path]]=None, translation_domain:typing.Optional[str]=None):
def load_file(path: str | os.PathLike[Any] | pathlib.Path, quiet:bool=False, style:typing.Optional[str]=None, include_paths:typing.Optional[typing.List[str | os.PathLike[Any] | pathlib.Path]]=None, library_paths:typing.Optional[typing.List[str | os.PathLike[Any] | pathlib.Path]]=None, translation_domain:typing.Optional[str]=None) -> Any:
compiler = native.Compiler()
if style is not None:
@ -264,13 +265,12 @@ def load_file(path: builtins.str | os.PathLike | pathlib.Path, quiet:bool=False,
class SlintAutoLoader:
def __init__(self, base_dir=None):
def __init__(self, base_dir: str | None=None):
self.local_dirs: typing.List[str] | None = None
if base_dir:
self.local_dirs = [base_dir]
else:
self.local_dirs = None
def __getattr__(self, name):
def __getattr__(self, name: str) -> Any:
for path in self.local_dirs or sys.path:
dir_candidate = os.path.join(path, name)
if os.path.isdir(dir_candidate):
@ -297,14 +297,14 @@ class SlintAutoLoader:
loader = SlintAutoLoader()
def _callback_decorator(callable, info):
def _callback_decorator(callable: typing.Callable[..., Any], info: typing.Dict[str, Any]) -> typing.Callable[..., Any]:
if "name" not in info:
info["name"] = callable.__name__
setattr(callable, "slint.callback", info)
return callable
def callback(global_name=None, name=None) -> typing.Callable[..., typing.Any]:
def callback(global_name: str | None=None, name : str | None=None) -> typing.Callable[..., Any]:
if callable(global_name):
callback = global_name
return _callback_decorator(callback, {})
@ -316,7 +316,7 @@ def callback(global_name=None, name=None) -> typing.Callable[..., typing.Any]:
info["global_name"] = global_name
return lambda callback: _callback_decorator(callback, info)
def set_xdg_app_id(app_id: str):
def set_xdg_app_id(app_id: str) -> None:
native.set_xdg_app_id(app_id)
__all__ = ["CompileError", "Component", "load_file", "loader", "Image", "Color",

View file

@ -4,29 +4,41 @@
from . import slint as native
from collections.abc import Iterable
import typing
from typing import Any, cast, Iterator
class Model[T](native.PyModelBase):
def __new__(cls, *args):
class Model[T](native.PyModelBase, Iterable[T]):
def __new__(cls, *args: Any) -> "Model[T]":
return super().__new__(cls)
def __init__(self):
def __init__(self) -> None:
self.init_self(self)
def __len__(self):
def __len__(self) -> int:
return self.row_count()
def __getitem__(self, index):
def __getitem__(self, index: int) -> typing.Optional[T]:
return self.row_data(index)
def __setitem__(self, index, value):
def __setitem__(self, index: int, value: T) -> None:
self.set_row_data(index, value)
def __iter__(self):
def __iter__(self) -> Iterator[T]:
return ModelIterator(self)
def notify_row_changed(self, row: int) -> None:
super().notify_row_changed(row)
def notify_row_removed(self, row: int, count: int) -> None:
super().notify_row_removed(row, count)
class ListModel[T](Model):
def notify_row_added(self, row: int, count: int) -> None:
super().notify_row_added(row, count)
def row_data(self, row: int) -> typing.Optional[T]:
return cast(T, super().row_data(row))
class ListModel[T](Model[T]):
def __init__(self, iterable: typing.Optional[Iterable[T]]=None):
super().__init__()
if iterable is not None:
@ -40,11 +52,11 @@ class ListModel[T](Model):
def row_data(self, row:int ) -> typing.Optional[T]:
return self.list[row]
def set_row_data(self, row: int, data: T):
def set_row_data(self, row: int, data: T) -> None:
self.list[row] = data
super().notify_row_changed(row)
def __delitem__(self, key: int | slice):
def __delitem__(self, key: int | slice) -> None:
if isinstance(key, slice):
start, stop, step = key.indices(len(self.list))
del self.list[key]
@ -54,23 +66,25 @@ class ListModel[T](Model):
del self.list[key]
super().notify_row_removed(key, 1)
def append(self, value: T):
def append(self, value: T) -> None:
index = len(self.list)
self.list.append(value)
super().notify_row_added(index, 1)
class ModelIterator:
def __init__(self, model):
class ModelIterator[T](Iterator[T]):
def __init__(self, model: Model[T]):
self.model = model
self.index = 0
def __iter__(self):
def __iter__(self) -> "ModelIterator[T]":
return self
def __next__(self):
def __next__(self) -> T:
if self.index >= self.model.row_count():
raise StopIteration()
index = self.index
self.index += 1
return self.model.row_data(index)
data = self.model.row_data(index)
assert data is not None
return data

View file

@ -31,7 +31,7 @@ class Color:
green: builtins.int
blue: builtins.int
alpha: builtins.int
def __new__(cls,maybe_value:typing.Optional[builtins.str | RgbaColor | RgbColor | typing.Dict[str, int]] = None): ...
def __new__(cls,maybe_value:typing.Optional[builtins.str | RgbaColor | RgbColor | typing.Dict[str, int]] = None) -> "Color": ...
def brighter(self, factor:builtins.float) -> "Color":
...
@ -57,7 +57,7 @@ class Color:
class Brush:
color: Color
def __new__(cls,maybe_value:typing.Optional[Color]): ...
def __new__(cls,maybe_value:typing.Optional[Color]) -> "Brush": ...
def is_transparent(self) -> builtins.bool:
...
@ -89,9 +89,9 @@ class Image:
width: builtins.int
height: builtins.int
path: typing.Optional[builtins.str]
def __new__(cls,): ...
def __new__(cls,) -> "Image": ...
@staticmethod
def load_from_path(path:builtins.str | os.PathLike | pathlib.Path) -> "Image":
def load_from_path(path:builtins.str | os.PathLike[Any] | pathlib.Path) -> "Image":
r"""
Loads the image from the specified path. Returns None if the image can't be loaded.
"""
@ -131,7 +131,7 @@ class Timer:
def set_interval(self, interval:datetime.timedelta) -> None:
...
def set_xdg_app_id(app_id: str):
def set_xdg_app_id(app_id: str) -> None:
...
def run_event_loop() -> None: ...
@ -139,7 +139,13 @@ def run_event_loop() -> None: ...
def quit_event_loop() -> None: ...
class PyModelBase:
...
def init_self(self, *args: Any) -> None: ...
def row_count(self) -> int: ...
def row_data(self, row: int) -> typing.Optional[Any]: ...
def set_row_data(self, row: int, value: Any) -> None: ...
def notify_row_changed(self, row: int) -> None: ...
def notify_row_removed(self, row: int, count: int) -> None: ...
def notify_row_added(self, row: int, count: int) -> None: ...
class PyStruct(Any):
...
@ -168,14 +174,17 @@ class PyDiagnostic:
class ComponentInstance:
def invoke(self, callback_name: str, *args): ...
def invoke_global(self, global_name: str, callback_name: str, *args): ...
def set_property(self, property_name: str, value: Any): ...
def get_property(self, property_name: str): ...
def set_callback(self, callback_name: str, callback: Callable[..., Any]): ...
def set_global_callback(self, global_name: str, callback_name: str, callback: Callable[..., Any]): ...
def set_global_property(self, global_name: str, property_name: str, value: Any): ...
def get_global_property(self, global_name: str, property_name: str): ...
def show(self) -> None: ...
def hide(self) -> None: ...
def run(self) -> None: ...
def invoke(self, callback_name: str, *args: Any) -> Any: ...
def invoke_global(self, global_name: str, callback_name: str, *args: Any) -> Any: ...
def set_property(self, property_name: str, value: Any) -> None: ...
def get_property(self, property_name: str) -> Any: ...
def set_callback(self, callback_name: str, callback: Callable[..., Any]) -> None: ...
def set_global_callback(self, global_name: str, callback_name: str, callback: Callable[..., Any]) -> None: ...
def set_global_property(self, global_name: str, property_name: str, value: Any) -> None: ...
def get_global_property(self, global_name: str, property_name: str) -> Any: ...
class ComponentDefinition:
@ -193,9 +202,14 @@ class ComponentDefinition:
class CompilationResult:
component_names: list[str]
diagnostics: list[PyDiagnostic]
named_exports: list[typing.Tuple[str, str]]
structs_and_enums: typing.Dict[str, PyStruct]
def component(self, name: str) -> ComponentDefinition: ...
class Compiler:
include_paths: list[str]
def build_from_path(self, path:str) -> CompilationResult: ...
def build_from_source(self, source:str, path: str) -> CompilationResult: ...
include_paths: list[str | os.PathLike[Any] | pathlib.Path]
library_paths: list[str | os.PathLike[Any] | pathlib.Path]
translation_domain: str
style: str
def build_from_path(self, path:str | os.PathLike[Any] | pathlib.Path) -> CompilationResult: ...
def build_from_source(self, source:str, path: str | os.PathLike[Any] | pathlib.Path) -> CompilationResult: ...