diff --git a/.github/workflows/autofix.yaml b/.github/workflows/autofix.yaml index 10ab5d41a..0ff242034 100644 --- a/.github/workflows/autofix.yaml +++ b/.github/workflows/autofix.yaml @@ -55,6 +55,14 @@ jobs: run: cargo xtask check_license_headers --fix-it - name: remove trailing whitespace run: git grep -I -l -O'sed -i "s/[[:space:]]*$//"' -e '' -- ':!*.patch' + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Format Python + working-directory: api/python + run: uv tool run ruff format - name: Suggest format changes uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef - name: Build wasm-interpreter before type check diff --git a/api/python/noxfile.py b/api/python/noxfile.py index a5cde01f6..9b03c86c2 100644 --- a/api/python/noxfile.py +++ b/api/python/noxfile.py @@ -3,6 +3,7 @@ import nox + @nox.session(python="3.12") def python(session: nox.Session): session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" diff --git a/api/python/pyproject.toml b/api/python/pyproject.toml index 9d363b192..8d3ce02cc 100644 --- a/api/python/pyproject.toml +++ b/api/python/pyproject.toml @@ -50,6 +50,7 @@ dev = [ "nox>=2024.10.9", "pdoc>=15.0.1", "pytest>=8.3.4", + "ruff>=0.9.6", ] [tool.uv] diff --git a/api/python/slint/__init__.py b/api/python/slint/__init__.py index 92a231b83..5555f907e 100644 --- a/api/python/slint/__init__.py +++ b/api/python/slint/__init__.py @@ -1,9 +1,9 @@ # Copyright © SixtyFPS GmbH # SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -r''' +r""" .. include:: ../README.md -''' +""" from importlib.machinery import ModuleSpec import os @@ -21,6 +21,7 @@ from .slint import Image, Color, Brush, Timer, TimerMode Struct = native.PyStruct + class CompileError(Exception): def __init__(self, message: str, diagnostics: list[native.PyDiagnostic]): self.message = message @@ -53,14 +54,14 @@ def _build_global_class(compdef: native.ComponentDefinition, global_name: str) - logging.warning(f"Duplicated property {prop_name}") continue - 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 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): # type: ignore + def setter(self, value): # type: ignore return self.__instance__.set_global_property( - global_name, prop_name, value) + global_name, prop_name, value + ) return property(getter, setter) @@ -72,15 +73,19 @@ def _build_global_class(compdef: native.ComponentDefinition, global_name: str) - logging.warning(f"Duplicated property {prop_name}") continue - def mk_setter_getter(callback_name: str): # type: ignore - def getter(self): # type: ignore + 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 self.__instance__.invoke_global( + global_name, callback_name, *args + ) + return call - def setter(self, value): # type: ignore + def setter(self, value): # type: ignore return self.__instance__.set_global_callback( - global_name, callback_name, value) + global_name, callback_name, value + ) return property(getter, setter) @@ -92,10 +97,13 @@ def _build_global_class(compdef: native.ComponentDefinition, global_name: str) - logging.warning(f"Duplicated function {prop_name}") continue - def mk_getter(function_name: str): # type: ignore - def getter(self): # type: ignore + 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 self.__instance__.invoke_global( + global_name, function_name, *args + ) + return call return property(getter) @@ -105,33 +113,33 @@ def _build_global_class(compdef: native.ComponentDefinition, global_name: str) - return type("SlintGlobalClassWrapper", (), properties_and_callbacks) -def _build_class(compdef: native.ComponentDefinition): # type: ignore - - def cls_init(self: Any, **kwargs) -> Any: # type: ignore +def _build_class(compdef: native.ComponentDefinition): # type: ignore + 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: Any, callback: typing.Callable[..., Any]) -> typing.Callable[..., Any]: + 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 if "global_name" in callback_info: self.__instance__.set_global_callback( - callback_info["global_name"], name, mk_callback(self, value)) + callback_info["global_name"], name, mk_callback(self, value) + ) else: - self.__instance__.set_callback( - name, mk_callback(self, value)) + self.__instance__.set_callback(name, mk_callback(self, value)) for prop, val in kwargs.items(): setattr(self, prop, val) - properties_and_callbacks = { - "__init__": cls_init - } + properties_and_callbacks = {"__init__": cls_init} for prop_name in compdef.properties.keys(): python_prop = _normalize_prop(prop_name) @@ -140,12 +148,11 @@ def _build_class(compdef: native.ComponentDefinition): # type: ignore continue def mk_setter_getter(prop_name: str) -> Any: - def getter(self) -> Any: # type: ignore + def getter(self) -> Any: # type: ignore return self.__instance__.get_property(prop_name) - def setter(self, value: Any) -> None: # type: ignore - self.__instance__.set_property( - prop_name, value) + def setter(self, value: Any) -> None: # type: ignore + self.__instance__.set_property(prop_name, value) return property(getter, setter) @@ -157,13 +164,14 @@ def _build_class(compdef: native.ComponentDefinition): # type: ignore logging.warning(f"Duplicated property {prop_name}") continue - def mk_setter_getter(callback_name: str) -> Any: # type: ignore - def getter(self): # type: ignore + 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: any): # type: ignore + def setter(self, value: any): # type: ignore self.__instance__.set_callback(callback_name, value) return property(getter, setter) @@ -176,10 +184,11 @@ def _build_class(compdef: native.ComponentDefinition): # type: ignore logging.warning(f"Duplicated function {prop_name}") continue - def mk_getter(function_name: str): # type: ignore - def getter(self) -> Any: # type: ignore + 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 return property(getter) @@ -189,8 +198,8 @@ def _build_class(compdef: native.ComponentDefinition): # type: ignore for global_name in compdef.globals: global_class = _build_global_class(compdef, global_name) - def mk_global(global_class: typing.Callable[..., Any]): # type: ignore - def global_getter(self) -> Any: # type: ignore + 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 @@ -203,7 +212,6 @@ def _build_class(compdef: native.ComponentDefinition): # type: ignore def _build_struct(name: str, struct_prototype: native.PyStruct) -> type: - def new_struct(cls: Any, *args: Any, **kwargs: Any) -> native.PyStruct: inst = copy.copy(struct_prototype) @@ -219,7 +227,18 @@ def _build_struct(name: str, struct_prototype: native.PyStruct) -> type: return type(name, (), type_dict) -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: +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: @@ -240,8 +259,11 @@ def load_file(path: str | os.PathLike[Any] | pathlib.Path, quiet:bool=False, sty if diag.level == native.DiagnosticLevel.Warning: logging.warning(diag) - errors = [diag for diag in diagnostics if diag.level == - native.DiagnosticLevel.Error] + errors = [ + diag + for diag in diagnostics + if diag.level == native.DiagnosticLevel.Error + ] if errors: raise CompileError(f"Could not compile {path}", diagnostics) @@ -265,11 +287,11 @@ def load_file(path: str | os.PathLike[Any] | pathlib.Path, quiet:bool=False, sty class SlintAutoLoader: - def __init__(self, base_dir: str | None=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] - + def __getattr__(self, name: str) -> Any: for path in self.local_dirs or sys.path: dir_candidate = os.path.join(path, name) @@ -284,7 +306,7 @@ class SlintAutoLoader: setattr(self, name, type_namespace) return type_namespace - dir_candidate = os.path.join(path, name.replace('_', '-')) + dir_candidate = os.path.join(path, name.replace("_", "-")) file_candidate = dir_candidate + ".slint" if os.path.isfile(file_candidate): type_namespace = load_file(file_candidate) @@ -297,14 +319,18 @@ class SlintAutoLoader: loader = SlintAutoLoader() -def _callback_decorator(callable: typing.Callable[..., Any], info: typing.Dict[str, Any]) -> typing.Callable[..., Any]: +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: str | None=None, name : str | None=None) -> typing.Callable[..., 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,9 +342,23 @@ def callback(global_name: str | None=None, name : str | None=None) -> typing.Cal info["global_name"] = global_name return lambda callback: _callback_decorator(callback, info) + def set_xdg_app_id(app_id: str) -> None: native.set_xdg_app_id(app_id) -__all__ = ["CompileError", "Component", "load_file", "loader", "Image", "Color", - "Brush", "Model", "ListModel", "Timer", "TimerMode", "set_xdg_app_id", - "callback"] + +__all__ = [ + "CompileError", + "Component", + "load_file", + "loader", + "Image", + "Color", + "Brush", + "Model", + "ListModel", + "Timer", + "TimerMode", + "set_xdg_app_id", + "callback", +] diff --git a/api/python/slint/models.py b/api/python/slint/models.py index b52b013f0..8f53e0b44 100644 --- a/api/python/slint/models.py +++ b/api/python/slint/models.py @@ -25,7 +25,7 @@ class Model[T](native.PyModelBase, Iterable[T]): def __iter__(self) -> Iterator[T]: return ModelIterator(self) - + def notify_row_changed(self, row: int) -> None: super().notify_row_changed(row) @@ -38,8 +38,9 @@ class Model[T](native.PyModelBase, Iterable[T]): 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): + def __init__(self, iterable: typing.Optional[Iterable[T]] = None): super().__init__() if iterable is not None: self.list = list(iterable) @@ -49,7 +50,7 @@ class ListModel[T](Model[T]): def row_count(self) -> int: return len(self.list) - def row_data(self, row:int ) -> typing.Optional[T]: + def row_data(self, row: int) -> typing.Optional[T]: return self.list[row] def set_row_data(self, row: int, data: T) -> None: diff --git a/api/python/slint/slint.pyi b/api/python/slint/slint.pyi index f934dd786..c1ac6170c 100644 --- a/api/python/slint/slint.pyi +++ b/api/python/slint/slint.pyi @@ -13,7 +13,6 @@ from typing import Any, List from collections.abc import Callable from enum import Enum, auto - class RgbColor: red: builtins.int green: builtins.int @@ -25,117 +24,83 @@ class RgbaColor: blue: builtins.int alpha: builtins.int - class Color: red: builtins.int 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) -> "Color": ... - def brighter(self, factor:builtins.float) -> "Color": - ... - - def darker(self, factor:builtins.float) -> "Color": - ... - - def transparentize(self, factor:builtins.float) -> "Color": - ... - - def mix(self, other:"Image", factor:builtins.float) -> "Color": - ... - - def with_alpha(self, alpha:builtins.float) -> "Color": - ... - - def __str__(self) -> builtins.str: - ... - - def __eq__(self, other:object) -> builtins.bool: - ... - - + def __new__( + cls, + maybe_value: typing.Optional[ + builtins.str | RgbaColor | RgbColor | typing.Dict[str, int] + ] = None, + ) -> "Color": ... + def brighter(self, factor: builtins.float) -> "Color": ... + def darker(self, factor: builtins.float) -> "Color": ... + def transparentize(self, factor: builtins.float) -> "Color": ... + def mix(self, other: "Image", factor: builtins.float) -> "Color": ... + def with_alpha(self, alpha: builtins.float) -> "Color": ... + def __str__(self) -> builtins.str: ... + def __eq__(self, other: object) -> builtins.bool: ... class Brush: color: Color - def __new__(cls,maybe_value:typing.Optional[Color]) -> "Brush": ... - def is_transparent(self) -> builtins.bool: - ... - - def is_opaque(self) -> builtins.bool: - ... - - def brighter(self, factor:builtins.float) -> "Brush": - ... - - def darker(self, factor:builtins.float) -> "Brush": - ... - - def transparentize(self, amount:builtins.float) -> "Brush": - ... - - def with_alpha(self, alpha:builtins.float) -> "Brush": - ... - - def __eq__(self, other:object) -> builtins.bool: - ... - + def __new__(cls, maybe_value: typing.Optional[Color]) -> "Brush": ... + def is_transparent(self) -> builtins.bool: ... + def is_opaque(self) -> builtins.bool: ... + def brighter(self, factor: builtins.float) -> "Brush": ... + def darker(self, factor: builtins.float) -> "Brush": ... + def transparentize(self, amount: builtins.float) -> "Brush": ... + def with_alpha(self, alpha: builtins.float) -> "Brush": ... + def __eq__(self, other: object) -> builtins.bool: ... class Image: r""" Image objects can be set on Slint Image elements for display. Construct Image objects from a path to an image file on disk, using `Image.load_from_path`. """ + size: tuple[builtins.int, builtins.int] width: builtins.int height: builtins.int path: typing.Optional[builtins.str] - def __new__(cls,) -> "Image": ... + def __new__( + cls, + ) -> "Image": ... @staticmethod - def load_from_path(path:builtins.str | os.PathLike[Any] | 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. """ ... @staticmethod - def load_from_svg_data(data:typing.Sequence[builtins.int]) -> "Image": + def load_from_svg_data(data: typing.Sequence[builtins.int]) -> "Image": r""" Creates a new image from a string that describes the image in SVG format. """ ... - class TimerMode(Enum): SingleShot = auto() Repeated = auto() - class Timer: - def __new__(cls,) -> "Timer": ... - def start(self, mode:TimerMode, interval:datetime.timedelta, callback:typing.Any) -> None: - ... - + def __new__( + cls, + ) -> "Timer": ... + def start( + self, mode: TimerMode, interval: datetime.timedelta, callback: typing.Any + ) -> None: ... @staticmethod - def single_shot(duration:datetime.timedelta, callback:typing.Any) -> None: - ... - - def stop(self) -> None: - ... - - def restart(self) -> None: - ... - - def running(self) -> builtins.bool: - ... - - def set_interval(self, interval:datetime.timedelta) -> None: - ... - -def set_xdg_app_id(app_id: str) -> None: - ... + def single_shot(duration: datetime.timedelta, callback: typing.Any) -> None: ... + def stop(self) -> None: ... + def restart(self) -> None: ... + def running(self) -> builtins.bool: ... + def set_interval(self, interval: datetime.timedelta) -> None: ... +def set_xdg_app_id(app_id: str) -> None: ... def run_event_loop() -> None: ... - def quit_event_loop() -> None: ... class PyModelBase: @@ -147,9 +112,7 @@ class PyModelBase: def notify_row_removed(self, row: int, count: int) -> None: ... def notify_row_added(self, row: int, count: int) -> None: ... -class PyStruct(Any): - ... - +class PyStruct(Any): ... class ValueType(Enum): Void = auto() @@ -172,21 +135,27 @@ class PyDiagnostic: column_number: int source_file: typing.Optional[str] - class ComponentInstance: 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 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 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: def create(self) -> ComponentInstance: ... name: str @@ -198,7 +167,6 @@ class ComponentDefinition: def global_callbacks(self, global_name: str) -> list[str]: ... def global_properties(self, global_name: str) -> typing.Dict[str, ValueType]: ... - class CompilationResult: component_names: list[str] diagnostics: list[PyDiagnostic] @@ -211,5 +179,9 @@ class Compiler: 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: ... + 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: ... diff --git a/api/python/tests/test_brush.py b/api/python/tests/test_brush.py index 4eda438df..08b961dda 100644 --- a/api/python/tests/test_brush.py +++ b/api/python/tests/test_brush.py @@ -5,7 +5,6 @@ from slint import slint as native from slint import Color, Brush - def test_col_default() -> None: col = Color() assert col.red == 0 @@ -24,7 +23,7 @@ def test_col_from_str() -> None: def test_col_from_rgb_dict() -> None: - coldict = {'red': 0x12, 'green': 0x34, 'blue': 0x56} + coldict = {"red": 0x12, "green": 0x34, "blue": 0x56} col = Color(coldict) assert col.red == 0x12 assert col.green == 0x34 @@ -33,7 +32,7 @@ def test_col_from_rgb_dict() -> None: def test_col_from_rgba_dict() -> None: - coldict = {'red': 0x12, 'green': 0x34, 'blue': 0x56, 'alpha': 128} + coldict = {"red": 0x12, "green": 0x34, "blue": 0x56, "alpha": 128} col = Color(coldict) assert col.red == 0x12 assert col.green == 0x34 diff --git a/api/python/tests/test_callback_decorators.py b/api/python/tests/test_callback_decorators.py index 0b565573e..6dcfdb5ef 100644 --- a/api/python/tests/test_callback_decorators.py +++ b/api/python/tests/test_callback_decorators.py @@ -19,7 +19,7 @@ def base_dir() -> str: def test_callback_decorators(caplog: pytest.LogCaptureFixture) -> None: module = load_file(os.path.join(base_dir(), "test-load-file.slint"), quiet=False) - class SubClass(module.App): # type: ignore + class SubClass(module.App): # type: ignore @slint.callback() def say_hello_again(self, arg: str) -> str: return "say_hello_again:" + arg diff --git a/api/python/tests/test_compiler.py b/api/python/tests/test_compiler.py index 94086461c..330df41c2 100644 --- a/api/python/tests/test_compiler.py +++ b/api/python/tests/test_compiler.py @@ -15,7 +15,8 @@ def test_basic_compiler() -> None: assert len(compiler.build_from_source("Garbage", "").component_names) == 0 - result = compiler.build_from_source(""" + result = compiler.build_from_source( + """ export global TestGlobal { in property theglobalprop; callback globallogic(); @@ -35,7 +36,9 @@ def test_basic_compiler() -> None: callback test-callback(); public function ff() {} } - """, "") + """, + "", + ) assert result.component_names == ["Test"] compdef = result.component("Test") @@ -44,8 +47,16 @@ def test_basic_compiler() -> None: assert compdef.name == "Test" props = [(name, type) for name, type in compdef.properties.items()] - assert props == [('boolprop', ValueType.Bool), ('brushprop', ValueType.Brush), ('colprop', ValueType.Brush), ('floatprop', ValueType.Number), - ('imgprop', ValueType.Image), ('intprop', ValueType.Number), ('modelprop', ValueType.Model), ('strprop', ValueType.String)] + assert props == [ + ("boolprop", ValueType.Bool), + ("brushprop", ValueType.Brush), + ("colprop", ValueType.Brush), + ("floatprop", ValueType.Number), + ("imgprop", ValueType.Image), + ("intprop", ValueType.Number), + ("modelprop", ValueType.Model), + ("strprop", ValueType.String), + ] assert compdef.callbacks == ["test-callback"] assert compdef.functions == ["ff"] @@ -53,8 +64,9 @@ def test_basic_compiler() -> None: assert compdef.globals == ["TestGlobal"] assert compdef.global_properties("Garbage") == None - assert [(name, type) for name, type in compdef.global_properties( - "TestGlobal").items()] == [('theglobalprop', ValueType.String)] + assert [ + (name, type) for name, type in compdef.global_properties("TestGlobal").items() + ] == [("theglobalprop", ValueType.String)] assert compdef.global_callbacks("Garbage") == None assert compdef.global_callbacks("TestGlobal") == ["globallogic"] diff --git a/api/python/tests/test_gc.py b/api/python/tests/test_gc.py index cb06301ef..069548da9 100644 --- a/api/python/tests/test_gc.py +++ b/api/python/tests/test_gc.py @@ -10,12 +10,15 @@ import typing def test_callback_gc() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export component Test { out property test-value: "Ok"; callback test-callback(string) -> string; } - """, "").component("Test") + """, + "", + ).component("Test") assert compdef != None instance: native.ComponentInstance | None = compdef.create() @@ -30,8 +33,7 @@ def test_callback_gc() -> None: handler: Handler | None = Handler(instance) assert handler is not None - instance.set_callback( - "test-callback", handler.python_callback) + instance.set_callback("test-callback", handler.python_callback) handler = None assert instance.invoke("test-callback", "World") == "WorldOk" diff --git a/api/python/tests/test_instance.py b/api/python/tests/test_instance.py index a210452cc..6c8292a32 100644 --- a/api/python/tests/test_instance.py +++ b/api/python/tests/test_instance.py @@ -7,11 +7,11 @@ from slint.slint import ValueType, Image, Color, Brush import os - def test_property_access() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export global TestGlobal { in property theglobalprop: "Hey"; callback globallogic(); @@ -41,7 +41,9 @@ def test_property_access() -> None: callback test-callback(); } - """, os.path.join(os.path.dirname(__file__), "main.slint")).component("Test") + """, + os.path.join(os.path.dirname(__file__), "main.slint"), + ).component("Test") assert compdef != None instance = compdef.create() @@ -80,7 +82,8 @@ def test_property_access() -> None: assert structval.finished == True assert structval.dash_prop == True instance.set_property( - "structprop", {'title': 'new', 'finished': False, 'dash_prop': False}) + "structprop", {"title": "new", "finished": False, "dash_prop": False} + ) structval = instance.get_property("structprop") assert structval.title == "new" assert structval.finished == False @@ -94,14 +97,23 @@ def test_property_access() -> None: with pytest.raises(RuntimeError, match="The image cannot be loaded"): Image.load_from_path("non-existent.png") - instance.set_property("imageprop", Image.load_from_path(os.path.join( - os.path.dirname(__file__), "../../../examples/iot-dashboard/images/humidity.png"))) + instance.set_property( + "imageprop", + Image.load_from_path( + os.path.join( + os.path.dirname(__file__), + "../../../examples/iot-dashboard/images/humidity.png", + ) + ), + ) imageval = instance.get_property("imageprop") assert imageval.size == (36, 36) assert "humidity.png" in imageval.path - with pytest.raises(TypeError, match="'int' object cannot be converted to 'PyString'"): - instance.set_property("structprop", {42: 'wrong'}) + with pytest.raises( + TypeError, match="'int' object cannot be converted to 'PyString'" + ): + instance.set_property("structprop", {42: "wrong"}) brushval = instance.get_property("brushprop") assert str(brushval.color) == "argb(255, 255, 0, 255)" @@ -128,7 +140,8 @@ def test_property_access() -> None: def test_callbacks() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export global TestGlobal { callback globallogic(string) -> string; globallogic(value) => { @@ -143,7 +156,9 @@ def test_callbacks() -> None: } callback void-callback(); } - """, "").component("Test") + """, + "", + ).component("Test") assert compdef != None instance = compdef.create() @@ -151,8 +166,7 @@ def test_callbacks() -> None: assert instance.invoke("test-callback", "foo") == "local foo" - assert instance.invoke_global( - "TestGlobal", "globallogic", "foo") == "global foo" + assert instance.invoke_global("TestGlobal", "globallogic", "foo") == "global foo" with pytest.raises(RuntimeError, match="no such callback"): instance.set_callback("non-existent", lambda x: x) @@ -164,9 +178,12 @@ def test_callbacks() -> None: instance.set_global_callback("TestGlobal", "non-existent", lambda x: x) instance.set_global_callback( - "TestGlobal", "globallogic", lambda x: "python global " + x) - assert instance.invoke_global( - "TestGlobal", "globallogic", "foo") == "python global foo" + "TestGlobal", "globallogic", lambda x: "python global " + x + ) + assert ( + instance.invoke_global("TestGlobal", "globallogic", "foo") + == "python global foo" + ) instance.set_callback("void-callback", lambda: None) instance.invoke("void-callback") @@ -174,9 +191,8 @@ def test_callbacks() -> None: if __name__ == "__main__": import slint - module = slint.load_file( - "../../demos/printerdemo/ui/printerdemo.slint") + + module = slint.load_file("../../demos/printerdemo/ui/printerdemo.slint") instance = module.MainWindow() - instance.PrinterQueue.start_job = lambda title: print( - f"new print job {title}") + instance.PrinterQueue.start_job = lambda title: print(f"new print job {title}") instance.run() diff --git a/api/python/tests/test_load_file.py b/api/python/tests/test_load_file.py index d2e0f2ad3..9db8fe2d0 100644 --- a/api/python/tests/test_load_file.py +++ b/api/python/tests/test_load_file.py @@ -14,10 +14,14 @@ def base_dir() -> str: assert base_dir is not None return base_dir + def test_load_file(caplog: pytest.LogCaptureFixture) -> None: module = load_file(os.path.join(base_dir(), "test-load-file.slint"), quiet=False) - assert "The property 'color' has been deprecated. Please use 'background' instead" in caplog.text + assert ( + "The property 'color' has been deprecated. Please use 'background' instead" + in caplog.text + ) assert len(list(module.__dict__.keys())) == 6 assert "App" in module.__dict__ diff --git a/api/python/tests/test_loader.py b/api/python/tests/test_loader.py index 6ac7c28e8..9f76d4d22 100644 --- a/api/python/tests/test_loader.py +++ b/api/python/tests/test_loader.py @@ -17,8 +17,7 @@ def test_magic_import_path() -> None: oldsyspath = sys.path assert loader.printerdemo == None try: - sys.path.append(os.path.join(os.path.dirname(__file__), - "..", "..", "..")) + sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "..")) instance = loader.demos.printerdemo.ui.printerdemo.MainWindow() del instance finally: diff --git a/api/python/tests/test_models.py b/api/python/tests/test_models.py index 32c22d335..e5ef30149 100644 --- a/api/python/tests/test_models.py +++ b/api/python/tests/test_models.py @@ -9,7 +9,8 @@ import typing def test_model_notify() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export component App { width: 300px; height: 300px; @@ -29,7 +30,9 @@ def test_model_notify() -> None: } } - """, "").component("App") + """, + "", + ).component("App") assert compdef != None instance = compdef.create() @@ -37,8 +40,7 @@ def test_model_notify() -> None: model = models.ListModel([100, 0]) - instance.set_property( - "fixed-height-model", model) + instance.set_property("fixed-height-model", model) assert instance.get_property("layout-height") == 100 model.set_row_data(1, 50) @@ -48,18 +50,20 @@ def test_model_notify() -> None: del model[1:] assert instance.get_property("layout-height") == 100 - assert isinstance(instance.get_property( - "fixed-height-model"), models.ListModel) + assert isinstance(instance.get_property("fixed-height-model"), models.ListModel) def test_model_from_list() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export component App { in-out property<[int]> data: [1, 2, 3, 4]; } - """, "").component("App") + """, + "", + ).component("App") assert compdef != None instance = compdef.create() @@ -100,11 +104,14 @@ def test_python_model_iterable() -> None: def test_rust_model_sequence() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export component App { in-out property<[int]> data: [1, 2, 3, 4, 5]; } - """, "").component("App") + """, + "", + ).component("App") assert compdef != None instance = compdef.create() @@ -120,7 +127,8 @@ def test_rust_model_sequence() -> None: def test_model_writeback() -> None: compiler = native.Compiler() - compdef = compiler.build_from_source(""" + compdef = compiler.build_from_source( + """ export component App { width: 300px; height: 300px; @@ -132,7 +140,9 @@ def test_model_writeback() -> None: } } - """, "").component("App") + """, + "", + ).component("App") assert compdef != None instance = compdef.create() @@ -140,8 +150,7 @@ def test_model_writeback() -> None: model = models.ListModel([100, 0]) - instance.set_property( - "model", model) + instance.set_property("model", model) instance.invoke("write-to-model", 1, 42) assert list(instance.get_property("model")) == [100, 42] diff --git a/api/python/tests/test_timers.py b/api/python/tests/test_timers.py index 8deaee157..b410e5d9b 100644 --- a/api/python/tests/test_timers.py +++ b/api/python/tests/test_timers.py @@ -3,7 +3,7 @@ import pytest from slint import slint as native -from slint.slint import ValueType; +from slint.slint import ValueType from datetime import timedelta counter: int @@ -12,6 +12,7 @@ counter: int def test_timer() -> None: global counter counter = 0 + def quit_after_two_invocations() -> None: global counter counter = min(counter + 1, 2) @@ -19,10 +20,15 @@ def test_timer() -> None: native.quit_event_loop() test_timer = native.Timer() - test_timer.start(native.TimerMode.Repeated, timedelta(milliseconds=100), quit_after_two_invocations) + test_timer.start( + native.TimerMode.Repeated, + timedelta(milliseconds=100), + quit_after_two_invocations, + ) native.run_event_loop() test_timer.stop() - assert(counter == 2) + assert counter == 2 + def test_single_shot() -> None: native.Timer.single_shot(timedelta(milliseconds=100), native.quit_event_loop)