- workflow updates
- python stubs
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
name: PyTest
|
||||||
|
|
||||||
|
on: [push, pull_request, workflow_call]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.10", "3.12", "3.14"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/pip
|
||||||
|
~/.cache/pip-wheel
|
||||||
|
~/.cache/sccache
|
||||||
|
~/.cache/cargo-xwin
|
||||||
|
~/.cargo
|
||||||
|
~/.osxcross
|
||||||
|
key: cache-ubuntu-maturin-cross-compile
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
run: |
|
||||||
|
export PATH="$HOME/.cargo/bin:$PATH"
|
||||||
|
if ! command -v rustc >/dev/null 2>&1; then
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
else
|
||||||
|
rustup update
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: "cargo test"
|
||||||
|
run: |-
|
||||||
|
export PATH="$HOME/.cargo/bin:$PATH"
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
- name: Store cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/pip
|
||||||
|
~/.cache/pip-wheel
|
||||||
|
~/.cache/sccache
|
||||||
|
~/.cache/cargo-xwin
|
||||||
|
~/.cargo
|
||||||
|
~/.osxcross
|
||||||
|
key: cache-ubuntu-maturin-cross-compile
|
||||||
@@ -8,8 +8,10 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
publish_pytest:
|
publish_pytest:
|
||||||
uses: ./.github/workflows/pytest.yml
|
uses: ./.github/workflows/pytest.yml
|
||||||
|
publish_cargo_test:
|
||||||
|
uses: ./.github/workflows/cargo_test.yml
|
||||||
crates_io_publish:
|
crates_io_publish:
|
||||||
needs: [ publish_pytest ]
|
needs: [ publish_pytest, publish_cargo_test ]
|
||||||
name: Publish (crates.io)
|
name: Publish (crates.io)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 25
|
timeout-minutes: 25
|
||||||
@@ -50,7 +52,7 @@ jobs:
|
|||||||
# have passed.
|
# have passed.
|
||||||
- name: "cargo release publish"
|
- name: "cargo release publish"
|
||||||
run: |-
|
run: |-
|
||||||
export PATH="$HOME/.osxcross/bin:$PATH"
|
export PATH="$HOME/.cargo/bin:$PATH"
|
||||||
cargo login ${{ secrets.CRATES_IO_API_TOKEN }}
|
cargo login ${{ secrets.CRATES_IO_API_TOKEN }}
|
||||||
cargo release \
|
cargo release \
|
||||||
publish \
|
publish \
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
publish_pytest:
|
publish_pytest:
|
||||||
uses: ./.github/workflows/pytest.yml
|
uses: ./.github/workflows/pytest.yml
|
||||||
|
publish_cargo_test:
|
||||||
|
uses: ./.github/workflows/cargo_test.yml
|
||||||
pypi_publish:
|
pypi_publish:
|
||||||
needs: [ publish_pytest ]
|
needs: [ publish_pytest, publish_cargo_test ]
|
||||||
name: Publish (pypi.org)
|
name: Publish (pypi.org)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
export PATH="$HOME/.cargo/bin:$PATH"
|
export PATH="$HOME/.cargo/bin:$PATH"
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install maturin ziglang
|
pip install maturin
|
||||||
if ! command -v sccache >/dev/null 2>&1; then
|
if ! command -v sccache >/dev/null 2>&1; then
|
||||||
cargo install sccache || pip install sccache
|
cargo install sccache || pip install sccache
|
||||||
fi
|
fi
|
||||||
|
|||||||
+2
-1
@@ -29,10 +29,11 @@ ndarray = "0.17"
|
|||||||
num = "0.4"
|
num = "0.4"
|
||||||
numpy = { version = "0.28", optional = true }
|
numpy = { version = "0.28", optional = true }
|
||||||
pyo3 = { version = "0.28", features = ["abi3-py310", "eyre", "generate-import-lib", "multiple-pymethods"], optional = true }
|
pyo3 = { version = "0.28", features = ["abi3-py310", "eyre", "generate-import-lib", "multiple-pymethods"], optional = true }
|
||||||
|
pyo3-stub-gen = { version = "0.22", optional = true }
|
||||||
rayon = "1"
|
rayon = "1"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
tokio = { version = "1", features = ["fs", "rt", "rt-multi-thread", "time"] }
|
tokio = { version = "1", features = ["fs", "rt", "rt-multi-thread", "time"] }
|
||||||
zstd = "0.13"
|
zstd = "0.13"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
python = ["dep:pyo3", "dep:numpy", "dep:color-eyre"]
|
python = ["dep:pyo3", "dep:numpy", "dep:color-eyre", "dep:pyo3-stub-gen"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://github.com/wimpomp/tiffwrite/actions/workflows/pytest.yml)
|
[](https://git.wimpomp.nl/wim/tiffwrite/actions/workflows/pytest.yml)
|
||||||
|
|
||||||
# Tiffwrite
|
# Tiffwrite
|
||||||
Write BioFormats/ImageJ compatible tiffs with zstd compression in parallel using Rust.
|
Write BioFormats/ImageJ compatible tiffs with zstd compression in parallel using Rust.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -10,7 +11,7 @@ import numpy as np
|
|||||||
from numpy.typing import ArrayLike, DTypeLike
|
from numpy.typing import ArrayLike, DTypeLike
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
from . import tiffwrite_rs as rs # noqa
|
from . import tiffwrite_rs as rs
|
||||||
|
|
||||||
__all__ = ["IJTiffFile", "IJTiffParallel", "FrameInfo", "Tag", "tiffwrite"]
|
__all__ = ["IJTiffFile", "IJTiffParallel", "FrameInfo", "Tag", "tiffwrite"]
|
||||||
|
|
||||||
@@ -236,3 +237,16 @@ try:
|
|||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
IJTiffParallel = None
|
IJTiffParallel = None
|
||||||
|
|
||||||
|
|
||||||
|
def tiffwrite_generate_stub():
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
path = Path(sys.argv[1]).resolve()
|
||||||
|
else:
|
||||||
|
path = Path.cwd().resolve()
|
||||||
|
if (path / "py" / "tiffwrite" / "__init__.py").exists():
|
||||||
|
rs.generate_stub(str(path)) # noqa
|
||||||
|
(path / "py" / "tiffwrite_rs" / "__init__.pyi").rename(path / "py" / "tiffwrite" / "tiffwrite_rs.pyi")
|
||||||
|
(path / "py" / "tiffwrite_rs").rmdir()
|
||||||
|
else:
|
||||||
|
raise ModuleNotFoundError(str(path / "py" / "tiffwrite" / "__init__.py"))
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# This file is automatically generated by pyo3_stub_gen
|
||||||
|
# ruff: noqa: E501, F401, F403, F405
|
||||||
|
|
||||||
|
import builtins
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import numpy.typing
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"IJTiffFile",
|
||||||
|
"Tag",
|
||||||
|
]
|
||||||
|
|
||||||
|
class IJTiffFile:
|
||||||
|
@property
|
||||||
|
def colors(self) -> typing.Optional[builtins.list[builtins.list[builtins.int]]]: ...
|
||||||
|
@colors.setter
|
||||||
|
def colors(self, value: typing.Sequence[builtins.str]) -> None: ...
|
||||||
|
@property
|
||||||
|
def colormap(self) -> typing.Optional[builtins.list[builtins.list[builtins.int]]]: ...
|
||||||
|
@colormap.setter
|
||||||
|
def colormap(self, value: builtins.str) -> None: ...
|
||||||
|
@property
|
||||||
|
def px_size(self) -> typing.Optional[builtins.float]: ...
|
||||||
|
@px_size.setter
|
||||||
|
def px_size(self, value: builtins.float) -> None: ...
|
||||||
|
@property
|
||||||
|
def delta_z(self) -> typing.Optional[builtins.float]: ...
|
||||||
|
@delta_z.setter
|
||||||
|
def delta_z(self, value: builtins.float) -> None: ...
|
||||||
|
@property
|
||||||
|
def time_interval(self) -> typing.Optional[builtins.float]: ...
|
||||||
|
@time_interval.setter
|
||||||
|
def time_interval(self, value: builtins.float) -> None: ...
|
||||||
|
@property
|
||||||
|
def comment(self) -> typing.Optional[builtins.str]: ...
|
||||||
|
@comment.setter
|
||||||
|
def comment(self, value: builtins.str) -> None: ...
|
||||||
|
def __new__(cls, path: builtins.str) -> IJTiffFile: ...
|
||||||
|
def set_compression(self, compression: builtins.int, level: builtins.int) -> None:
|
||||||
|
r"""
|
||||||
|
set zstd compression level: -7 ..= 22
|
||||||
|
"""
|
||||||
|
def append_extra_tag(
|
||||||
|
self, tag: Tag, czt: typing.Optional[tuple[builtins.int, builtins.int, builtins.int]] = None
|
||||||
|
) -> None: ...
|
||||||
|
def get_tags(
|
||||||
|
self, czt: typing.Optional[tuple[builtins.int, builtins.int, builtins.int]] = None
|
||||||
|
) -> builtins.list[Tag]: ...
|
||||||
|
def close(self) -> None: ...
|
||||||
|
def save_f64(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_u32(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_u16(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_i64(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_f32(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_u8(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_i32(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_u64(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_i16(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
def save_i8(self, frame: numpy.typing.ArrayLike, c: builtins.int, t: builtins.int, z: builtins.int) -> None: ...
|
||||||
|
|
||||||
|
class Tag:
|
||||||
|
@staticmethod
|
||||||
|
def byte(code: builtins.int, byte: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def ascii(code: builtins.int, ascii: builtins.str) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def short(code: builtins.int, short: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def long(code: builtins.int, long: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def rational(code: builtins.int, rational: typing.Sequence[builtins.float]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def sbyte(code: builtins.int, sbyte: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def sshort(code: builtins.int, sshort: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def slong(code: builtins.int, slong: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def srational(code: builtins.int, srational: typing.Sequence[builtins.float]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def float(code: builtins.int, float: typing.Sequence[builtins.float]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def double(code: builtins.int, double: typing.Sequence[builtins.float]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def ifd(code: builtins.int, ifd: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def unicode(code: builtins.int, unicode: builtins.str) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def complex(code: builtins.int, complex: typing.Sequence[tuple[builtins.float, builtins.float]]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def long8(code: builtins.int, long8: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def slong8(code: builtins.int, slong8: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
@staticmethod
|
||||||
|
def ifd8(code: builtins.int, ifd8: typing.Sequence[builtins.int]) -> Tag: ...
|
||||||
|
def count(self) -> builtins.int:
|
||||||
|
r"""
|
||||||
|
get the number of values in the tag
|
||||||
|
"""
|
||||||
@@ -33,6 +33,9 @@ test = ["pytest", "tifffile", "imagecodecs"]
|
|||||||
homepage = "https://github.com/wimpomp/tiffwrite"
|
homepage = "https://github.com/wimpomp/tiffwrite"
|
||||||
repository = "https://github.com/wimpomp/tiffwrite"
|
repository = "https://github.com/wimpomp/tiffwrite"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
tiffwrite_generate_stub = "tiffwrite:tiffwrite_generate_stub"
|
||||||
|
|
||||||
[tool.maturin]
|
[tool.maturin]
|
||||||
python-source = "py"
|
python-source = "py"
|
||||||
features = ["pyo3/extension-module", "python"]
|
features = ["pyo3/extension-module", "python"]
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ use num::{Complex, FromPrimitive, Rational32};
|
|||||||
use numpy::{AllowTypeChange, PyArrayLike2};
|
use numpy::{AllowTypeChange, PyArrayLike2};
|
||||||
use pyo3::exceptions::PyValueError;
|
use pyo3::exceptions::PyValueError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
|
||||||
|
use pyo3_stub_gen::{StubGenConfig, StubInfo};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
impl From<crate::error::Error> for PyErr {
|
impl From<crate::error::Error> for PyErr {
|
||||||
fn from(err: crate::error::Error) -> PyErr {
|
fn from(err: crate::error::Error) -> PyErr {
|
||||||
@@ -10,6 +13,7 @@ impl From<crate::error::Error> for PyErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gen_stub_pyclass]
|
||||||
#[pyclass(name = "Tag", module = "tiffwrite_rs", subclass, from_py_object)]
|
#[pyclass(name = "Tag", module = "tiffwrite_rs", subclass, from_py_object)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct PyTag {
|
struct PyTag {
|
||||||
@@ -17,6 +21,7 @@ struct PyTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tiff tag, use one of the constructors to get a tag of a specific type
|
/// Tiff tag, use one of the constructors to get a tag of a specific type
|
||||||
|
#[gen_stub_pymethods]
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyTag {
|
impl PyTag {
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
@@ -162,12 +167,14 @@ impl PyTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gen_stub_pyclass]
|
||||||
#[pyclass(name = "IJTiffFile", module = "tiffwrite_rs", subclass)]
|
#[pyclass(name = "IJTiffFile", module = "tiffwrite_rs", subclass)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PyIJTiffFile {
|
struct PyIJTiffFile {
|
||||||
ijtifffile: Option<IJTiffFile>,
|
ijtifffile: Option<IJTiffFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gen_stub_pymethods]
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyIJTiffFile {
|
impl PyIJTiffFile {
|
||||||
#[new]
|
#[new]
|
||||||
@@ -333,10 +340,12 @@ impl PyIJTiffFile {
|
|||||||
macro_rules! impl_save {
|
macro_rules! impl_save {
|
||||||
($($T:ty: $t:ident $(,)?)*) => {
|
($($T:ty: $t:ident $(,)?)*) => {
|
||||||
$(
|
$(
|
||||||
|
#[gen_stub_pymethods]
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl PyIJTiffFile {
|
impl PyIJTiffFile {
|
||||||
fn $t(
|
fn $t(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
#[gen_stub(override_type(type_repr="numpy.typing.ArrayLike", imports=("numpy", "numpy.typing")))]
|
||||||
frame: PyArrayLike2<$T, AllowTypeChange>,
|
frame: PyArrayLike2<$T, AllowTypeChange>,
|
||||||
c: usize,
|
c: usize,
|
||||||
t: usize,
|
t: usize,
|
||||||
@@ -365,11 +374,28 @@ impl_save! {
|
|||||||
f64: save_f64,
|
f64: save_f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generates tiffwrite/tiffwrite_rs.pyi
|
||||||
|
#[pyfunction]
|
||||||
|
fn generate_stub(dest_path: String) -> PyResult<()> {
|
||||||
|
StubInfo::from_project_root(
|
||||||
|
"tiffwrite_rs".to_string(),
|
||||||
|
PathBuf::from(dest_path).join("py"),
|
||||||
|
true,
|
||||||
|
StubGenConfig::default(),
|
||||||
|
)
|
||||||
|
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))?
|
||||||
|
.generate()
|
||||||
|
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
#[pyo3(name = "tiffwrite_rs")]
|
#[pyo3(name = "tiffwrite_rs")]
|
||||||
mod tiffwrite_rs {
|
mod tiffwrite_rs {
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[pymodule_export]
|
||||||
|
use super::generate_stub;
|
||||||
|
|
||||||
#[pymodule_export]
|
#[pymodule_export]
|
||||||
use super::PyTag;
|
use super::PyTag;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user