Skip to content

Commit

Permalink
Add Python typings for Rust declared types (#7575)
Browse files Browse the repository at this point in the history
This comes with a bump of the Python version to 3.11
due to the need for typing.Self.
  • Loading branch information
tronical authored Feb 9, 2025
1 parent 30aefd4 commit 94c6557
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ jobs:
key: x-napi-v2-${{ steps.node-install.outputs.node-version }} # the cache key consists of a manually bumpable version and the node version, as the cached rustc artifacts contain linking information where to find node.lib, which is in a versioned directory.
- uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v5
- uses: fjwillemsen/setup-nox2@v3.0.0
Expand Down
7 changes: 6 additions & 1 deletion api/python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ rust-version.workspace = true

[lib]
path = "lib.rs"
crate-type = ["cdylib"]
crate-type = ["cdylib", "rlib"]

[[bin]]
name = "stub-gen"
path = "stub-gen/main.rs"

[features]
default = ["backend-winit", "renderer-femtovg", "renderer-software", "backend-qt", "accessibility"]
Expand Down Expand Up @@ -46,6 +50,7 @@ indexmap = { version = "2.1.0" }
chrono = "0.4"
spin_on = { workspace = true }
css-color-parser2 = { workspace = true }
pyo3-stub-gen = { version = "0.7.0", default-features = false }

[package.metadata.maturin]
python-source = "slint"
35 changes: 35 additions & 0 deletions api/python/brush.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,36 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3::prelude::*;
use pyo3_stub_gen::{derive::gen_stub_pyclass, derive::gen_stub_pymethods, impl_stub_type};

use crate::errors::PyColorParseError;

#[gen_stub_pyclass]
#[pyclass]
#[derive(FromPyObject)]
struct RgbaColor {
#[pyo3(get, set)]
red: u8,
#[pyo3(get, set)]
green: u8,
#[pyo3(get, set)]
blue: u8,
#[pyo3(get, set)]
alpha: u8,
}

#[gen_stub_pyclass]
#[pyclass]
#[derive(FromPyObject)]
struct RgbColor {
#[pyo3(get, set)]
red: u8,
#[pyo3(get, set)]
green: u8,
#[pyo3(get, set)]
blue: u8,
}

#[derive(FromPyObject)]
enum PyColorInput {
ColorStr(String),
Expand All @@ -29,12 +56,16 @@ enum PyColorInput {
},
}

impl_stub_type!(PyColorInput = String | RgbaColor | RgbColor);

#[gen_stub_pyclass]
#[pyclass(name = "Color")]
#[derive(Clone)]
pub struct PyColor {
pub color: slint_interpreter::Color,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyColor {
#[new]
Expand Down Expand Up @@ -124,11 +155,15 @@ enum PyBrushInput {
SolidColor(PyColor),
}

impl_stub_type!(PyBrushInput = PyColor);

#[gen_stub_pyclass]
#[pyclass(name = "Brush")]
pub struct PyBrush {
pub brush: slint_interpreter::Brush,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyBrush {
#[new]
Expand Down
11 changes: 7 additions & 4 deletions api/python/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3::prelude::*;
use pyo3_stub_gen::{derive::gen_stub_pyclass, derive::gen_stub_pymethods};

/// 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`.
#[gen_stub_pyclass]
#[pyclass(unsendable, name = "Image")]
pub struct PyImage {
pub image: slint_interpreter::Image,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyImage {
#[new]
Expand Down Expand Up @@ -37,8 +40,8 @@ impl PyImage {

/// The path of the image if it was loaded from disk, or None.
#[getter]
fn path(&self) -> PyResult<Option<&std::path::Path>> {
Ok(self.image.path())
fn path(&self) -> PyResult<Option<std::path::PathBuf>> {
Ok(self.image.path().map(|p| p.to_path_buf()))
}

/// Loads the image from the specified path. Returns None if the image can't be loaded.
Expand All @@ -50,8 +53,8 @@ impl PyImage {

/// Creates a new image from a string that describes the image in SVG format.
#[staticmethod]
fn load_from_svg_data(data: &[u8]) -> Result<Self, crate::errors::PyLoadImageError> {
let image = slint_interpreter::Image::load_from_svg_data(data)?;
fn load_from_svg_data(data: Vec<u8>) -> Result<Self, crate::errors::PyLoadImageError> {
let image = slint_interpreter::Image::load_from_svg_data(&data)?;
Ok(Self { image })
}
}
Expand Down
4 changes: 4 additions & 0 deletions api/python/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3_stub_gen::define_stub_info_gatherer;

mod image;
mod interpreter;
use interpreter::{CompilationResult, Compiler, PyDiagnostic, PyDiagnosticLevel, PyValueType};
Expand Down Expand Up @@ -53,3 +55,5 @@ fn slint(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {

Ok(())
}

define_stub_info_gatherer!(stub_info);
2 changes: 1 addition & 1 deletion api/python/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import nox

@nox.session(python="3.10")
@nox.session(python="3.11")
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
Expand Down
2 changes: 1 addition & 1 deletion api/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "maturin"
[project]
name = "slint"
version = "1.10.0a1"
requires-python = ">= 3.10"
requires-python = ">= 3.11"
authors = [
{name = "Slint Team", email = "info@slint.dev"},
]
Expand Down
134 changes: 134 additions & 0 deletions api/python/slint/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401

import builtins
import datetime
import os
import pathlib
import typing
from typing import Self
from enum import Enum, auto


class RgbColor:
red: builtins.int
green: builtins.int
blue: builtins.int

class RgbaColor:
red: builtins.int
green: builtins.int
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]): ...
def brighter(self, factor:builtins.float) -> Self:
...

def darker(self, factor:builtins.float) -> Self:
...

def transparentize(self, factor:builtins.float) -> Self:
...

def mix(self, other:Self, factor:builtins.float) -> Self:
...

def with_alpha(self, alpha:builtins.float) -> Self:
...

def __str__(self) -> builtins.str:
...

def __eq__(self, other:Self) -> builtins.bool:
...



class Brush:
color: Color
def __new__(cls,maybe_value:typing.Optional[Color]): ...
def is_transparent(self) -> builtins.bool:
...

def is_opaque(self) -> builtins.bool:
...

def brighter(self, factor:builtins.float) -> Self:
...

def darker(self, factor:builtins.float) -> Self:
...

def transparentize(self, amount:builtins.float) -> Self:
...

def with_alpha(self, alpha:builtins.float) -> Self:
...

def __eq__(self, other:Self) -> 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,): ...
@staticmethod
def load_from_path(path:builtins.str | os.PathLike | pathlib.Path) -> Self:
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]) -> Self:
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,): ...
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:
...



11 changes: 11 additions & 0 deletions api/python/stub-gen/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3_stub_gen::Result;

fn main() -> Result<()> {
// `stub_info` is a function defined by `define_stub_info_gatherer!` macro.
let stub = slint_python::stub_info()?;
stub.generate()?;
Ok(())
}
6 changes: 6 additions & 0 deletions api/python/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3::prelude::*;
use pyo3_stub_gen::{
derive::gen_stub_pyclass, derive::gen_stub_pyclass_enum, derive::gen_stub_pymethods,
};

#[derive(Copy, Clone)]
#[gen_stub_pyclass_enum]
#[pyclass(name = "TimerMode")]
pub enum PyTimerMode {
/// A SingleShot timer is fired only once.
Expand All @@ -21,11 +25,13 @@ impl From<PyTimerMode> for i_slint_core::timers::TimerMode {
}
}

#[gen_stub_pyclass]
#[pyclass(name = "Timer", unsendable)]
pub struct PyTimer {
timer: i_slint_core::timers::Timer,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyTimer {
#[new]
Expand Down
1 change: 1 addition & 0 deletions xtask/src/license_headers_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ lazy_static! {
("\\.yaml$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
("\\.yml$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
("\\.py$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
("\\.pyi$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
("\\.proto$", LicenseLocation::Tag(LicenseTagStyle::c_style_comment_style())),
("\\.bazelrc$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
("MODULE.bazel$", LicenseLocation::Tag(LicenseTagStyle::shell_comment_style())),
Expand Down

0 comments on commit 94c6557

Please sign in to comment.