From 5c08081c6902f4fe45f389203f1033be9d1a8d0b Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 13:06:58 +0200 Subject: [PATCH 001/131] init --- src/squidpy/gr/__init__.py | 16 +- src/squidpy/gr/_build.py | 531 +++++++++++++++++++++----- src/squidpy/gr/neighbors.py | 7 + tests/graph/test_spatial_neighbors.py | 38 +- 4 files changed, 505 insertions(+), 87 deletions(-) create mode 100644 src/squidpy/gr/neighbors.py diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index 7bb9bb4a2..9a5b454c8 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -2,7 +2,16 @@ from __future__ import annotations -from squidpy.gr._build import SpatialNeighborsResult, mask_graph, spatial_neighbors +from squidpy.gr._build import ( + DelaunayBuilder, + GraphBuilder, + GridBuilder, + KNNBuilder, + RadiusBuilder, + SpatialNeighborsResult, + mask_graph, + spatial_neighbors, +) from squidpy.gr._ligrec import ligrec from squidpy.gr._nhood import ( NhoodEnrichmentResult, @@ -17,6 +26,11 @@ __all__ = [ "SpatialNeighborsResult", + "GraphBuilder", + "KNNBuilder", + "RadiusBuilder", + "DelaunayBuilder", + "GridBuilder", "NhoodEnrichmentResult", "mask_graph", "spatial_neighbors", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 32630955d..104ad040c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,6 +3,7 @@ from __future__ import annotations import warnings +from abc import ABC, abstractmethod from collections.abc import Iterable from functools import partial from itertools import chain @@ -49,7 +50,15 @@ _save_data, ) -__all__ = ["spatial_neighbors"] +__all__ = [ + "SpatialNeighborsResult", + "GraphBuilder", + "KNNBuilder", + "RadiusBuilder", + "DelaunayBuilder", + "GridBuilder", + "spatial_neighbors", +] class SpatialNeighborsResult(NamedTuple): @@ -59,6 +68,358 @@ class SpatialNeighborsResult(NamedTuple): distances: csr_matrix +class GraphBuilder(ABC): + """Base class for spatial graph construction strategies.""" + + def __init__( + self, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + self.transform = Transform.NONE if transform is None else Transform(transform) + self.set_diag = set_diag + self.percentile = percentile + + @property + @abstractmethod + def coord_type(self) -> CoordType: + """Coordinate system supported by this builder.""" + + @property + def legacy_params(self) -> dict[str, Any]: + """Return parameters expressed in the legacy spatial_neighbors API.""" + return { + "coord_type": self.coord_type, + "n_neighs": 6, + "radius": None, + "delaunay": False, + "n_rings": 1, + "percentile": self.percentile, + "transform": self.transform, + "set_diag": self.set_diag, + } + + @property + def metadata(self) -> dict[str, Any]: + """Return metadata stored in adata.uns after graph construction.""" + params = self.legacy_params + return { + "n_neighbors": params["n_neighs"], + "coord_type": self.coord_type.v, + "radius": params["radius"], + "transform": self.transform.v, + } + + def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", SparseEfficiencyWarning) + Adj, Dst = self._build_graph(coords) + + self._apply_filters(Adj, Dst) + self._apply_percentile(Adj, Dst) + Adj.eliminate_zeros() + Dst.eliminate_zeros() + + return self._apply_transform(Adj), Dst + + @abstractmethod + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + """Construct raw adjacency and distance matrices.""" + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + """Apply builder-specific post-processing filters.""" + return None + + def _apply_percentile(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if self.percentile is not None and self.coord_type == CoordType.GENERIC: + threshold = np.percentile(Dst.data, self.percentile) + Adj[Dst > threshold] = 0.0 + Dst[Dst > threshold] = 0.0 + + def _apply_transform(self, Adj: csr_matrix) -> csr_matrix: + if self.transform == Transform.SPECTRAL: + return cast(csr_matrix, _transform_a_spectral(Adj)) + if self.transform == Transform.COSINE: + return cast(csr_matrix, _transform_a_cosine(Adj)) + if self.transform == Transform.NONE: + return Adj + + raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") + + +class KNNBuilder(GraphBuilder): + """Build a generic k-nearest-neighbor spatial graph.""" + + def __init__( + self, + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.n_neighs = n_neighs + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + +class RadiusBuilder(GraphBuilder): + """Build a generic radius-based spatial graph.""" + + def __init__( + self, + radius: float | tuple[float, float], + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.radius = radius + self.n_neighs = n_neighs + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + radius=self.radius, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) + + +class DelaunayBuilder(GraphBuilder): + """Build a generic spatial graph from a Delaunay triangulation.""" + + def __init__( + self, + radius: float | tuple[float, float] | None = None, + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.radius = radius + self.n_neighs = n_neighs + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius, "delaunay": True} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + radius=self.radius, + delaunay=True, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) + + +class GridBuilder(GraphBuilder): + """Build a grid-based spatial graph.""" + + def __init__( + self, + n_neighs: int = 6, + n_rings: int = 1, + delaunay: bool = False, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + assert_positive(n_rings, name="n_rings") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.n_neighs = n_neighs + self.n_rings = n_rings + self.delaunay = delaunay + + @property + def coord_type(self) -> CoordType: + return CoordType.GRID + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | { + "n_neighs": self.n_neighs, + "delaunay": self.delaunay, + "n_rings": self.n_rings, + } + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return _build_grid( + coords, + n_neighs=self.n_neighs, + n_rings=self.n_rings, + delaunay=self.delaunay, + set_diag=self.set_diag, + ) + + +def _filter_by_radius_interval( + Adj: csr_matrix, + Dst: csr_matrix, + radius: Iterable[float], +) -> None: + minn, maxx = sorted(radius)[:2] + mask = (Dst.data < minn) | (Dst.data > maxx) + a_diag = Adj.diagonal() + + Dst.data[mask] = 0.0 + Adj.data[mask] = 0.0 + Adj.setdiag(a_diag) + + +def _normalize_builder_param(name: str, value: Any) -> Any: + if name == "coord_type": + return None if value is None else CoordType(value) + if name == "transform": + return Transform.NONE if value is None else Transform(value) + if name == "radius" and isinstance(value, tuple): + return tuple(sorted(value)[:2]) + + return value + + +def _validate_builder_compatibility( + builder: GraphBuilder, + *, + coord_type: str | CoordType | None, + n_neighs: int, + radius: float | tuple[float, float] | None, + delaunay: bool, + n_rings: int, + percentile: float | None, + transform: str | Transform | None, + set_diag: bool, +) -> None: + defaults = { + "coord_type": None, + "n_neighs": 6, + "radius": None, + "delaunay": False, + "n_rings": 1, + "percentile": None, + "transform": None, + "set_diag": False, + } + provided = { + "coord_type": coord_type, + "n_neighs": n_neighs, + "radius": radius, + "delaunay": delaunay, + "n_rings": n_rings, + "percentile": percentile, + "transform": transform, + "set_diag": set_diag, + } + + for key, value in provided.items(): + if value == defaults[key]: + continue + + if _normalize_builder_param(key, value) != _normalize_builder_param(key, builder.legacy_params[key]): + raise ValueError( + f"Parameter `{key}` conflicts with `{type(builder).__name__}`. " + "Leave graph-construction arguments at their defaults or make them match the builder." + ) + + +def _resolve_graph_builder( + *, + coord_type: CoordType, + n_neighs: int, + radius: float | tuple[float, float] | None, + delaunay: bool, + n_rings: int, + percentile: float | None, + transform: Transform, + set_diag: bool, +) -> GraphBuilder: + if coord_type == CoordType.GRID: + return GridBuilder( + n_neighs=n_neighs, + n_rings=n_rings, + delaunay=delaunay, + transform=transform, + set_diag=set_diag, + percentile=percentile, + ) + if delaunay: + return DelaunayBuilder( + radius=radius, + n_neighs=n_neighs, + transform=transform, + set_diag=set_diag, + percentile=percentile, + ) + if radius is not None: + return RadiusBuilder( + radius=radius, + n_neighs=n_neighs, + transform=transform, + set_diag=set_diag, + percentile=percentile, + ) + + return KNNBuilder( + n_neighs=n_neighs, + transform=transform, + set_diag=set_diag, + percentile=percentile, + ) + + @d.dedent @inject_docs(t=Transform, c=CoordType) def spatial_neighbors( @@ -75,6 +436,7 @@ def spatial_neighbors( percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool = False, + builder: GraphBuilder | None = None, key_added: str = "spatial", copy: bool = False, ) -> SpatialNeighborsResult | None: @@ -128,10 +490,47 @@ def spatial_neighbors( - `{t.NONE.v}` - no transformation of the adjacency matrix. set_diag Whether to set the diagonal of the spatial connectivities to `1.0`. + builder + Advanced graph construction strategy. When provided, graph-construction arguments + (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, ``percentile``, + ``transform``, ``set_diag``) must either be left at their defaults or match the builder. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s + Notes + ----- + ``spatial_neighbors`` has 4 graph-construction modes: + + - Grid mode: + ``coord_type='grid'``. Uses ``n_neighs`` and ``n_rings``. + ``radius`` is ignored. ``delaunay`` is forwarded to the + underlying grid connectivity builder. This is the mode used + for Visium-like grid coordinates. + - Generic k-nearest-neighbor mode: + ``coord_type='generic'``, ``delaunay=False``, ``radius=None``. + Uses ``n_neighs``. + - Generic radius mode: + ``coord_type='generic'``, ``delaunay=False``, ``radius`` set. + Uses ``radius`` and builds a radius-based neighbor graph. + If ``radius`` is a tuple, the graph is built with the maximum + radius and then pruned to the interval + ``[min(radius), max(radius)]``. + - Generic Delaunay mode: + ``coord_type='generic'``, ``delaunay=True``. + Builds a Delaunay triangulation graph. ``n_neighs`` is not + used for the triangulation itself, but is still accepted for + backward compatibility. If ``radius`` is a tuple, it is used + only as a post-construction pruning interval. + + Across these modes: + + - ``percentile`` only affects generic graphs. + - ``transform`` and ``set_diag`` apply to all modes. + - If ``builder`` is provided, it determines the mode directly. + The legacy graph-construction arguments must then stay at + their defaults or match the builder configuration. + Returns ------- If ``copy = True``, returns a :class:`~squidpy.gr.SpatialNeighborsResult` with the spatial connectivities and distances matrices. @@ -191,20 +590,47 @@ def spatial_neighbors( adata = adata.tables[table_key] library_key = region_key - assert_positive(n_rings, name="n_rings") - assert_positive(n_neighs, name="n_neighs") _assert_spatial_basis(adata, spatial_key) - transform = Transform.NONE if transform is None else Transform(transform) - if coord_type is None: - if radius is not None: - logg.warning( - f"Graph creation with `radius` is only available when `coord_type = {CoordType.GENERIC!r}` specified. " - f"Ignoring parameter `radius = {radius}`." - ) - coord_type = CoordType.GRID if Key.uns.spatial in adata.uns else CoordType.GENERIC + if builder is not None: + if not isinstance(builder, GraphBuilder): + raise TypeError(f"Expected `builder` to be a `GraphBuilder`, found `{type(builder)}`.") + _validate_builder_compatibility( + builder, + coord_type=coord_type, + n_neighs=n_neighs, + radius=radius, + delaunay=delaunay, + n_rings=n_rings, + percentile=percentile, + transform=transform, + set_diag=set_diag, + ) else: - coord_type = CoordType(coord_type) + assert_positive(n_rings, name="n_rings") + assert_positive(n_neighs, name="n_neighs") + + transform = Transform.NONE if transform is None else Transform(transform) + if coord_type is None: + if radius is not None: + logg.warning( + f"Graph creation with `radius` is only available when `coord_type = {CoordType.GENERIC!r}` specified. " + f"Ignoring parameter `radius = {radius}`." + ) + coord_type = CoordType.GRID if Key.uns.spatial in adata.uns else CoordType.GENERIC + else: + coord_type = CoordType(coord_type) + + builder = _resolve_graph_builder( + coord_type=coord_type, + n_neighs=n_neighs, + radius=radius, + delaunay=delaunay, + n_rings=n_rings, + percentile=percentile, + transform=transform, + set_diag=set_diag, + ) if library_key is not None: _assert_categorical_obs(adata, key=library_key) @@ -214,19 +640,12 @@ def spatial_neighbors( libs = [None] start = logg.info( - f"Creating graph using `{coord_type}` coordinates and `{transform}` transform and `{len(libs)}` libraries." + f"Creating graph using `{builder.coord_type}` coordinates and `{builder.transform}` transform and `{len(libs)}` libraries." ) _build_fun = partial( _spatial_neighbor, spatial_key=spatial_key, - coord_type=coord_type, - n_neighs=n_neighs, - radius=radius, - delaunay=delaunay, - n_rings=n_rings, - transform=transform, - set_diag=set_diag, - percentile=percentile, + builder=builder, ) if library_key is not None: @@ -248,12 +667,7 @@ def spatial_neighbors( neighbors_dict = { "connectivities_key": conns_key, "distances_key": dists_key, - "params": { - "n_neighbors": n_neighs, - "coord_type": coord_type.v, - "radius": radius, - "transform": transform.v, - }, + "params": builder.metadata, } if copy: @@ -267,66 +681,13 @@ def spatial_neighbors( def _spatial_neighbor( adata: AnnData, spatial_key: str = Key.obsm.spatial, - coord_type: str | CoordType | None = None, - n_neighs: int = 6, - radius: float | tuple[float, float] | None = None, - delaunay: bool = False, - n_rings: int = 1, - transform: str | Transform | None = None, - set_diag: bool = False, - percentile: float | None = None, + builder: GraphBuilder | None = None, ) -> tuple[csr_matrix, csr_matrix]: - coords = adata.obsm[spatial_key] - with warnings.catch_warnings(): - warnings.simplefilter("ignore", SparseEfficiencyWarning) - if coord_type == CoordType.GRID: - Adj, Dst = _build_grid( - coords, - n_neighs=n_neighs, - n_rings=n_rings, - delaunay=delaunay, - set_diag=set_diag, - ) - elif coord_type == CoordType.GENERIC: - Adj, Dst = _build_connectivity( - coords, - n_neighs=n_neighs, - radius=radius, - delaunay=delaunay, - return_distance=True, - set_diag=set_diag, - ) - else: - raise NotImplementedError(f"Coordinate type `{coord_type}` is not yet implemented.") - - if coord_type == CoordType.GENERIC and isinstance(radius, Iterable): - minn, maxx = sorted(radius)[:2] - mask = (Dst.data < minn) | (Dst.data > maxx) - a_diag = Adj.diagonal() - - Dst.data[mask] = 0.0 - Adj.data[mask] = 0.0 - Adj.setdiag(a_diag) - - if percentile is not None and coord_type == CoordType.GENERIC: - threshold = np.percentile(Dst.data, percentile) - Adj[Dst > threshold] = 0.0 - Dst[Dst > threshold] = 0.0 - - Adj.eliminate_zeros() - Dst.eliminate_zeros() - - # check transform - if transform == Transform.SPECTRAL: - Adj = _transform_a_spectral(Adj) - elif transform == Transform.COSINE: - Adj = _transform_a_cosine(Adj) - elif transform == Transform.NONE: - pass - else: - raise NotImplementedError(f"Transform `{transform}` is not yet implemented.") + if builder is None: + raise ValueError("No graph builder was provided.") - return Adj, Dst + coords = adata.obsm[spatial_key] + return builder.build(coords) def _build_grid( diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py new file mode 100644 index 000000000..3e9d1d14c --- /dev/null +++ b/src/squidpy/gr/neighbors.py @@ -0,0 +1,7 @@ +"""Public builder API for spatial neighbor graph construction.""" + +from __future__ import annotations + +from squidpy.gr._build import DelaunayBuilder, GraphBuilder, GridBuilder, KNNBuilder, RadiusBuilder + +__all__ = ["GraphBuilder", "KNNBuilder", "RadiusBuilder", "DelaunayBuilder", "GridBuilder"] diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index db638132b..57b6528c0 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -11,8 +11,9 @@ from spatialdata.datasets import blobs from squidpy._constants._pkg_constants import Key -from squidpy.gr import mask_graph, spatial_neighbors +from squidpy.gr import DelaunayBuilder, GridBuilder, KNNBuilder, RadiusBuilder, mask_graph, spatial_neighbors from squidpy.gr._build import _build_connectivity +from squidpy.gr.neighbors import KNNBuilder as PublicKNNBuilder class TestSpatialNeighbors: @@ -213,6 +214,41 @@ def test_copy(self, non_visium_adata: AnnData): np.testing.assert_allclose(result.distances.toarray(), self._gt_ddist) np.testing.assert_allclose(result.connectivities.toarray(), self._gt_dgraph) + def test_builder_module_export(self): + assert PublicKNNBuilder is KNNBuilder + + def test_knn_builder_matches_legacy(self, non_visium_adata: AnnData): + legacy = spatial_neighbors(non_visium_adata, n_neighs=3, coord_type="generic", copy=True) + builder = spatial_neighbors(non_visium_adata, builder=KNNBuilder(n_neighs=3), copy=True) + + np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + + def test_radius_builder_matches_legacy(self, non_visium_adata: AnnData): + legacy = spatial_neighbors(non_visium_adata, radius=5.0, coord_type="generic", copy=True) + builder = spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), copy=True) + + np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + + def test_delaunay_builder_matches_legacy(self, non_visium_adata: AnnData): + legacy = spatial_neighbors(non_visium_adata, delaunay=True, coord_type="generic", copy=True) + builder = spatial_neighbors(non_visium_adata, builder=DelaunayBuilder(), copy=True) + + np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + + def test_grid_builder_matches_legacy(self, adata_squaregrid: AnnData): + legacy = spatial_neighbors(adata_squaregrid, n_neighs=4, n_rings=2, coord_type="grid", copy=True) + builder = spatial_neighbors(adata_squaregrid, builder=GridBuilder(n_neighs=4, n_rings=2), copy=True) + + np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + + def test_builder_argument_conflict(self, non_visium_adata: AnnData): + with pytest.raises(ValueError, match="conflicts"): + spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) + @pytest.mark.parametrize("percentile", [99.0, 95.0]) def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord_type="generic"): result = spatial_neighbors(adata_hne, coord_type=coord_type, copy=True) From 5847d8406b26ad1cf79ab9fcc342cb6c3a228c96 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 13:11:33 +0200 Subject: [PATCH 002/131] doc clarifications --- src/squidpy/gr/_build.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 104ad040c..25f9de2c0 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -530,6 +530,39 @@ def spatial_neighbors( - If ``builder`` is provided, it determines the mode directly. The legacy graph-construction arguments must then stay at their defaults or match the builder configuration. + - By default, observations are not treated as their own + neighbors. The distance matrix always has a zero diagonal. + The connectivity matrix only gets a nonzero diagonal when + ``set_diag=True``. + + Argument precedence + ------------------- + When ``builder`` is not provided, the mode is resolved as follows: + + - If ``coord_type`` resolves to ``'grid'``, grid mode is used. + In that case ``radius`` is ignored. + - Otherwise, if ``delaunay=True``, Delaunay mode is used. + In this mode ``n_neighs`` does not affect the triangulation. + A tuple ``radius`` is only used afterward as a pruning + interval. A scalar ``radius`` is ignored. + - Otherwise, if ``radius`` is set, radius mode is used. + In this mode ``n_neighs`` does not act as a second cutoff. + - Otherwise, k-nearest-neighbor mode is used. + + Grid-specific behavior + ---------------------- + Grid mode currently does not validate ``n_neighs`` to a fixed set + such as ``{4, 6}``. Internally it first queries the + ``n_neighs`` nearest candidates and then applies a distance-based + correction tuned for grid-like coordinates. As a result: + + - values such as ``n_neighs=4`` and ``n_neighs=6`` are the + intended square-grid and hex-grid choices, respectively; + - other values are accepted for backward compatibility, but + their geometric interpretation is not guaranteed to match a + continuous ring on the grid; + - no clockwise or other within-ring ordering is part of the + public API. Returns ------- From 179ba06a0ae6d54eb7029c4b4d167b780ba05367 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 13:35:54 +0200 Subject: [PATCH 003/131] don't expose the builders --- src/squidpy/gr/__init__.py | 10 ---------- src/squidpy/gr/_build.py | 9 ++++++--- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index 9a5b454c8..d4f39958e 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -3,11 +3,6 @@ from __future__ import annotations from squidpy.gr._build import ( - DelaunayBuilder, - GraphBuilder, - GridBuilder, - KNNBuilder, - RadiusBuilder, SpatialNeighborsResult, mask_graph, spatial_neighbors, @@ -26,11 +21,6 @@ __all__ = [ "SpatialNeighborsResult", - "GraphBuilder", - "KNNBuilder", - "RadiusBuilder", - "DelaunayBuilder", - "GridBuilder", "NhoodEnrichmentResult", "mask_graph", "spatial_neighbors", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 25f9de2c0..3823c22f7 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -275,11 +275,10 @@ def __init__( delaunay: bool = False, transform: str | Transform | None = None, set_diag: bool = False, - percentile: float | None = None, ) -> None: assert_positive(n_neighs, name="n_neighs") assert_positive(n_rings, name="n_rings") - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + super().__init__(transform=transform, set_diag=set_diag, percentile=None) self.n_neighs = n_neighs self.n_rings = n_rings self.delaunay = delaunay @@ -387,13 +386,17 @@ def _resolve_graph_builder( set_diag: bool, ) -> GraphBuilder: if coord_type == CoordType.GRID: + if percentile is not None: + raise ValueError( + "`percentile` is not supported for grid coordinates. " + "It only applies to generic (non-grid) graphs." + ) return GridBuilder( n_neighs=n_neighs, n_rings=n_rings, delaunay=delaunay, transform=transform, set_diag=set_diag, - percentile=percentile, ) if delaunay: return DelaunayBuilder( From e187abafd210691740163c04d5639445eb878b0d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:37:14 +0000 Subject: [PATCH 004/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 3823c22f7..b7bea6dfa 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -388,8 +388,7 @@ def _resolve_graph_builder( if coord_type == CoordType.GRID: if percentile is not None: raise ValueError( - "`percentile` is not supported for grid coordinates. " - "It only applies to generic (non-grid) graphs." + "`percentile` is not supported for grid coordinates. It only applies to generic (non-grid) graphs." ) return GridBuilder( n_neighs=n_neighs, From cdb569923c259dbb720d71816c9363236afeb65d Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 13:50:49 +0200 Subject: [PATCH 005/131] deduplicate --- src/squidpy/gr/_build.py | 37 +++--- tests/graph/test_spatial_neighbors.py | 183 ++++++++++++++++---------- 2 files changed, 133 insertions(+), 87 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 3823c22f7..be724e369 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -182,7 +182,22 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: ) -class RadiusBuilder(GraphBuilder): +class _RadiusFilterBuilder(GraphBuilder): + """Intermediate base for builders that support radius-interval pruning.""" + + radius: float | tuple[float, float] | None + n_neighs: int + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) + + +class RadiusBuilder(_RadiusFilterBuilder): """Build a generic radius-based spatial graph.""" def __init__( @@ -198,10 +213,6 @@ def __init__( self.radius = radius self.n_neighs = n_neighs - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - @property def legacy_params(self) -> dict[str, Any]: return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius} @@ -218,12 +229,8 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: ), ) - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) - -class DelaunayBuilder(GraphBuilder): +class DelaunayBuilder(_RadiusFilterBuilder): """Build a generic spatial graph from a Delaunay triangulation.""" def __init__( @@ -239,10 +246,6 @@ def __init__( self.radius = radius self.n_neighs = n_neighs - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - @property def legacy_params(self) -> dict[str, Any]: return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius, "delaunay": True} @@ -260,10 +263,6 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: ), ) - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) - class GridBuilder(GraphBuilder): """Build a grid-based spatial graph.""" @@ -555,7 +554,7 @@ def spatial_neighbors( Grid-specific behavior ---------------------- Grid mode currently does not validate ``n_neighs`` to a fixed set - such as ``{4, 6}``. Internally it first queries the + such as ``{{4, 6}}``. Internally it first queries the ``n_neighs`` nearest candidates and then applies a distance-based correction tuned for grid-like coordinates. As a result: diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 57b6528c0..b99c7c751 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -10,6 +10,7 @@ from shapely import Point from spatialdata.datasets import blobs +from squidpy._constants._constants import Transform from squidpy._constants._pkg_constants import Key from squidpy.gr import DelaunayBuilder, GridBuilder, KNNBuilder, RadiusBuilder, mask_graph, spatial_neighbors from squidpy.gr._build import _build_connectivity @@ -47,6 +48,22 @@ def _adata_concat(adata1, adata2): ) return adata_concat, batch1, batch2 + @staticmethod + def _assert_library_key_block_diagonal(adata, **neighbor_kwargs): + adata2 = adata.copy() + adata_concat, batch1, batch2 = TestSpatialNeighbors._adata_concat(adata, adata2) + spatial_neighbors(adata2, **neighbor_kwargs) + spatial_neighbors(adata_concat, library_key="library_id", **neighbor_kwargs) + np.testing.assert_array_equal( + adata_concat[adata_concat.obs["library_id"] == batch1].obsp[Key.obsp.spatial_conn()].toarray(), + adata.obsp[Key.obsp.spatial_conn()].toarray(), + ) + np.testing.assert_array_equal( + adata_concat[adata_concat.obs["library_id"] == batch2].obsp[Key.obsp.spatial_conn()].toarray(), + adata2.obsp[Key.obsp.spatial_conn()].toarray(), + ) + return adata_concat + # TODO: add edge cases # TODO(giovp): test with reshuffling @pytest.mark.parametrize(("n_rings", "n_neigh", "sum_dist"), [(1, 6, 0), (2, 18, 30), (3, 36, 84)]) @@ -66,20 +83,8 @@ def test_spatial_neighbors_visium( if n_rings > 1: assert visium_adata.obsp[Key.obsp.spatial_dist()][0].sum() == sum_dist - # test for library_key - visium_adata2 = visium_adata.copy() - adata_concat, batch1, batch2 = TestSpatialNeighbors._adata_concat(visium_adata, visium_adata2) - spatial_neighbors(visium_adata2, n_rings=n_rings) - spatial_neighbors(adata_concat, library_key="library_id", n_rings=n_rings) + adata_concat = self._assert_library_key_block_diagonal(visium_adata, n_rings=n_rings) assert adata_concat.obsp[Key.obsp.spatial_conn()][0].sum() == n_neigh - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch1].obsp[Key.obsp.spatial_conn()].toarray(), - visium_adata.obsp[Key.obsp.spatial_conn()].toarray(), - ) - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch2].obsp[Key.obsp.spatial_conn()].toarray(), - visium_adata2.obsp[Key.obsp.spatial_conn()].toarray(), - ) @pytest.mark.parametrize(("n_rings", "n_neigh", "sum_neigh"), [(1, 4, 4), (2, 4, 12), (3, 4, 24)]) def test_spatial_neighbors_squaregrid(self, adata_squaregrid: AnnData, n_rings: int, n_neigh: int, sum_neigh: int): @@ -91,26 +96,10 @@ def test_spatial_neighbors_squaregrid(self, adata_squaregrid: AnnData, n_rings: assert np.diff(adata.obsp[Key.obsp.spatial_conn()].indptr).max() == sum_neigh assert adata.uns[Key.uns.spatial_neighs()]["distances_key"] == Key.obsp.spatial_dist() - # test for library_key - adata2 = adata.copy() - adata_concat, batch1, batch2 = TestSpatialNeighbors._adata_concat(adata, adata2) - spatial_neighbors(adata2, n_neighs=n_neigh, n_rings=n_rings, coord_type="grid") - spatial_neighbors( - adata_concat, - library_key="library_id", - n_neighs=n_neigh, - n_rings=n_rings, - coord_type="grid", + adata_concat = self._assert_library_key_block_diagonal( + adata, n_neighs=n_neigh, n_rings=n_rings, coord_type="grid", ) assert np.diff(adata_concat.obsp[Key.obsp.spatial_conn()].indptr).max() == sum_neigh - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch1].obsp[Key.obsp.spatial_conn()].toarray(), - adata.obsp[Key.obsp.spatial_conn()].toarray(), - ) - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch2].obsp[Key.obsp.spatial_conn()].toarray(), - adata2.obsp[Key.obsp.spatial_conn()].toarray(), - ) @pytest.mark.parametrize("type_rings", [("grid", 1), ("grid", 6), ("generic", 1)]) @pytest.mark.parametrize("set_diag", [False, True]) @@ -162,20 +151,7 @@ def test_spatial_neighbors_non_visium(self, non_visium_adata: AnnData): np.testing.assert_array_equal(spatial_graph, self._gt_dgraph) np.testing.assert_allclose(spatial_dist, self._gt_ddist) - # test for library_key - non_visium_adata2 = non_visium_adata.copy() - adata_concat, batch1, batch2 = TestSpatialNeighbors._adata_concat(non_visium_adata, non_visium_adata2) - spatial_neighbors(adata_concat, library_key="library_id", delaunay=True, coord_type=None) - spatial_neighbors(non_visium_adata2, delaunay=True, coord_type=None) - - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch1].obsp[Key.obsp.spatial_conn()].toarray(), - non_visium_adata.obsp[Key.obsp.spatial_conn()].toarray(), - ) - np.testing.assert_array_equal( - adata_concat[adata_concat.obs["library_id"] == batch2].obsp[Key.obsp.spatial_conn()].toarray(), - non_visium_adata2.obsp[Key.obsp.spatial_conn()].toarray(), - ) + self._assert_library_key_block_diagonal(non_visium_adata, delaunay=True, coord_type=None) @pytest.mark.parametrize("set_diag", [False, True]) @pytest.mark.parametrize("radius", [(0, np.inf), (2.0, 4.0), (-42, -420), (100, 200)]) @@ -217,37 +193,108 @@ def test_copy(self, non_visium_adata: AnnData): def test_builder_module_export(self): assert PublicKNNBuilder is KNNBuilder - def test_knn_builder_matches_legacy(self, non_visium_adata: AnnData): - legacy = spatial_neighbors(non_visium_adata, n_neighs=3, coord_type="generic", copy=True) - builder = spatial_neighbors(non_visium_adata, builder=KNNBuilder(n_neighs=3), copy=True) + @pytest.mark.parametrize( + ("legacy_kwargs", "builder"), + [ + ({"n_neighs": 3, "coord_type": "generic"}, KNNBuilder(n_neighs=3)), + ({"radius": 5.0, "coord_type": "generic"}, RadiusBuilder(radius=5.0)), + ({"delaunay": True, "coord_type": "generic"}, DelaunayBuilder()), + ], + ids=["knn", "radius", "delaunay"], + ) + def test_generic_builder_matches_legacy( + self, non_visium_adata: AnnData, legacy_kwargs: dict, builder: object + ): + legacy = spatial_neighbors(non_visium_adata, **legacy_kwargs, copy=True) + result = spatial_neighbors(non_visium_adata, builder=builder, copy=True) - np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) - np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + np.testing.assert_array_equal(legacy.connectivities.toarray(), result.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), result.distances.toarray()) - def test_radius_builder_matches_legacy(self, non_visium_adata: AnnData): - legacy = spatial_neighbors(non_visium_adata, radius=5.0, coord_type="generic", copy=True) - builder = spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), copy=True) + @pytest.mark.parametrize( + ("legacy_kwargs", "builder"), + [ + ({"n_neighs": 4, "n_rings": 2, "coord_type": "grid"}, GridBuilder(n_neighs=4, n_rings=2)), + ({"n_neighs": 6, "n_rings": 1, "coord_type": "grid"}, GridBuilder(n_neighs=6, n_rings=1)), + ], + ids=["4neighs_2rings", "6neighs_1ring"], + ) + def test_grid_builder_matches_legacy( + self, adata_squaregrid: AnnData, legacy_kwargs: dict, builder: object + ): + legacy = spatial_neighbors(adata_squaregrid, **legacy_kwargs, copy=True) + result = spatial_neighbors(adata_squaregrid, builder=builder, copy=True) - np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) - np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + np.testing.assert_array_equal(legacy.connectivities.toarray(), result.connectivities.toarray()) + np.testing.assert_allclose(legacy.distances.toarray(), result.distances.toarray()) - def test_delaunay_builder_matches_legacy(self, non_visium_adata: AnnData): - legacy = spatial_neighbors(non_visium_adata, delaunay=True, coord_type="generic", copy=True) - builder = spatial_neighbors(non_visium_adata, builder=DelaunayBuilder(), copy=True) + def test_builder_argument_conflict(self, non_visium_adata: AnnData): + with pytest.raises(ValueError, match="conflicts"): + spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) - np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) - np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + def test_builder_matching_non_default_legacy_args(self, non_visium_adata: AnnData): + builder = RadiusBuilder(radius=5.0, n_neighs=3, percentile=95.0, transform="cosine", set_diag=True) - def test_grid_builder_matches_legacy(self, adata_squaregrid: AnnData): - legacy = spatial_neighbors(adata_squaregrid, n_neighs=4, n_rings=2, coord_type="grid", copy=True) - builder = spatial_neighbors(adata_squaregrid, builder=GridBuilder(n_neighs=4, n_rings=2), copy=True) + baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) + matched = spatial_neighbors( + non_visium_adata, + builder=builder, + coord_type="generic", + n_neighs=3, + radius=5.0, + percentile=95.0, + transform=Transform.COSINE, + set_diag=True, + copy=True, + ) - np.testing.assert_array_equal(legacy.connectivities.toarray(), builder.connectivities.toarray()) - np.testing.assert_allclose(legacy.distances.toarray(), builder.distances.toarray()) + np.testing.assert_allclose(baseline.connectivities.toarray(), matched.connectivities.toarray()) + np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) - def test_builder_argument_conflict(self, non_visium_adata: AnnData): - with pytest.raises(ValueError, match="conflicts"): - spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) + def test_builder_compatibility_normalizes_none_transform(self, non_visium_adata: AnnData): + builder = KNNBuilder(n_neighs=3, transform=Transform.NONE) + + baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) + matched = spatial_neighbors(non_visium_adata, builder=builder, transform=None, copy=True) + + np.testing.assert_array_equal(baseline.connectivities.toarray(), matched.connectivities.toarray()) + np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) + + def test_builder_compatibility_normalizes_tuple_radius_order(self, non_visium_adata: AnnData): + builder = RadiusBuilder(radius=(4.0, 2.0)) + + baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) + matched = spatial_neighbors( + non_visium_adata, + builder=builder, + coord_type="generic", + radius=(2.0, 4.0), + copy=True, + ) + + np.testing.assert_array_equal(baseline.connectivities.toarray(), matched.connectivities.toarray()) + np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) + + def test_grid_mode_ignores_radius(self, adata_squaregrid: AnnData): + default = spatial_neighbors(adata_squaregrid, coord_type="grid", n_neighs=4, n_rings=2, copy=True) + ignored = spatial_neighbors( + adata_squaregrid, + coord_type="grid", + n_neighs=4, + n_rings=2, + radius=(0.1, 0.2), + copy=True, + ) + + np.testing.assert_array_equal(default.connectivities.toarray(), ignored.connectivities.toarray()) + np.testing.assert_allclose(default.distances.toarray(), ignored.distances.toarray()) + + def test_delaunay_mode_ignores_scalar_radius(self, non_visium_adata: AnnData): + default = spatial_neighbors(non_visium_adata, coord_type="generic", delaunay=True, copy=True) + ignored = spatial_neighbors(non_visium_adata, coord_type="generic", delaunay=True, radius=5.0, copy=True) + + np.testing.assert_array_equal(default.connectivities.toarray(), ignored.connectivities.toarray()) + np.testing.assert_allclose(default.distances.toarray(), ignored.distances.toarray()) @pytest.mark.parametrize("percentile", [99.0, 95.0]) def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord_type="generic"): From 69cda1acbe05444be285b9b471de60357470ca74 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:51:15 +0000 Subject: [PATCH 006/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/graph/test_spatial_neighbors.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index b99c7c751..be1d897e7 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -97,7 +97,10 @@ def test_spatial_neighbors_squaregrid(self, adata_squaregrid: AnnData, n_rings: assert adata.uns[Key.uns.spatial_neighs()]["distances_key"] == Key.obsp.spatial_dist() adata_concat = self._assert_library_key_block_diagonal( - adata, n_neighs=n_neigh, n_rings=n_rings, coord_type="grid", + adata, + n_neighs=n_neigh, + n_rings=n_rings, + coord_type="grid", ) assert np.diff(adata_concat.obsp[Key.obsp.spatial_conn()].indptr).max() == sum_neigh @@ -202,9 +205,7 @@ def test_builder_module_export(self): ], ids=["knn", "radius", "delaunay"], ) - def test_generic_builder_matches_legacy( - self, non_visium_adata: AnnData, legacy_kwargs: dict, builder: object - ): + def test_generic_builder_matches_legacy(self, non_visium_adata: AnnData, legacy_kwargs: dict, builder: object): legacy = spatial_neighbors(non_visium_adata, **legacy_kwargs, copy=True) result = spatial_neighbors(non_visium_adata, builder=builder, copy=True) @@ -219,9 +220,7 @@ def test_generic_builder_matches_legacy( ], ids=["4neighs_2rings", "6neighs_1ring"], ) - def test_grid_builder_matches_legacy( - self, adata_squaregrid: AnnData, legacy_kwargs: dict, builder: object - ): + def test_grid_builder_matches_legacy(self, adata_squaregrid: AnnData, legacy_kwargs: dict, builder: object): legacy = spatial_neighbors(adata_squaregrid, **legacy_kwargs, copy=True) result = spatial_neighbors(adata_squaregrid, builder=builder, copy=True) From fddc027d835485f3e5629b31a3be4e40c0df03bc Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 13:53:06 +0200 Subject: [PATCH 007/131] arrange builders --- src/squidpy/gr/_build.py | 5 ----- tests/graph/test_spatial_neighbors.py | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index d2602cfe1..d4506b8a8 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -52,11 +52,6 @@ __all__ = [ "SpatialNeighborsResult", - "GraphBuilder", - "KNNBuilder", - "RadiusBuilder", - "DelaunayBuilder", - "GridBuilder", "spatial_neighbors", ] diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index b99c7c751..94b70f387 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -12,8 +12,8 @@ from squidpy._constants._constants import Transform from squidpy._constants._pkg_constants import Key -from squidpy.gr import DelaunayBuilder, GridBuilder, KNNBuilder, RadiusBuilder, mask_graph, spatial_neighbors -from squidpy.gr._build import _build_connectivity +from squidpy.gr import mask_graph, spatial_neighbors +from squidpy.gr._build import DelaunayBuilder, GridBuilder, KNNBuilder, RadiusBuilder, _build_connectivity from squidpy.gr.neighbors import KNNBuilder as PublicKNNBuilder From f194b1081a9dcaabc8acf63f5be3c93c2444138a Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:09:15 +0200 Subject: [PATCH 008/131] better behaviour --- src/squidpy/gr/_build.py | 105 +++++++++----------------- tests/graph/test_spatial_neighbors.py | 53 ++++++------- 2 files changed, 57 insertions(+), 101 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index d4506b8a8..140e06b26 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -313,59 +313,13 @@ def _filter_by_radius_interval( Adj.setdiag(a_diag) -def _normalize_builder_param(name: str, value: Any) -> Any: - if name == "coord_type": - return None if value is None else CoordType(value) - if name == "transform": - return Transform.NONE if value is None else Transform(value) - if name == "radius" and isinstance(value, tuple): - return tuple(sorted(value)[:2]) - - return value - - -def _validate_builder_compatibility( - builder: GraphBuilder, - *, - coord_type: str | CoordType | None, - n_neighs: int, - radius: float | tuple[float, float] | None, - delaunay: bool, - n_rings: int, - percentile: float | None, - transform: str | Transform | None, - set_diag: bool, -) -> None: - defaults = { - "coord_type": None, - "n_neighs": 6, - "radius": None, - "delaunay": False, - "n_rings": 1, - "percentile": None, - "transform": None, - "set_diag": False, - } - provided = { - "coord_type": coord_type, - "n_neighs": n_neighs, - "radius": radius, - "delaunay": delaunay, - "n_rings": n_rings, - "percentile": percentile, - "transform": transform, - "set_diag": set_diag, - } - - for key, value in provided.items(): - if value == defaults[key]: - continue - - if _normalize_builder_param(key, value) != _normalize_builder_param(key, builder.legacy_params[key]): - raise ValueError( - f"Parameter `{key}` conflicts with `{type(builder).__name__}`. " - "Leave graph-construction arguments at their defaults or make them match the builder." - ) +def _validate_no_legacy_params(**kwargs: Any) -> None: + conflicts = [k for k, v in kwargs.items() if v is not None] + if conflicts: + raise ValueError( + "When `builder` is provided, graph-construction arguments must not be set. " + f"Got non-default values for: {', '.join(conflicts)}." + ) def _resolve_graph_builder( @@ -425,13 +379,13 @@ def spatial_neighbors( table_key: str | None = None, library_key: str | None = None, coord_type: str | CoordType | None = None, - n_neighs: int = 6, + n_neighs: int | None = None, radius: float | tuple[float, float] | None = None, - delaunay: bool = False, - n_rings: int = 1, + delaunay: bool | None = None, + n_rings: int | None = None, percentile: float | None = None, transform: str | Transform | None = None, - set_diag: bool = False, + set_diag: bool | None = None, builder: GraphBuilder | None = None, key_added: str = "spatial", copy: bool = False, @@ -452,11 +406,10 @@ def spatial_neighbors( table_key Key in :attr:`spatialdata.SpatialData.tables` where the spatialdata table is stored. Must not be `None` if `adata` is a :class:`spatialdata.SpatialData`. - mask_polygon - The Polygon or MultiPolygon element. %(library_key)s coord_type - Type of coordinate system. Valid options are: + Type of coordinate system. Must not be set when ``builder`` is given. + Valid options are: - `{c.GRID.s!r}` - grid coordinates. - `{c.GENERIC.s!r}` - generic coordinates. @@ -467,29 +420,37 @@ def spatial_neighbors( - `{c.GRID.s!r}` - number of neighboring tiles. - `{c.GENERIC.s!r}` - number of neighborhoods for non-grid data. Only used when ``delaunay = False``. + + Defaults to ``6`` when no ``builder`` is provided. Must not be set when ``builder`` is given. radius - Only available when ``coord_type = {c.GENERIC.s!r}``. Depending on the type: + Only available when ``coord_type = {c.GENERIC.s!r}``. Must not be set when ``builder`` is given. + Depending on the type: - :class:`float` - compute the graph based on neighborhood radius. - :class:`tuple` - prune the final graph to only contain edges in interval `[min(radius), max(radius)]`. delaunay Whether to compute the graph from Delaunay triangulation. Only used when ``coord_type = {c.GENERIC.s!r}``. + Defaults to ``False`` when no ``builder`` is provided. Must not be set when ``builder`` is given. n_rings Number of rings of neighbors for grid data. Only used when ``coord_type = {c.GRID.s!r}``. + Defaults to ``1`` when no ``builder`` is provided. Must not be set when ``builder`` is given. percentile Percentile of the distances to use as threshold. Only used when ``coord_type = {c.GENERIC.s!r}``. + Must not be set when ``builder`` is given. transform - Type of adjacency matrix transform. Valid options are: + Type of adjacency matrix transform. Must not be set when ``builder`` is given. + Valid options are: - `{t.SPECTRAL.s!r}` - spectral transformation of the adjacency matrix. - `{t.COSINE.s!r}` - cosine transformation of the adjacency matrix. - `{t.NONE.v}` - no transformation of the adjacency matrix. set_diag Whether to set the diagonal of the spatial connectivities to `1.0`. + Defaults to ``False`` when no ``builder`` is provided. Must not be set when ``builder`` is given. builder - Advanced graph construction strategy. When provided, graph-construction arguments - (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, ``percentile``, - ``transform``, ``set_diag``) must either be left at their defaults or match the builder. + Advanced graph construction strategy. When provided, all other graph-construction + arguments (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, + ``percentile``, ``transform``, ``set_diag``) must be left as ``None``. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s @@ -524,8 +485,8 @@ def spatial_neighbors( - ``percentile`` only affects generic graphs. - ``transform`` and ``set_diag`` apply to all modes. - If ``builder`` is provided, it determines the mode directly. - The legacy graph-construction arguments must then stay at - their defaults or match the builder configuration. + All other graph-construction arguments must be left as + ``None``. - By default, observations are not treated as their own neighbors. The distance matrix always has a zero diagonal. The connectivity matrix only gets a nonzero diagonal when @@ -622,10 +583,7 @@ def spatial_neighbors( _assert_spatial_basis(adata, spatial_key) if builder is not None: - if not isinstance(builder, GraphBuilder): - raise TypeError(f"Expected `builder` to be a `GraphBuilder`, found `{type(builder)}`.") - _validate_builder_compatibility( - builder, + _validate_no_legacy_params( coord_type=coord_type, n_neighs=n_neighs, radius=radius, @@ -636,6 +594,11 @@ def spatial_neighbors( set_diag=set_diag, ) else: + n_neighs = n_neighs if n_neighs is not None else 6 + delaunay = delaunay if delaunay is not None else False + n_rings = n_rings if n_rings is not None else 1 + set_diag = set_diag if set_diag is not None else False + assert_positive(n_rings, name="n_rings") assert_positive(n_neighs, name="n_neighs") diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 851a81ced..6908a36c2 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -228,29 +228,26 @@ def test_grid_builder_matches_legacy(self, adata_squaregrid: AnnData, legacy_kwa np.testing.assert_allclose(legacy.distances.toarray(), result.distances.toarray()) def test_builder_argument_conflict(self, non_visium_adata: AnnData): - with pytest.raises(ValueError, match="conflicts"): + with pytest.raises(ValueError, match="must not be set"): spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) - def test_builder_matching_non_default_legacy_args(self, non_visium_adata: AnnData): + def test_builder_rejects_any_legacy_args(self, non_visium_adata: AnnData): builder = RadiusBuilder(radius=5.0, n_neighs=3, percentile=95.0, transform="cosine", set_diag=True) - baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) - matched = spatial_neighbors( - non_visium_adata, - builder=builder, - coord_type="generic", - n_neighs=3, - radius=5.0, - percentile=95.0, - transform=Transform.COSINE, - set_diag=True, - copy=True, - ) - - np.testing.assert_allclose(baseline.connectivities.toarray(), matched.connectivities.toarray()) - np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) + with pytest.raises(ValueError, match="must not be set"): + spatial_neighbors( + non_visium_adata, + builder=builder, + coord_type="generic", + n_neighs=3, + radius=5.0, + percentile=95.0, + transform="cosine", + set_diag=True, + copy=True, + ) - def test_builder_compatibility_normalizes_none_transform(self, non_visium_adata: AnnData): + def test_builder_allows_none_legacy_args(self, non_visium_adata: AnnData): builder = KNNBuilder(n_neighs=3, transform=Transform.NONE) baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) @@ -259,20 +256,16 @@ def test_builder_compatibility_normalizes_none_transform(self, non_visium_adata: np.testing.assert_array_equal(baseline.connectivities.toarray(), matched.connectivities.toarray()) np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) - def test_builder_compatibility_normalizes_tuple_radius_order(self, non_visium_adata: AnnData): + def test_builder_rejects_radius(self, non_visium_adata: AnnData): builder = RadiusBuilder(radius=(4.0, 2.0)) - baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) - matched = spatial_neighbors( - non_visium_adata, - builder=builder, - coord_type="generic", - radius=(2.0, 4.0), - copy=True, - ) - - np.testing.assert_array_equal(baseline.connectivities.toarray(), matched.connectivities.toarray()) - np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) + with pytest.raises(ValueError, match="must not be set"): + spatial_neighbors( + non_visium_adata, + builder=builder, + radius=(2.0, 4.0), + copy=True, + ) def test_grid_mode_ignores_radius(self, adata_squaregrid: AnnData): default = spatial_neighbors(adata_squaregrid, coord_type="grid", n_neighs=4, n_rings=2, copy=True) From ae1eba1358d4dc866fb447fac81eeb149c5b44a5 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:22:20 +0200 Subject: [PATCH 009/131] move classes --- src/squidpy/gr/_build.py | 445 +------------------------ src/squidpy/gr/neighbors.py | 456 +++++++++++++++++++++++++- tests/graph/test_spatial_neighbors.py | 12 +- 3 files changed, 472 insertions(+), 441 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 140e06b26..1c54fda49 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -2,11 +2,7 @@ from __future__ import annotations -import warnings -from abc import ABC, abstractmethod -from collections.abc import Iterable from functools import partial -from itertools import chain from typing import Any, NamedTuple, cast import geopandas as gpd @@ -14,20 +10,13 @@ import pandas as pd from anndata import AnnData from anndata.utils import make_index_unique -from fast_array_utils import stats as fau_stats -from numba import njit, prange +from numba import njit from scipy.sparse import ( - SparseEfficiencyWarning, block_diag, - csr_array, csr_matrix, - isspmatrix_csr, spmatrix, ) -from scipy.spatial import Delaunay from shapely import LineString, MultiPolygon, Polygon -from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances -from sklearn.neighbors import NearestNeighbors from spatialdata import SpatialData from spatialdata._core.centroids import get_centroids from spatialdata._core.query.relational_query import get_element_instances, match_element_to_table @@ -49,6 +38,13 @@ _assert_spatial_basis, _save_data, ) +from squidpy.gr.neighbors import ( + DelaunayBuilder, + GraphBuilder, + GridBuilder, + KNNBuilder, + RadiusBuilder, +) __all__ = [ "SpatialNeighborsResult", @@ -63,256 +59,6 @@ class SpatialNeighborsResult(NamedTuple): distances: csr_matrix -class GraphBuilder(ABC): - """Base class for spatial graph construction strategies.""" - - def __init__( - self, - transform: str | Transform | None = None, - set_diag: bool = False, - percentile: float | None = None, - ) -> None: - self.transform = Transform.NONE if transform is None else Transform(transform) - self.set_diag = set_diag - self.percentile = percentile - - @property - @abstractmethod - def coord_type(self) -> CoordType: - """Coordinate system supported by this builder.""" - - @property - def legacy_params(self) -> dict[str, Any]: - """Return parameters expressed in the legacy spatial_neighbors API.""" - return { - "coord_type": self.coord_type, - "n_neighs": 6, - "radius": None, - "delaunay": False, - "n_rings": 1, - "percentile": self.percentile, - "transform": self.transform, - "set_diag": self.set_diag, - } - - @property - def metadata(self) -> dict[str, Any]: - """Return metadata stored in adata.uns after graph construction.""" - params = self.legacy_params - return { - "n_neighbors": params["n_neighs"], - "coord_type": self.coord_type.v, - "radius": params["radius"], - "transform": self.transform.v, - } - - def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", SparseEfficiencyWarning) - Adj, Dst = self._build_graph(coords) - - self._apply_filters(Adj, Dst) - self._apply_percentile(Adj, Dst) - Adj.eliminate_zeros() - Dst.eliminate_zeros() - - return self._apply_transform(Adj), Dst - - @abstractmethod - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - """Construct raw adjacency and distance matrices.""" - - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - """Apply builder-specific post-processing filters.""" - return None - - def _apply_percentile(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - if self.percentile is not None and self.coord_type == CoordType.GENERIC: - threshold = np.percentile(Dst.data, self.percentile) - Adj[Dst > threshold] = 0.0 - Dst[Dst > threshold] = 0.0 - - def _apply_transform(self, Adj: csr_matrix) -> csr_matrix: - if self.transform == Transform.SPECTRAL: - return cast(csr_matrix, _transform_a_spectral(Adj)) - if self.transform == Transform.COSINE: - return cast(csr_matrix, _transform_a_cosine(Adj)) - if self.transform == Transform.NONE: - return Adj - - raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") - - -class KNNBuilder(GraphBuilder): - """Build a generic k-nearest-neighbor spatial graph.""" - - def __init__( - self, - n_neighs: int = 6, - transform: str | Transform | None = None, - set_diag: bool = False, - percentile: float | None = None, - ) -> None: - assert_positive(n_neighs, name="n_neighs") - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) - self.n_neighs = n_neighs - - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs} - - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - return_distance=True, - set_diag=self.set_diag, - ), - ) - - -class _RadiusFilterBuilder(GraphBuilder): - """Intermediate base for builders that support radius-interval pruning.""" - - radius: float | tuple[float, float] | None - n_neighs: int - - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) - - -class RadiusBuilder(_RadiusFilterBuilder): - """Build a generic radius-based spatial graph.""" - - def __init__( - self, - radius: float | tuple[float, float], - n_neighs: int = 6, - transform: str | Transform | None = None, - set_diag: bool = False, - percentile: float | None = None, - ) -> None: - assert_positive(n_neighs, name="n_neighs") - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) - self.radius = radius - self.n_neighs = n_neighs - - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius} - - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - radius=self.radius, - return_distance=True, - set_diag=self.set_diag, - ), - ) - - -class DelaunayBuilder(_RadiusFilterBuilder): - """Build a generic spatial graph from a Delaunay triangulation.""" - - def __init__( - self, - radius: float | tuple[float, float] | None = None, - n_neighs: int = 6, - transform: str | Transform | None = None, - set_diag: bool = False, - percentile: float | None = None, - ) -> None: - assert_positive(n_neighs, name="n_neighs") - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) - self.radius = radius - self.n_neighs = n_neighs - - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius, "delaunay": True} - - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - radius=self.radius, - delaunay=True, - return_distance=True, - set_diag=self.set_diag, - ), - ) - - -class GridBuilder(GraphBuilder): - """Build a grid-based spatial graph.""" - - def __init__( - self, - n_neighs: int = 6, - n_rings: int = 1, - delaunay: bool = False, - transform: str | Transform | None = None, - set_diag: bool = False, - ) -> None: - assert_positive(n_neighs, name="n_neighs") - assert_positive(n_rings, name="n_rings") - super().__init__(transform=transform, set_diag=set_diag, percentile=None) - self.n_neighs = n_neighs - self.n_rings = n_rings - self.delaunay = delaunay - - @property - def coord_type(self) -> CoordType: - return CoordType.GRID - - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | { - "n_neighs": self.n_neighs, - "delaunay": self.delaunay, - "n_rings": self.n_rings, - } - - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return _build_grid( - coords, - n_neighs=self.n_neighs, - n_rings=self.n_rings, - delaunay=self.delaunay, - set_diag=self.set_diag, - ) - - -def _filter_by_radius_interval( - Adj: csr_matrix, - Dst: csr_matrix, - radius: Iterable[float], -) -> None: - minn, maxx = sorted(radius)[:2] - mask = (Dst.data < minn) | (Dst.data > maxx) - a_diag = Adj.diagonal() - - Dst.data[mask] = 0.0 - Adj.data[mask] = 0.0 - Adj.setdiag(a_diag) - - def _validate_no_legacy_params(**kwargs: Any) -> None: conflicts = [k for k, v in kwargs.items() if v is not None] if conflicts: @@ -682,181 +428,6 @@ def _spatial_neighbor( return builder.build(coords) -def _build_grid( - coords: NDArrayA, - n_neighs: int, - n_rings: int, - delaunay: bool = False, - set_diag: bool = False, -) -> tuple[csr_matrix, csr_matrix]: - if n_rings > 1: - Adj: csr_matrix = _build_connectivity( - coords, - n_neighs=n_neighs, - neigh_correct=True, - set_diag=True, - delaunay=delaunay, - return_distance=False, - ) - Res, Walk = Adj, Adj - for i in range(n_rings - 1): - Walk = Walk @ Adj - Walk[Res.nonzero()] = 0.0 - Walk.eliminate_zeros() - Walk.data[:] = i + 2.0 - Res = Res + Walk - Adj = Res - Adj.setdiag(float(set_diag)) - Adj.eliminate_zeros() - - Dst = Adj.copy() - Adj.data[:] = 1.0 - else: - Adj = _build_connectivity( - coords, - n_neighs=n_neighs, - neigh_correct=True, - delaunay=delaunay, - set_diag=set_diag, - ) - Dst = Adj.copy() - - Dst.setdiag(0.0) - - return Adj, Dst - - -def _build_connectivity( - coords: NDArrayA, - n_neighs: int, - radius: float | tuple[float, float] | None = None, - delaunay: bool = False, - neigh_correct: bool = False, - set_diag: bool = False, - return_distance: bool = False, -) -> csr_matrix | tuple[csr_matrix, csr_matrix]: - N = coords.shape[0] - if delaunay: - tri = Delaunay(coords) - indptr, indices = tri.vertex_neighbor_vertices - Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) - - if return_distance: - # fmt: off - dists = np.array(list(chain(*( - euclidean_distances(coords[indices[indptr[i] : indptr[i + 1]], :], coords[np.newaxis, i, :]) - for i in range(N) - if len(indices[indptr[i] : indptr[i + 1]]) - )))).squeeze() - Dst = csr_matrix((dists, indices, indptr), shape=(N, N)) - # fmt: on - else: - r = 1 if radius is None else radius if isinstance(radius, int | float) else max(radius) - tree = NearestNeighbors(n_neighbors=n_neighs, radius=r, metric="euclidean") - tree.fit(coords) - - if radius is None: - dists, col_indices = tree.kneighbors() - dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) - row_indices = np.repeat(np.arange(N), n_neighs) - if neigh_correct: - dist_cutoff = np.median(dists) * 1.3 # there's a small amount of sway - mask = dists < dist_cutoff - row_indices, col_indices, dists = ( - row_indices[mask], - col_indices[mask], - dists[mask], - ) - else: - dists, col_indices = tree.radius_neighbors() - row_indices = np.repeat(np.arange(N), [len(x) for x in col_indices]) - dists = np.concatenate(dists) - col_indices = np.concatenate(col_indices) - - Adj = csr_matrix( - (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), - shape=(N, N), - ) - if return_distance: - Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) - - # radius-based filtering needs same indices/indptr: do not remove 0s - Adj.setdiag(1.0 if set_diag else Adj.diagonal()) - if return_distance: - Dst.setdiag(0.0) - return Adj, Dst - - return Adj - - -@njit -def _csr_bilateral_diag_scale_helper( - mat: csr_array | csr_matrix, - degrees: NDArrayA, -) -> NDArrayA: - """ - Return an array F aligned with CSR non-zeros such that - F[k] = d[i] * data[k] * d[j] for the k-th non-zero (i, j) in CSR order. - - Parameters - ---------- - - data : array of float - CSR `data` (non-zero values). - indices : array of int - CSR `indices` (column indices). - indptr : array of int - CSR `indptr` (row pointer). - degrees : array of float, shape (n,) - Diagonal scaling vector. - - Returns - ------- - array of float - Length equals len(data). Entry-wise factors d_i * d_j * data[k] - """ - - res = np.empty_like(mat.data, dtype=np.float32) - for i in prange(len(mat.indptr) - 1): - ixs = mat.indices[mat.indptr[i] : mat.indptr[i + 1]] - res[mat.indptr[i] : mat.indptr[i + 1]] = degrees[i] * degrees[ixs] * mat.data[mat.indptr[i] : mat.indptr[i + 1]] - - return res - - -def symmetric_normalize_csr(adj: spmatrix) -> csr_matrix: - """ - Return D^{-1/2} * A * D^{-1/2}, where D = diag(degrees(A)) and A = adj. - - - Parameters - ---------- - adj : scipy.sparse.csr_matrix - - Returns - ------- - scipy.sparse.csr_matrix - """ - degrees = np.squeeze(np.array(np.sqrt(1.0 / fau_stats.sum(adj, axis=0)))) - if adj.shape[0] != len(degrees): - raise ValueError("len(degrees) must equal number of rows of adj") - res_data = _csr_bilateral_diag_scale_helper(adj, degrees) - return csr_matrix((res_data, adj.indices, adj.indptr), shape=adj.shape) - - -def _transform_a_spectral(a: spmatrix) -> spmatrix: - if not isspmatrix_csr(a): - a = a.tocsr() - if not a.nnz: - return a - - return symmetric_normalize_csr(a) - - -def _transform_a_cosine(a: spmatrix) -> spmatrix: - return cosine_similarity(a, dense_output=False) - - @d.dedent def mask_graph( sdata: SpatialData, diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 3e9d1d14c..79cf62195 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -1,7 +1,459 @@ -"""Public builder API for spatial neighbor graph construction.""" +"""Builder classes and helpers for spatial neighbor graph construction.""" from __future__ import annotations -from squidpy.gr._build import DelaunayBuilder, GraphBuilder, GridBuilder, KNNBuilder, RadiusBuilder +import warnings +from abc import ABC, abstractmethod +from collections.abc import Iterable +from itertools import chain +from typing import Any, cast + +import numpy as np +from fast_array_utils import stats as fau_stats +from numba import njit, prange +from scipy.sparse import ( + SparseEfficiencyWarning, + csr_array, + csr_matrix, + isspmatrix_csr, + spmatrix, +) +from scipy.spatial import Delaunay +from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances +from sklearn.neighbors import NearestNeighbors + +from squidpy._constants._constants import CoordType, Transform +from squidpy._utils import NDArrayA +from squidpy._validators import assert_positive __all__ = ["GraphBuilder", "KNNBuilder", "RadiusBuilder", "DelaunayBuilder", "GridBuilder"] + + +class GraphBuilder(ABC): + """Base class for spatial graph construction strategies.""" + + def __init__( + self, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + self.transform = Transform.NONE if transform is None else Transform(transform) + self.set_diag = set_diag + self.percentile = percentile + + @property + @abstractmethod + def coord_type(self) -> CoordType: + """Coordinate system supported by this builder.""" + + @property + def legacy_params(self) -> dict[str, Any]: + """Return parameters expressed in the legacy spatial_neighbors API.""" + return { + "coord_type": self.coord_type, + "n_neighs": 6, + "radius": None, + "delaunay": False, + "n_rings": 1, + "percentile": self.percentile, + "transform": self.transform, + "set_diag": self.set_diag, + } + + @property + def metadata(self) -> dict[str, Any]: + """Return metadata stored in adata.uns after graph construction.""" + params = self.legacy_params + return { + "n_neighbors": params["n_neighs"], + "coord_type": self.coord_type.v, + "radius": params["radius"], + "transform": self.transform.v, + } + + def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", SparseEfficiencyWarning) + Adj, Dst = self._build_graph(coords) + + self._apply_filters(Adj, Dst) + self._apply_percentile(Adj, Dst) + Adj.eliminate_zeros() + Dst.eliminate_zeros() + + return self._apply_transform(Adj), Dst + + @abstractmethod + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + """Construct raw adjacency and distance matrices.""" + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + """Apply builder-specific post-processing filters.""" + return None + + def _apply_percentile(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if self.percentile is not None and self.coord_type == CoordType.GENERIC: + threshold = np.percentile(Dst.data, self.percentile) + Adj[Dst > threshold] = 0.0 + Dst[Dst > threshold] = 0.0 + + def _apply_transform(self, Adj: csr_matrix) -> csr_matrix: + if self.transform == Transform.SPECTRAL: + return cast(csr_matrix, _transform_a_spectral(Adj)) + if self.transform == Transform.COSINE: + return cast(csr_matrix, _transform_a_cosine(Adj)) + if self.transform == Transform.NONE: + return Adj + + raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") + + +class KNNBuilder(GraphBuilder): + """Build a generic k-nearest-neighbor spatial graph.""" + + def __init__( + self, + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.n_neighs = n_neighs + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + +class _RadiusFilterBuilder(GraphBuilder): + """Intermediate base for builders that support radius-interval pruning.""" + + radius: float | tuple[float, float] | None + n_neighs: int + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) + + +class RadiusBuilder(_RadiusFilterBuilder): + """Build a generic radius-based spatial graph.""" + + def __init__( + self, + radius: float | tuple[float, float], + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.radius = radius + self.n_neighs = n_neighs + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + radius=self.radius, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + +class DelaunayBuilder(_RadiusFilterBuilder): + """Build a generic spatial graph from a Delaunay triangulation.""" + + def __init__( + self, + radius: float | tuple[float, float] | None = None, + n_neighs: int = 6, + transform: str | Transform | None = None, + set_diag: bool = False, + percentile: float | None = None, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + self.radius = radius + self.n_neighs = n_neighs + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius, "delaunay": True} + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return cast( + tuple[csr_matrix, csr_matrix], + _build_connectivity( + coords, + n_neighs=self.n_neighs, + radius=self.radius, + delaunay=True, + return_distance=True, + set_diag=self.set_diag, + ), + ) + + +class GridBuilder(GraphBuilder): + """Build a grid-based spatial graph.""" + + def __init__( + self, + n_neighs: int = 6, + n_rings: int = 1, + delaunay: bool = False, + transform: str | Transform | None = None, + set_diag: bool = False, + ) -> None: + assert_positive(n_neighs, name="n_neighs") + assert_positive(n_rings, name="n_rings") + super().__init__(transform=transform, set_diag=set_diag, percentile=None) + self.n_neighs = n_neighs + self.n_rings = n_rings + self.delaunay = delaunay + + @property + def coord_type(self) -> CoordType: + return CoordType.GRID + + @property + def legacy_params(self) -> dict[str, Any]: + return super().legacy_params | { + "n_neighs": self.n_neighs, + "delaunay": self.delaunay, + "n_rings": self.n_rings, + } + + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + return _build_grid( + coords, + n_neighs=self.n_neighs, + n_rings=self.n_rings, + delaunay=self.delaunay, + set_diag=self.set_diag, + ) + + +# --------------------------------------------------------------------------- +# Private helpers used by the builder classes +# --------------------------------------------------------------------------- + + +def _filter_by_radius_interval( + Adj: csr_matrix, + Dst: csr_matrix, + radius: Iterable[float], +) -> None: + minn, maxx = sorted(radius)[:2] + mask = (Dst.data < minn) | (Dst.data > maxx) + a_diag = Adj.diagonal() + + Dst.data[mask] = 0.0 + Adj.data[mask] = 0.0 + Adj.setdiag(a_diag) + + +def _build_grid( + coords: NDArrayA, + n_neighs: int, + n_rings: int, + delaunay: bool = False, + set_diag: bool = False, +) -> tuple[csr_matrix, csr_matrix]: + if n_rings > 1: + Adj: csr_matrix = _build_connectivity( + coords, + n_neighs=n_neighs, + neigh_correct=True, + set_diag=True, + delaunay=delaunay, + return_distance=False, + ) + Res, Walk = Adj, Adj + for i in range(n_rings - 1): + Walk = Walk @ Adj + Walk[Res.nonzero()] = 0.0 + Walk.eliminate_zeros() + Walk.data[:] = i + 2.0 + Res = Res + Walk + Adj = Res + Adj.setdiag(float(set_diag)) + Adj.eliminate_zeros() + + Dst = Adj.copy() + Adj.data[:] = 1.0 + else: + Adj = _build_connectivity( + coords, + n_neighs=n_neighs, + neigh_correct=True, + delaunay=delaunay, + set_diag=set_diag, + ) + Dst = Adj.copy() + + Dst.setdiag(0.0) + + return Adj, Dst + + +def _build_connectivity( + coords: NDArrayA, + n_neighs: int, + radius: float | tuple[float, float] | None = None, + delaunay: bool = False, + neigh_correct: bool = False, + set_diag: bool = False, + return_distance: bool = False, +) -> csr_matrix | tuple[csr_matrix, csr_matrix]: + N = coords.shape[0] + if delaunay: + tri = Delaunay(coords) + indptr, indices = tri.vertex_neighbor_vertices + Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) + + if return_distance: + # fmt: off + dists = np.array(list(chain(*( + euclidean_distances(coords[indices[indptr[i] : indptr[i + 1]], :], coords[np.newaxis, i, :]) + for i in range(N) + if len(indices[indptr[i] : indptr[i + 1]]) + )))).squeeze() + Dst = csr_matrix((dists, indices, indptr), shape=(N, N)) + # fmt: on + else: + r = 1 if radius is None else radius if isinstance(radius, int | float) else max(radius) + tree = NearestNeighbors(n_neighbors=n_neighs, radius=r, metric="euclidean") + tree.fit(coords) + + if radius is None: + dists, col_indices = tree.kneighbors() + dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) + row_indices = np.repeat(np.arange(N), n_neighs) + if neigh_correct: + dist_cutoff = np.median(dists) * 1.3 # there's a small amount of sway + mask = dists < dist_cutoff + row_indices, col_indices, dists = ( + row_indices[mask], + col_indices[mask], + dists[mask], + ) + else: + dists, col_indices = tree.radius_neighbors() + row_indices = np.repeat(np.arange(N), [len(x) for x in col_indices]) + dists = np.concatenate(dists) + col_indices = np.concatenate(col_indices) + + Adj = csr_matrix( + (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), + shape=(N, N), + ) + if return_distance: + Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) + + # radius-based filtering needs same indices/indptr: do not remove 0s + Adj.setdiag(1.0 if set_diag else Adj.diagonal()) + if return_distance: + Dst.setdiag(0.0) + return Adj, Dst + + return Adj + + +@njit +def _csr_bilateral_diag_scale_helper( + mat: csr_array | csr_matrix, + degrees: NDArrayA, +) -> NDArrayA: + """ + Return an array F aligned with CSR non-zeros such that + F[k] = d[i] * data[k] * d[j] for the k-th non-zero (i, j) in CSR order. + + Parameters + ---------- + + data : array of float + CSR `data` (non-zero values). + indices : array of int + CSR `indices` (column indices). + indptr : array of int + CSR `indptr` (row pointer). + degrees : array of float, shape (n,) + Diagonal scaling vector. + + Returns + ------- + array of float + Length equals len(data). Entry-wise factors d_i * d_j * data[k] + """ + + res = np.empty_like(mat.data, dtype=np.float32) + for i in prange(len(mat.indptr) - 1): + ixs = mat.indices[mat.indptr[i] : mat.indptr[i + 1]] + res[mat.indptr[i] : mat.indptr[i + 1]] = degrees[i] * degrees[ixs] * mat.data[mat.indptr[i] : mat.indptr[i + 1]] + + return res + + +def symmetric_normalize_csr(adj: spmatrix) -> csr_matrix: + """ + Return D^{-1/2} * A * D^{-1/2}, where D = diag(degrees(A)) and A = adj. + + + Parameters + ---------- + adj : scipy.sparse.csr_matrix + + Returns + ------- + scipy.sparse.csr_matrix + """ + degrees = np.squeeze(np.array(np.sqrt(1.0 / fau_stats.sum(adj, axis=0)))) + if adj.shape[0] != len(degrees): + raise ValueError("len(degrees) must equal number of rows of adj") + res_data = _csr_bilateral_diag_scale_helper(adj, degrees) + return csr_matrix((res_data, adj.indices, adj.indptr), shape=adj.shape) + + +def _transform_a_spectral(a: spmatrix) -> spmatrix: + if not isspmatrix_csr(a): + a = a.tocsr() + if not a.nnz: + return a + + return symmetric_normalize_csr(a) + + +def _transform_a_cosine(a: spmatrix) -> spmatrix: + return cosine_similarity(a, dense_output=False) diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 6908a36c2..8cf32c2c5 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -13,8 +13,16 @@ from squidpy._constants._constants import Transform from squidpy._constants._pkg_constants import Key from squidpy.gr import mask_graph, spatial_neighbors -from squidpy.gr._build import DelaunayBuilder, GridBuilder, KNNBuilder, RadiusBuilder, _build_connectivity -from squidpy.gr.neighbors import KNNBuilder as PublicKNNBuilder +from squidpy.gr.neighbors import ( + DelaunayBuilder, + GridBuilder, + KNNBuilder, + RadiusBuilder, + _build_connectivity, +) +from squidpy.gr.neighbors import ( + KNNBuilder as PublicKNNBuilder, +) class TestSpatialNeighbors: From 039b8527d857ce3816aba661db7e2dd1d735d4a6 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:29:01 +0200 Subject: [PATCH 010/131] cleanup --- src/squidpy/gr/__init__.py | 2 ++ src/squidpy/gr/_build.py | 7 +++++- src/squidpy/gr/neighbors.py | 47 +------------------------------------ 3 files changed, 9 insertions(+), 47 deletions(-) diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index d4f39958e..6053b2743 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -7,6 +7,7 @@ mask_graph, spatial_neighbors, ) +from squidpy.gr import neighbors from squidpy.gr._ligrec import ligrec from squidpy.gr._nhood import ( NhoodEnrichmentResult, @@ -22,6 +23,7 @@ __all__ = [ "SpatialNeighborsResult", "NhoodEnrichmentResult", + "neighbors", "mask_graph", "spatial_neighbors", "ligrec", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 1c54fda49..5bfbb1ceb 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -405,7 +405,12 @@ def spatial_neighbors( neighbors_dict = { "connectivities_key": conns_key, "distances_key": dists_key, - "params": builder.metadata, + "params": { + "n_neighbors": getattr(builder, "n_neighs", 6), + "coord_type": builder.coord_type.v, + "radius": getattr(builder, "radius", None), + "transform": builder.transform.v, + }, } if copy: diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 79cf62195..00a630168 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -6,7 +6,7 @@ from abc import ABC, abstractmethod from collections.abc import Iterable from itertools import chain -from typing import Any, cast +from typing import cast import numpy as np from fast_array_utils import stats as fau_stats @@ -47,31 +47,6 @@ def __init__( def coord_type(self) -> CoordType: """Coordinate system supported by this builder.""" - @property - def legacy_params(self) -> dict[str, Any]: - """Return parameters expressed in the legacy spatial_neighbors API.""" - return { - "coord_type": self.coord_type, - "n_neighs": 6, - "radius": None, - "delaunay": False, - "n_rings": 1, - "percentile": self.percentile, - "transform": self.transform, - "set_diag": self.set_diag, - } - - @property - def metadata(self) -> dict[str, Any]: - """Return metadata stored in adata.uns after graph construction.""" - params = self.legacy_params - return { - "n_neighbors": params["n_neighs"], - "coord_type": self.coord_type.v, - "radius": params["radius"], - "transform": self.transform.v, - } - def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: with warnings.catch_warnings(): warnings.simplefilter("ignore", SparseEfficiencyWarning) @@ -127,10 +102,6 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GENERIC - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs} - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return cast( tuple[csr_matrix, csr_matrix], @@ -174,10 +145,6 @@ def __init__( self.radius = radius self.n_neighs = n_neighs - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius} - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return cast( tuple[csr_matrix, csr_matrix], @@ -207,10 +174,6 @@ def __init__( self.radius = radius self.n_neighs = n_neighs - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | {"n_neighs": self.n_neighs, "radius": self.radius, "delaunay": True} - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return cast( tuple[csr_matrix, csr_matrix], @@ -247,14 +210,6 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GRID - @property - def legacy_params(self) -> dict[str, Any]: - return super().legacy_params | { - "n_neighs": self.n_neighs, - "delaunay": self.delaunay, - "n_rings": self.n_rings, - } - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return _build_grid( coords, From b5dcf9f59a7bf4086a021b2d36174dd0ce86d5da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:29:39 +0000 Subject: [PATCH 011/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index 6053b2743..a64930622 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -2,12 +2,12 @@ from __future__ import annotations +from squidpy.gr import neighbors from squidpy.gr._build import ( SpatialNeighborsResult, mask_graph, spatial_neighbors, ) -from squidpy.gr import neighbors from squidpy.gr._ligrec import ligrec from squidpy.gr._nhood import ( NhoodEnrichmentResult, From 4c11adfd096cd4db9eb50bc7d8d5f7af67f13f03 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:36:56 +0200 Subject: [PATCH 012/131] add docs --- docs/api.md | 17 +++++++++++++++++ src/squidpy/gr/neighbors.py | 19 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index fc9922a31..59b08f632 100644 --- a/docs/api.md +++ b/docs/api.md @@ -109,3 +109,20 @@ import squidpy as sq datasets.visium_hne_image_crop datasets.visium_fluo_image_crop ``` +## Types + + + +## Extensibility +```{eval-rst} +.. module:: squidpy.gr.neighbors +.. currentmodule:: squidpy +.. autosummary:: + :toctree: api + + gr.neighbors.GraphBuilder + gr.neighbors.KNNBuilder + gr.neighbors.RadiusBuilder + gr.neighbors.DelaunayBuilder + gr.neighbors.GridBuilder +``` diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 00a630168..0c71aedc7 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -1,4 +1,21 @@ -"""Builder classes and helpers for spatial neighbor graph construction.""" +"""Graph construction strategies for :func:`squidpy.gr.spatial_neighbors`. + +This module provides the builder classes that control how spatial neighbor +graphs are constructed. :func:`~squidpy.gr.spatial_neighbors` accepts a +``builder`` argument; when supplied, the builder fully determines the +graph-construction algorithm and its parameters. + +Built-in builders cover the common cases: + +- :class:`KNNBuilder` -- k-nearest-neighbor graph. +- :class:`RadiusBuilder` -- radius-based graph. +- :class:`DelaunayBuilder` -- Delaunay triangulation graph. +- :class:`GridBuilder` -- grid (Visium-style) graph. + +To implement a custom strategy, subclass :class:`GraphBuilder` and override +:meth:`~GraphBuilder._build_graph`. The returned object can then be passed +to ``spatial_neighbors(adata, builder=my_builder)``. +""" from __future__ import annotations From ea1ce936aafb0185db05e2cd56c3484f28a55a82 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:39:10 +0200 Subject: [PATCH 013/131] remove leftover --- docs/api.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/api.md b/docs/api.md index 59b08f632..37610f75a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -109,8 +109,6 @@ import squidpy as sq datasets.visium_hne_image_crop datasets.visium_fluo_image_crop ``` -## Types - ## Extensibility From ebb7fd52cb648873c02d373a9bb10cf7f6a180ae Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:41:41 +0200 Subject: [PATCH 014/131] resolution in reolve func --- src/squidpy/gr/_build.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 5bfbb1ceb..ab611b496 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -70,15 +70,27 @@ def _validate_no_legacy_params(**kwargs: Any) -> None: def _resolve_graph_builder( *, - coord_type: CoordType, + coord_type: str | CoordType | None, n_neighs: int, radius: float | tuple[float, float] | None, delaunay: bool, n_rings: int, percentile: float | None, - transform: Transform, + transform: str | Transform | None, set_diag: bool, + has_spatial_uns: bool = False, ) -> GraphBuilder: + transform = Transform.NONE if transform is None else Transform(transform) + if coord_type is None: + if radius is not None: + logg.warning( + f"Graph creation with `radius` is only available when `coord_type = {CoordType.GENERIC!r}` specified. " + f"Ignoring parameter `radius = {radius}`." + ) + coord_type = CoordType.GRID if has_spatial_uns else CoordType.GENERIC + else: + coord_type = CoordType(coord_type) + if coord_type == CoordType.GRID: if percentile is not None: raise ValueError( @@ -348,17 +360,6 @@ def spatial_neighbors( assert_positive(n_rings, name="n_rings") assert_positive(n_neighs, name="n_neighs") - transform = Transform.NONE if transform is None else Transform(transform) - if coord_type is None: - if radius is not None: - logg.warning( - f"Graph creation with `radius` is only available when `coord_type = {CoordType.GENERIC!r}` specified. " - f"Ignoring parameter `radius = {radius}`." - ) - coord_type = CoordType.GRID if Key.uns.spatial in adata.uns else CoordType.GENERIC - else: - coord_type = CoordType(coord_type) - builder = _resolve_graph_builder( coord_type=coord_type, n_neighs=n_neighs, @@ -368,6 +369,7 @@ def spatial_neighbors( percentile=percentile, transform=transform, set_diag=set_diag, + has_spatial_uns=Key.uns.spatial in adata.uns, ) if library_key is not None: From 69a939492b0f18409d1793600560b315bb024ea6 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 14:45:03 +0200 Subject: [PATCH 015/131] reduce dup code --- src/squidpy/gr/_build.py | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index ab611b496..f61da6df5 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -91,41 +91,23 @@ def _resolve_graph_builder( else: coord_type = CoordType(coord_type) + common: dict[str, Any] = { + "n_neighs": n_neighs, + "transform": transform, + "set_diag": set_diag, + } + if coord_type == CoordType.GRID: if percentile is not None: raise ValueError( "`percentile` is not supported for grid coordinates. It only applies to generic (non-grid) graphs." ) - return GridBuilder( - n_neighs=n_neighs, - n_rings=n_rings, - delaunay=delaunay, - transform=transform, - set_diag=set_diag, - ) + return GridBuilder(**common, n_rings=n_rings, delaunay=delaunay) if delaunay: - return DelaunayBuilder( - radius=radius, - n_neighs=n_neighs, - transform=transform, - set_diag=set_diag, - percentile=percentile, - ) + return DelaunayBuilder(**common, radius=radius, percentile=percentile) if radius is not None: - return RadiusBuilder( - radius=radius, - n_neighs=n_neighs, - transform=transform, - set_diag=set_diag, - percentile=percentile, - ) - - return KNNBuilder( - n_neighs=n_neighs, - transform=transform, - set_diag=set_diag, - percentile=percentile, - ) + return RadiusBuilder(**common, radius=radius, percentile=percentile) + return KNNBuilder(**common, percentile=percentile) @d.dedent From 77755ed844266555d4c6b99bc87f8cb16ccc6b4f Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:11:53 +0200 Subject: [PATCH 016/131] deprecate invalid n_neighs --- src/squidpy/gr/_build.py | 10 +++++----- src/squidpy/gr/neighbors.py | 11 ++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index f61da6df5..d1060bef9 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -215,10 +215,10 @@ def spatial_neighbors( ``[min(radius), max(radius)]``. - Generic Delaunay mode: ``coord_type='generic'``, ``delaunay=True``. - Builds a Delaunay triangulation graph. ``n_neighs`` is not - used for the triangulation itself, but is still accepted for - backward compatibility. If ``radius`` is a tuple, it is used - only as a post-construction pruning interval. + Builds a Delaunay triangulation graph. ``n_neighs`` is + ignored by the triangulation and passing it is deprecated. + If ``radius`` is a tuple, it is used only as a + post-construction pruning interval. Across these modes: @@ -239,7 +239,7 @@ def spatial_neighbors( - If ``coord_type`` resolves to ``'grid'``, grid mode is used. In that case ``radius`` is ignored. - Otherwise, if ``delaunay=True``, Delaunay mode is used. - In this mode ``n_neighs`` does not affect the triangulation. + ``n_neighs`` is ignored (deprecated). A tuple ``radius`` is only used afterward as a pruning interval. A scalar ``radius`` is ignored. - Otherwise, if ``radius`` is set, radius mode is used. diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 0c71aedc7..c65a3c7b8 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -181,11 +181,20 @@ class DelaunayBuilder(_RadiusFilterBuilder): def __init__( self, radius: float | tuple[float, float] | None = None, - n_neighs: int = 6, + n_neighs: int | None = None, transform: str | Transform | None = None, set_diag: bool = False, percentile: float | None = None, ) -> None: + if n_neighs is not None: + warnings.warn( + "Parameter `n_neighs` of `DelaunayBuilder` is ignored by " + "Delaunay triangulation and will be removed in squidpy v2.0.0.", + FutureWarning, + stacklevel=2, + ) + else: + n_neighs = 6 assert_positive(n_neighs, name="n_neighs") super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.radius = radius From d8a81451663227aa5d8db362209474ebd551f2ee Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:28:37 +0200 Subject: [PATCH 017/131] expose new functions and deprecate the flat one --- docs/api.md | 4 + src/squidpy/_docs.py | 30 +++ src/squidpy/gr/__init__.py | 8 + src/squidpy/gr/_build.py | 376 ++++++++++++++++++++++++++++++++----- 4 files changed, 373 insertions(+), 45 deletions(-) diff --git a/docs/api.md b/docs/api.md index 37610f75a..cfbc16256 100644 --- a/docs/api.md +++ b/docs/api.md @@ -18,6 +18,10 @@ import squidpy as sq :toctree: api gr.spatial_neighbors + gr.spatial_neighbors_knn + gr.spatial_neighbors_radius + gr.spatial_neighbors_delaunay + gr.spatial_neighbors_grid gr.SpatialNeighborsResult gr.mask_graph gr.nhood_enrichment diff --git a/src/squidpy/_docs.py b/src/squidpy/_docs.py index a4596df75..4580b34ef 100644 --- a/src/squidpy/_docs.py +++ b/src/squidpy/_docs.py @@ -359,6 +359,33 @@ def decorator2(obj: Any) -> Any: If multiple `library_id`, column in :attr:`anndata.AnnData.obs` which stores mapping between ``library_id`` and obs.""" +_sdata_params = """\ +elements_to_coordinate_systems + A dictionary mapping element names of the SpatialData object to coordinate systems. + The elements can be either Shapes or Labels. For compatibility, the spatialdata table must annotate + all regions keys. Must not be ``None`` if ``adata`` is a :class:`spatialdata.SpatialData`. +table_key + Key in :attr:`spatialdata.SpatialData.tables` where the spatialdata table is stored. Must not be ``None`` if + ``adata`` is a :class:`spatialdata.SpatialData`.""" +_graph_common_params = """\ +percentile + Percentile of the distances to use as threshold. +transform + Adjacency matrix transform (``'spectral'``, ``'cosine'``, or ``None``). +set_diag + Whether to set the diagonal of the connectivities to ``1.0``. +key_added + Key which controls where the results are saved if ``copy = False``.""" +_spatial_neighbors_returns = """\ +If ``copy = True``, returns a :class:`~squidpy.gr.SpatialNeighborsResult` with the +spatial connectivities and distances matrices. + +Otherwise, modifies the ``adata`` with the following keys: + + - :attr:`anndata.AnnData.obsp` ``['{key_added}_connectivities']`` - the spatial connectivities. + - :attr:`anndata.AnnData.obsp` ``['{key_added}_distances']`` - the spatial distances. + - :attr:`anndata.AnnData.uns` ``['{key_added}']`` - :class:`dict` containing parameters.""" + d = DocstringProcessor( adata=_adata, img_container=_img_container, @@ -401,4 +428,7 @@ def decorator2(obj: Any) -> Any: groups=_groups, plotting_library_id=_plotting_library_id, library_key=_library_key, + sdata_params=_sdata_params, + graph_common_params=_graph_common_params, + spatial_neighbors_returns=_spatial_neighbors_returns, ) diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index a64930622..a5dd26973 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -7,6 +7,10 @@ SpatialNeighborsResult, mask_graph, spatial_neighbors, + spatial_neighbors_delaunay, + spatial_neighbors_grid, + spatial_neighbors_knn, + spatial_neighbors_radius, ) from squidpy.gr._ligrec import ligrec from squidpy.gr._nhood import ( @@ -26,6 +30,10 @@ "neighbors", "mask_graph", "spatial_neighbors", + "spatial_neighbors_knn", + "spatial_neighbors_radius", + "spatial_neighbors_delaunay", + "spatial_neighbors_grid", "ligrec", "centrality_scores", "interaction_matrix", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index d1060bef9..7572fad39 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -2,6 +2,7 @@ from __future__ import annotations +import warnings from functools import partial from typing import Any, NamedTuple, cast @@ -49,6 +50,10 @@ __all__ = [ "SpatialNeighborsResult", "spatial_neighbors", + "spatial_neighbors_knn", + "spatial_neighbors_radius", + "spatial_neighbors_delaunay", + "spatial_neighbors_grid", ] @@ -110,6 +115,66 @@ def _resolve_graph_builder( return KNNBuilder(**common, percentile=percentile) +def _resolve_spatial_data( + adata: AnnData | SpatialData, + *, + spatial_key: str, + elements_to_coordinate_systems: dict[str, str] | None, + table_key: str | None, + library_key: str | None, +) -> tuple[AnnData, str | None]: + """Resolve SpatialData to AnnData, returning (adata, library_key).""" + if isinstance(adata, SpatialData): + assert elements_to_coordinate_systems is not None, ( + "Since `adata` is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." + ) + assert table_key is not None, ( + "Since `adata` is a :class:`spatialdata.SpatialData`, `table_key` must not be `None`." + ) + elements, table = match_element_to_table(adata, list(elements_to_coordinate_systems), table_key) + assert table.obs_names.equals(adata.tables[table_key].obs_names), ( + "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." + ) + regions, region_key, instance_key = get_table_keys(adata.tables[table_key]) + regions = [regions] if isinstance(regions, str) else regions + ordered_regions_in_table = adata.tables[table_key].obs[region_key].unique() + + # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 + remove_centroids = {} + elem_instances = [] + for e in regions: + schema = get_model(elements[e]) + element_instances = get_element_instances(elements[e]).to_series() + if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): + element_instances = element_instances.drop(index=0) + remove_centroids[e] = True + else: + remove_centroids[e] = False + elem_instances.append(element_instances) + + element_instances = pd.concat(elem_instances) + if (not np.all(element_instances.values == adata.tables[table_key].obs[instance_key].values)) or ( + not np.all(ordered_regions_in_table == regions) + ): + raise ValueError( + "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." + ) + centroids = [] + for region_ in ordered_regions_in_table: + cs = elements_to_coordinate_systems[region_] + centroid = get_centroids(adata[region_], coordinate_system=cs)[["x", "y"]].compute() + + # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 + if remove_centroids[region_]: + centroid = centroid[1:].copy() + centroids.append(centroid) + + adata.tables[table_key].obsm[spatial_key] = np.concatenate(centroids) + adata = adata.tables[table_key] + library_key = region_key + return adata, library_key + + @d.dedent @inject_docs(t=Transform, c=CoordType) def spatial_neighbors( @@ -133,6 +198,18 @@ def spatial_neighbors( """ Create a graph from spatial coordinates. + .. deprecated:: 1.6.0 + The flat-parameter API of ``spatial_neighbors`` is deprecated and will + be removed in squidpy v1.7.0. Use one of the mode-specific functions + instead: + + - :func:`spatial_neighbors_knn` + - :func:`spatial_neighbors_radius` + - :func:`spatial_neighbors_delaunay` + - :func:`spatial_neighbors_grid` + + Passing a ``builder`` instance directly remains supported. + Parameters ---------- %(adata)s @@ -271,54 +348,59 @@ def spatial_neighbors( - :attr:`anndata.AnnData.obsp` ``['{{key_added}}_distances']`` - the spatial distances. - :attr:`anndata.AnnData.uns` ``['{{key_added}}']`` - :class:`dict` containing parameters. """ - if isinstance(adata, SpatialData): - assert elements_to_coordinate_systems is not None, ( - "Since `adata` is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." - ) - assert table_key is not None, ( - "Since `adata` is a :class:`spatialdata.SpatialData`, `table_key` must not be `None`." - ) - elements, table = match_element_to_table(adata, list(elements_to_coordinate_systems), table_key) - assert table.obs_names.equals(adata.tables[table_key].obs_names), ( - "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." + if builder is None: + warnings.warn( + "Calling `spatial_neighbors` without a `builder` argument is deprecated " + "and will be removed in squidpy v1.7.0. Use one of the mode-specific " + "functions instead: `spatial_neighbors_knn`, `spatial_neighbors_radius`, " + "`spatial_neighbors_delaunay`, or `spatial_neighbors_grid`.", + FutureWarning, + stacklevel=2, ) - regions, region_key, instance_key = get_table_keys(adata.tables[table_key]) - regions = [regions] if isinstance(regions, str) else regions - ordered_regions_in_table = adata.tables[table_key].obs[region_key].unique() - - # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - remove_centroids = {} - elem_instances = [] - for e in regions: - schema = get_model(elements[e]) - element_instances = get_element_instances(elements[e]).to_series() - if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): - element_instances = element_instances.drop(index=0) - remove_centroids[e] = True - else: - remove_centroids[e] = False - elem_instances.append(element_instances) - - element_instances = pd.concat(elem_instances) - if (not np.all(element_instances.values == adata.tables[table_key].obs[instance_key].values)) or ( - not np.all(ordered_regions_in_table == regions) - ): - raise ValueError( - "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." - ) - centroids = [] - for region_ in ordered_regions_in_table: - cs = elements_to_coordinate_systems[region_] - centroid = get_centroids(adata[region_], coordinate_system=cs)[["x", "y"]].compute() + return _spatial_neighbors( + adata, + spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, + library_key=library_key, + coord_type=coord_type, + n_neighs=n_neighs, + radius=radius, + delaunay=delaunay, + n_rings=n_rings, + percentile=percentile, + transform=transform, + set_diag=set_diag, + builder=builder, + key_added=key_added, + copy=copy, + ) - # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - if remove_centroids[region_]: - centroid = centroid[1:].copy() - centroids.append(centroid) - adata.tables[table_key].obsm[spatial_key] = np.concatenate(centroids) - adata = adata.tables[table_key] - library_key = region_key +def _spatial_neighbors( + adata: AnnData | SpatialData, + *, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + coord_type: str | CoordType | None = None, + n_neighs: int | None = None, + radius: float | tuple[float, float] | None = None, + delaunay: bool | None = None, + n_rings: int | None = None, + percentile: float | None = None, + transform: str | Transform | None = None, + set_diag: bool | None = None, + builder: GraphBuilder | None = None, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Internal implementation of spatial_neighbors (no deprecation warning).""" + adata, library_key = _resolve_spatial_data( + adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, library_key=library_key, + ) _assert_spatial_basis(adata, spatial_key) @@ -354,6 +436,210 @@ def spatial_neighbors( has_spatial_uns=Key.uns.spatial in adata.uns, ) + return _run_spatial_neighbors(adata, builder=builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy) + + +@d.dedent +def spatial_neighbors_knn( + adata: AnnData | SpatialData, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + n_neighs: int = 6, + percentile: float | None = None, + transform: str | Transform | None = None, + set_diag: bool = False, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Create a k-nearest-neighbor graph from spatial coordinates. + + Parameters + ---------- + %(adata)s + %(spatial_key)s + %(sdata_params)s + %(library_key)s + n_neighs + Number of nearest neighbors. Defaults to ``6``. + %(graph_common_params)s + %(copy)s + + Returns + ------- + %(spatial_neighbors_returns)s + """ + transform_enum = Transform.NONE if transform is None else Transform(transform) + builder = KNNBuilder( + n_neighs=n_neighs, percentile=percentile, + transform=transform_enum, set_diag=set_diag, + ) + return _spatial_neighbors( + adata, spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, library_key=library_key, + builder=builder, key_added=key_added, copy=copy, + ) + + +@d.dedent +def spatial_neighbors_radius( + adata: AnnData | SpatialData, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + radius: float | tuple[float, float] = 1.0, + n_neighs: int = 6, + percentile: float | None = None, + transform: str | Transform | None = None, + set_diag: bool = False, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Create a radius-based graph from spatial coordinates. + + Parameters + ---------- + %(adata)s + %(spatial_key)s + %(sdata_params)s + %(library_key)s + radius + Neighborhood radius. If a :class:`tuple`, the graph is built with the + maximum radius and then pruned to the interval ``[min(radius), max(radius)]``. + n_neighs + Number of nearest neighbors used internally by the radius graph builder. + Defaults to ``6``. + %(graph_common_params)s + %(copy)s + + Returns + ------- + %(spatial_neighbors_returns)s + """ + transform_enum = Transform.NONE if transform is None else Transform(transform) + builder = RadiusBuilder( + n_neighs=n_neighs, radius=radius, percentile=percentile, + transform=transform_enum, set_diag=set_diag, + ) + return _spatial_neighbors( + adata, spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, library_key=library_key, + builder=builder, key_added=key_added, copy=copy, + ) + + +@d.dedent +def spatial_neighbors_delaunay( + adata: AnnData | SpatialData, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + radius: tuple[float, float] | None = None, + percentile: float | None = None, + transform: str | Transform | None = None, + set_diag: bool = False, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Create a Delaunay triangulation graph from spatial coordinates. + + Parameters + ---------- + %(adata)s + %(spatial_key)s + %(sdata_params)s + %(library_key)s + radius + If a :class:`tuple`, used as a post-construction pruning interval + ``[min(radius), max(radius)]``. + %(graph_common_params)s + %(copy)s + + Returns + ------- + %(spatial_neighbors_returns)s + """ + transform_enum = Transform.NONE if transform is None else Transform(transform) + builder = DelaunayBuilder( + radius=radius, percentile=percentile, + transform=transform_enum, set_diag=set_diag, + ) + return _spatial_neighbors( + adata, spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, library_key=library_key, + builder=builder, key_added=key_added, copy=copy, + ) + + +@d.dedent +def spatial_neighbors_grid( + adata: AnnData | SpatialData, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + n_neighs: int = 6, + n_rings: int = 1, + delaunay: bool = False, + transform: str | Transform | None = None, + set_diag: bool = False, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Create a grid-based graph from spatial coordinates. + + This is the mode used for Visium-like grid coordinates. + + Parameters + ---------- + %(adata)s + %(spatial_key)s + %(sdata_params)s + %(library_key)s + n_neighs + Number of neighboring tiles. Defaults to ``6``. + n_rings + Number of rings of neighbors. Defaults to ``1``. + delaunay + Whether to compute the grid graph from Delaunay triangulation. + %(graph_common_params)s + %(copy)s + + Returns + ------- + %(spatial_neighbors_returns)s + """ + assert_positive(n_rings, name="n_rings") + assert_positive(n_neighs, name="n_neighs") + transform_enum = Transform.NONE if transform is None else Transform(transform) + builder = GridBuilder( + n_neighs=n_neighs, n_rings=n_rings, delaunay=delaunay, + transform=transform_enum, set_diag=set_diag, + ) + return _spatial_neighbors( + adata, spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, library_key=library_key, + builder=builder, key_added=key_added, copy=copy, + ) + + +def _run_spatial_neighbors( + adata: AnnData, + *, + builder: GraphBuilder, + spatial_key: str = Key.obsm.spatial, + library_key: str | None = None, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Shared core: build the graph from a resolved builder and save results.""" if library_key is not None: _assert_categorical_obs(adata, key=library_key) libs = adata.obs[library_key].cat.categories From 2e75b3f3413513711fef56addf49d9b7a4f6c464 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:40:49 +0200 Subject: [PATCH 018/131] move branches to classes --- src/squidpy/gr/neighbors.py | 239 +++++++++++--------------- tests/graph/test_spatial_neighbors.py | 3 +- 2 files changed, 99 insertions(+), 143 deletions(-) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index c65a3c7b8..67d8089d6 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -120,15 +120,23 @@ def coord_type(self) -> CoordType: return CoordType.GENERIC def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - return_distance=True, - set_diag=self.set_diag, - ), + N = coords.shape[0] + tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") + tree.fit(coords) + + dists, col_indices = tree.kneighbors() + dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) + row_indices = np.repeat(np.arange(N), self.n_neighs) + + Adj = csr_matrix( + (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), + shape=(N, N), ) + Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) + + Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) + Dst.setdiag(0.0) + return Adj, Dst class _RadiusFilterBuilder(GraphBuilder): @@ -163,16 +171,25 @@ def __init__( self.n_neighs = n_neighs def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - radius=self.radius, - return_distance=True, - set_diag=self.set_diag, - ), + N = coords.shape[0] + r = self.radius if isinstance(self.radius, int | float) else max(self.radius) + tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=r, metric="euclidean") + tree.fit(coords) + + dists, col_indices = tree.radius_neighbors() + row_indices = np.repeat(np.arange(N), [len(x) for x in col_indices]) + dists = np.concatenate(dists) + col_indices = np.concatenate(col_indices) + + Adj = csr_matrix( + (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), + shape=(N, N), ) + Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) + + Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) + Dst.setdiag(0.0) + return Adj, Dst class DelaunayBuilder(_RadiusFilterBuilder): @@ -201,17 +218,23 @@ def __init__( self.n_neighs = n_neighs def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return cast( - tuple[csr_matrix, csr_matrix], - _build_connectivity( - coords, - n_neighs=self.n_neighs, - radius=self.radius, - delaunay=True, - return_distance=True, - set_diag=self.set_diag, - ), - ) + N = coords.shape[0] + tri = Delaunay(coords) + indptr, indices = tri.vertex_neighbor_vertices + Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) + + # fmt: off + dists = np.array(list(chain(*( + euclidean_distances(coords[indices[indptr[i] : indptr[i + 1]], :], coords[np.newaxis, i, :]) + for i in range(N) + if len(indices[indptr[i] : indptr[i + 1]]) + )))).squeeze() + # fmt: on + Dst = csr_matrix((dists, indices, indptr), shape=(N, N)) + + Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) + Dst.setdiag(0.0) + return Adj, Dst class GridBuilder(GraphBuilder): @@ -237,13 +260,53 @@ def coord_type(self) -> CoordType: return CoordType.GRID def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: - return _build_grid( - coords, - n_neighs=self.n_neighs, - n_rings=self.n_rings, - delaunay=self.delaunay, - set_diag=self.set_diag, - ) + if self.n_rings > 1: + Adj = self._base_adjacency(coords, set_diag=True) + Res, Walk = Adj, Adj + for i in range(self.n_rings - 1): + Walk = Walk @ Adj + Walk[Res.nonzero()] = 0.0 + Walk.eliminate_zeros() + Walk.data[:] = i + 2.0 + Res = Res + Walk + Adj = Res + Adj.setdiag(float(self.set_diag)) + Adj.eliminate_zeros() + + Dst = Adj.copy() + Adj.data[:] = 1.0 + else: + Adj = self._base_adjacency(coords, set_diag=self.set_diag) + Dst = Adj.copy() + + Dst.setdiag(0.0) + return Adj, Dst + + def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: + """KNN adjacency with median-distance correction for grid coordinates.""" + N = coords.shape[0] + if self.delaunay: + tri = Delaunay(coords) + indptr, indices = tri.vertex_neighbor_vertices + Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) + else: + tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") + tree.fit(coords) + dists, col_indices = tree.kneighbors() + dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) + row_indices = np.repeat(np.arange(N), self.n_neighs) + + dist_cutoff = np.median(dists) * 1.3 + mask = dists < dist_cutoff + row_indices, col_indices = row_indices[mask], col_indices[mask] + + Adj = csr_matrix( + (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), + shape=(N, N), + ) + + Adj.setdiag(1.0 if set_diag else Adj.diagonal()) + return Adj # --------------------------------------------------------------------------- @@ -265,112 +328,6 @@ def _filter_by_radius_interval( Adj.setdiag(a_diag) -def _build_grid( - coords: NDArrayA, - n_neighs: int, - n_rings: int, - delaunay: bool = False, - set_diag: bool = False, -) -> tuple[csr_matrix, csr_matrix]: - if n_rings > 1: - Adj: csr_matrix = _build_connectivity( - coords, - n_neighs=n_neighs, - neigh_correct=True, - set_diag=True, - delaunay=delaunay, - return_distance=False, - ) - Res, Walk = Adj, Adj - for i in range(n_rings - 1): - Walk = Walk @ Adj - Walk[Res.nonzero()] = 0.0 - Walk.eliminate_zeros() - Walk.data[:] = i + 2.0 - Res = Res + Walk - Adj = Res - Adj.setdiag(float(set_diag)) - Adj.eliminate_zeros() - - Dst = Adj.copy() - Adj.data[:] = 1.0 - else: - Adj = _build_connectivity( - coords, - n_neighs=n_neighs, - neigh_correct=True, - delaunay=delaunay, - set_diag=set_diag, - ) - Dst = Adj.copy() - - Dst.setdiag(0.0) - - return Adj, Dst - - -def _build_connectivity( - coords: NDArrayA, - n_neighs: int, - radius: float | tuple[float, float] | None = None, - delaunay: bool = False, - neigh_correct: bool = False, - set_diag: bool = False, - return_distance: bool = False, -) -> csr_matrix | tuple[csr_matrix, csr_matrix]: - N = coords.shape[0] - if delaunay: - tri = Delaunay(coords) - indptr, indices = tri.vertex_neighbor_vertices - Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) - - if return_distance: - # fmt: off - dists = np.array(list(chain(*( - euclidean_distances(coords[indices[indptr[i] : indptr[i + 1]], :], coords[np.newaxis, i, :]) - for i in range(N) - if len(indices[indptr[i] : indptr[i + 1]]) - )))).squeeze() - Dst = csr_matrix((dists, indices, indptr), shape=(N, N)) - # fmt: on - else: - r = 1 if radius is None else radius if isinstance(radius, int | float) else max(radius) - tree = NearestNeighbors(n_neighbors=n_neighs, radius=r, metric="euclidean") - tree.fit(coords) - - if radius is None: - dists, col_indices = tree.kneighbors() - dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) - row_indices = np.repeat(np.arange(N), n_neighs) - if neigh_correct: - dist_cutoff = np.median(dists) * 1.3 # there's a small amount of sway - mask = dists < dist_cutoff - row_indices, col_indices, dists = ( - row_indices[mask], - col_indices[mask], - dists[mask], - ) - else: - dists, col_indices = tree.radius_neighbors() - row_indices = np.repeat(np.arange(N), [len(x) for x in col_indices]) - dists = np.concatenate(dists) - col_indices = np.concatenate(col_indices) - - Adj = csr_matrix( - (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), - shape=(N, N), - ) - if return_distance: - Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) - - # radius-based filtering needs same indices/indptr: do not remove 0s - Adj.setdiag(1.0 if set_diag else Adj.diagonal()) - if return_distance: - Dst.setdiag(0.0) - return Adj, Dst - - return Adj - @njit def _csr_bilateral_diag_scale_helper( diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 8cf32c2c5..1f6bdbd03 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -18,7 +18,6 @@ GridBuilder, KNNBuilder, RadiusBuilder, - _build_connectivity, ) from squidpy.gr.neighbors import ( KNNBuilder as PublicKNNBuilder, @@ -305,7 +304,7 @@ def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord assert not ((result.connectivities != result_filtered.connectivities).nnz == 0) assert result.distances.max() > result_filtered.distances.max() - Adj, Dst = _build_connectivity(adata_hne.obsm["spatial"], n_neighs=6, return_distance=True, set_diag=False) + Adj, Dst = KNNBuilder(n_neighs=6, set_diag=False)._build_graph(adata_hne.obsm["spatial"]) threshold = np.percentile(Dst.data, percentile) Adj[Dst > threshold] = 0.0 Dst[Dst > threshold] = 0.0 From 8c1c2ef94c474d142f5b40801c6ef7f889f8884a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:41:01 +0000 Subject: [PATCH 019/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_build.py | 77 ++++++++++++++++++++++++++----------- src/squidpy/gr/neighbors.py | 1 - 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 7572fad39..203ecc0d7 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -398,8 +398,11 @@ def _spatial_neighbors( ) -> SpatialNeighborsResult | None: """Internal implementation of spatial_neighbors (no deprecation warning).""" adata, library_key = _resolve_spatial_data( - adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, library_key=library_key, + adata, + spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, + library_key=library_key, ) _assert_spatial_basis(adata, spatial_key) @@ -436,7 +439,9 @@ def _spatial_neighbors( has_spatial_uns=Key.uns.spatial in adata.uns, ) - return _run_spatial_neighbors(adata, builder=builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy) + return _run_spatial_neighbors( + adata, builder=builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy + ) @d.dedent @@ -472,14 +477,20 @@ def spatial_neighbors_knn( """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = KNNBuilder( - n_neighs=n_neighs, percentile=percentile, - transform=transform_enum, set_diag=set_diag, + n_neighs=n_neighs, + percentile=percentile, + transform=transform_enum, + set_diag=set_diag, ) return _spatial_neighbors( - adata, spatial_key=spatial_key, + adata, + spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, library_key=library_key, - builder=builder, key_added=key_added, copy=copy, + table_key=table_key, + library_key=library_key, + builder=builder, + key_added=key_added, + copy=copy, ) @@ -521,14 +532,21 @@ def spatial_neighbors_radius( """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = RadiusBuilder( - n_neighs=n_neighs, radius=radius, percentile=percentile, - transform=transform_enum, set_diag=set_diag, + n_neighs=n_neighs, + radius=radius, + percentile=percentile, + transform=transform_enum, + set_diag=set_diag, ) return _spatial_neighbors( - adata, spatial_key=spatial_key, + adata, + spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, library_key=library_key, - builder=builder, key_added=key_added, copy=copy, + table_key=table_key, + library_key=library_key, + builder=builder, + key_added=key_added, + copy=copy, ) @@ -566,14 +584,20 @@ def spatial_neighbors_delaunay( """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = DelaunayBuilder( - radius=radius, percentile=percentile, - transform=transform_enum, set_diag=set_diag, + radius=radius, + percentile=percentile, + transform=transform_enum, + set_diag=set_diag, ) return _spatial_neighbors( - adata, spatial_key=spatial_key, + adata, + spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, library_key=library_key, - builder=builder, key_added=key_added, copy=copy, + table_key=table_key, + library_key=library_key, + builder=builder, + key_added=key_added, + copy=copy, ) @@ -619,14 +643,21 @@ def spatial_neighbors_grid( assert_positive(n_neighs, name="n_neighs") transform_enum = Transform.NONE if transform is None else Transform(transform) builder = GridBuilder( - n_neighs=n_neighs, n_rings=n_rings, delaunay=delaunay, - transform=transform_enum, set_diag=set_diag, + n_neighs=n_neighs, + n_rings=n_rings, + delaunay=delaunay, + transform=transform_enum, + set_diag=set_diag, ) return _spatial_neighbors( - adata, spatial_key=spatial_key, + adata, + spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, library_key=library_key, - builder=builder, key_added=key_added, copy=copy, + table_key=table_key, + library_key=library_key, + builder=builder, + key_added=key_added, + copy=copy, ) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 67d8089d6..a4726ce85 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -328,7 +328,6 @@ def _filter_by_radius_interval( Adj.setdiag(a_diag) - @njit def _csr_bilateral_diag_scale_helper( mat: csr_array | csr_matrix, From 268d30cb9e88b97a5e391e0696871504614b6b38 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:42:42 +0200 Subject: [PATCH 020/131] remove radius abstraction --- src/squidpy/gr/neighbors.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 67d8089d6..8606dd644 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -139,22 +139,7 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return Adj, Dst -class _RadiusFilterBuilder(GraphBuilder): - """Intermediate base for builders that support radius-interval pruning.""" - - radius: float | tuple[float, float] | None - n_neighs: int - - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) - - -class RadiusBuilder(_RadiusFilterBuilder): +class RadiusBuilder(GraphBuilder): """Build a generic radius-based spatial graph.""" def __init__( @@ -170,6 +155,10 @@ def __init__( self.radius = radius self.n_neighs = n_neighs + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] r = self.radius if isinstance(self.radius, int | float) else max(self.radius) @@ -191,8 +180,12 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: Dst.setdiag(0.0) return Adj, Dst + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) -class DelaunayBuilder(_RadiusFilterBuilder): + +class DelaunayBuilder(GraphBuilder): """Build a generic spatial graph from a Delaunay triangulation.""" def __init__( @@ -217,6 +210,10 @@ def __init__( self.radius = radius self.n_neighs = n_neighs + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tri = Delaunay(coords) @@ -236,6 +233,10 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: Dst.setdiag(0.0) return Adj, Dst + def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + if isinstance(self.radius, Iterable): + _filter_by_radius_interval(Adj, Dst, self.radius) + class GridBuilder(GraphBuilder): """Build a grid-based spatial graph.""" From aa9734ab0bb6ae446da7ab6fd363d8a1c3339b22 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:47:33 +0200 Subject: [PATCH 021/131] add extensibility page --- docs/api.md | 3 ++ docs/extensibility.md | 65 +++++++++++++++++++++++++++++++++++++ docs/index.md | 1 + src/squidpy/gr/neighbors.py | 18 ++-------- 4 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 docs/extensibility.md diff --git a/docs/api.md b/docs/api.md index cfbc16256..60434e4be 100644 --- a/docs/api.md +++ b/docs/api.md @@ -116,6 +116,9 @@ import squidpy as sq ## Extensibility + +See the {doc}`extensibility guide ` for how to implement a custom graph builder. + ```{eval-rst} .. module:: squidpy.gr.neighbors .. currentmodule:: squidpy diff --git a/docs/extensibility.md b/docs/extensibility.md new file mode 100644 index 000000000..b6605f61b --- /dev/null +++ b/docs/extensibility.md @@ -0,0 +1,65 @@ +# Extensibility + +## Custom graph builders + +The `squidpy.gr.neighbors` module exposes a {class}`~squidpy.gr.neighbors.GraphBuilder` +base class that all built-in graph construction strategies inherit from. +You can implement your own strategy by subclassing it. + +### Minimal example + +```python +import numpy as np +from scipy.sparse import csr_matrix +from squidpy.gr.neighbors import GraphBuilder +from squidpy._constants._constants import CoordType + + +class MyBuilder(GraphBuilder): + """Example: connect every point to its single nearest neighbor.""" + + @property + def coord_type(self) -> CoordType: + return CoordType.GENERIC + + def _build_graph(self, coords): + from sklearn.neighbors import NearestNeighbors + + N = coords.shape[0] + tree = NearestNeighbors(n_neighbors=2, metric="euclidean") + tree.fit(coords) + dists, indices = tree.kneighbors() + + # drop self-neighbor (index 0), keep only the closest other point + row = np.arange(N) + col = indices[:, 1] + d = dists[:, 1] + + Adj = csr_matrix((np.ones(N, dtype=np.float32), (row, col)), shape=(N, N)) + Dst = csr_matrix((d, (row, col)), shape=(N, N)) + return Adj, Dst +``` + +### Using a custom builder + +Pass an instance to any of the spatial-neighbor functions via the ``builder`` +argument: + +```python +import squidpy as sq + +sq.gr.spatial_neighbors_knn(adata) # built-in KNN mode +sq.gr.spatial_neighbors(adata, builder=MyBuilder()) # custom builder +``` + +### What to override + +| Method / property | Required | Purpose | +|---|---|---| +| {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder._build_graph` | yes | Construct and return ``(Adj, Dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder._apply_filters` | no | Post-processing on the raw ``Adj``/``Dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | + +The base class handles percentile filtering ({meth}`~squidpy.gr.neighbors.GraphBuilder._apply_percentile`), +adjacency transforms ({meth}`~squidpy.gr.neighbors.GraphBuilder._apply_transform`), and +{class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. diff --git a/docs/index.md b/docs/index.md index e44bc2995..7ddaff4d7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -63,6 +63,7 @@ We are happy about any contributions! Before you start, check out our [contribut installation api classes + extensibility release_notes references contributing diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 886ec0659..d167aabbc 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -1,20 +1,6 @@ -"""Graph construction strategies for :func:`squidpy.gr.spatial_neighbors`. +"""Graph construction strategies for spatial neighbor graphs. -This module provides the builder classes that control how spatial neighbor -graphs are constructed. :func:`~squidpy.gr.spatial_neighbors` accepts a -``builder`` argument; when supplied, the builder fully determines the -graph-construction algorithm and its parameters. - -Built-in builders cover the common cases: - -- :class:`KNNBuilder` -- k-nearest-neighbor graph. -- :class:`RadiusBuilder` -- radius-based graph. -- :class:`DelaunayBuilder` -- Delaunay triangulation graph. -- :class:`GridBuilder` -- grid (Visium-style) graph. - -To implement a custom strategy, subclass :class:`GraphBuilder` and override -:meth:`~GraphBuilder._build_graph`. The returned object can then be passed -to ``spatial_neighbors(adata, builder=my_builder)``. +See the :doc:`/extensibility` guide for how to implement a custom builder. """ from __future__ import annotations From 67bad43323b553da20fc69c546cb61a8cc26b821 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 17:53:46 +0200 Subject: [PATCH 022/131] unprivate methods and add extensibility page --- docs/extensibility.md | 73 +++++++++++++++------------ src/squidpy/gr/neighbors.py | 20 ++++---- tests/graph/test_spatial_neighbors.py | 2 +- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index b6605f61b..1ea9b0704 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -6,60 +6,71 @@ The `squidpy.gr.neighbors` module exposes a {class}`~squidpy.gr.neighbors.GraphB base class that all built-in graph construction strategies inherit from. You can implement your own strategy by subclassing it. -### Minimal example +### What to override + +| Method / property | Required | Purpose | +|---|---|---| +| {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(Adj, Dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-processing on the raw ``Adj``/``Dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | + +The base class handles percentile filtering, adjacency transforms, and +{class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. + +### Example: mutual KNN + +The built-in {class}`~squidpy.gr.neighbors.KNNBuilder` keeps all k-nearest +edges regardless of reciprocity. A mutual KNN graph retains an edge only when +both endpoints consider each other a neighbor, producing a sparser and more +conservative graph: ```python import numpy as np from scipy.sparse import csr_matrix -from squidpy.gr.neighbors import GraphBuilder +from sklearn.neighbors import NearestNeighbors + from squidpy._constants._constants import CoordType +from squidpy.gr.neighbors import GraphBuilder + +class MutualKNNBuilder(GraphBuilder): + """KNN graph keeping only mutual (reciprocal) edges.""" -class MyBuilder(GraphBuilder): - """Example: connect every point to its single nearest neighbor.""" + def __init__(self, n_neighs: int = 6, **kwargs): + super().__init__(**kwargs) + self.n_neighs = n_neighs @property def coord_type(self) -> CoordType: return CoordType.GENERIC - def _build_graph(self, coords): - from sklearn.neighbors import NearestNeighbors - + def build_graph(self, coords): N = coords.shape[0] - tree = NearestNeighbors(n_neighbors=2, metric="euclidean") + tree = NearestNeighbors(n_neighbors=self.n_neighs, metric="euclidean") tree.fit(coords) dists, indices = tree.kneighbors() - # drop self-neighbor (index 0), keep only the closest other point - row = np.arange(N) - col = indices[:, 1] - d = dists[:, 1] + row = np.repeat(np.arange(N), self.n_neighs) + col = indices.reshape(-1) + d = dists.reshape(-1) - Adj = csr_matrix((np.ones(N, dtype=np.float32), (row, col)), shape=(N, N)) + Adj = csr_matrix((np.ones_like(d, dtype=np.float32), (row, col)), shape=(N, N)) Dst = csr_matrix((d, (row, col)), shape=(N, N)) + + # keep only mutual edges + mutual = Adj.multiply(Adj.T) + Adj = mutual.tocsr() + Dst = Dst.multiply(mutual).tocsr() + + Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) + Dst.setdiag(0.0) return Adj, Dst ``` -### Using a custom builder - -Pass an instance to any of the spatial-neighbor functions via the ``builder`` -argument: +Use it like any other builder: ```python import squidpy as sq -sq.gr.spatial_neighbors_knn(adata) # built-in KNN mode -sq.gr.spatial_neighbors(adata, builder=MyBuilder()) # custom builder +sq.gr.spatial_neighbors(adata, builder=MutualKNNBuilder(n_neighs=6)) ``` - -### What to override - -| Method / property | Required | Purpose | -|---|---|---| -| {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder._build_graph` | yes | Construct and return ``(Adj, Dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder._apply_filters` | no | Post-processing on the raw ``Adj``/``Dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | - -The base class handles percentile filtering ({meth}`~squidpy.gr.neighbors.GraphBuilder._apply_percentile`), -adjacency transforms ({meth}`~squidpy.gr.neighbors.GraphBuilder._apply_transform`), and -{class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index d167aabbc..9a15ef3b8 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -53,9 +53,9 @@ def coord_type(self) -> CoordType: def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: with warnings.catch_warnings(): warnings.simplefilter("ignore", SparseEfficiencyWarning) - Adj, Dst = self._build_graph(coords) + Adj, Dst = self.build_graph(coords) - self._apply_filters(Adj, Dst) + self.apply_filters(Adj, Dst) self._apply_percentile(Adj, Dst) Adj.eliminate_zeros() Dst.eliminate_zeros() @@ -63,10 +63,10 @@ def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return self._apply_transform(Adj), Dst @abstractmethod - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: """Construct raw adjacency and distance matrices.""" - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: """Apply builder-specific post-processing filters.""" return None @@ -105,7 +105,7 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GENERIC - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") tree.fit(coords) @@ -145,7 +145,7 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GENERIC - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] r = self.radius if isinstance(self.radius, int | float) else max(self.radius) tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=r, metric="euclidean") @@ -166,7 +166,7 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: Dst.setdiag(0.0) return Adj, Dst - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: if isinstance(self.radius, Iterable): _filter_by_radius_interval(Adj, Dst, self.radius) @@ -200,7 +200,7 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GENERIC - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tri = Delaunay(coords) indptr, indices = tri.vertex_neighbor_vertices @@ -219,7 +219,7 @@ def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: Dst.setdiag(0.0) return Adj, Dst - def _apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: if isinstance(self.radius, Iterable): _filter_by_radius_interval(Adj, Dst, self.radius) @@ -246,7 +246,7 @@ def __init__( def coord_type(self) -> CoordType: return CoordType.GRID - def _build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: if self.n_rings > 1: Adj = self._base_adjacency(coords, set_diag=True) Res, Walk = Adj, Adj diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 1f6bdbd03..39be507cb 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -304,7 +304,7 @@ def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord assert not ((result.connectivities != result_filtered.connectivities).nnz == 0) assert result.distances.max() > result_filtered.distances.max() - Adj, Dst = KNNBuilder(n_neighs=6, set_diag=False)._build_graph(adata_hne.obsm["spatial"]) + Adj, Dst = KNNBuilder(n_neighs=6, set_diag=False).build_graph(adata_hne.obsm["spatial"]) threshold = np.percentile(Dst.data, percentile) Adj[Dst > threshold] = 0.0 Dst[Dst > threshold] = 0.0 From 65fa2453a5edb7ca9bb7fdaf1a3775687f7c3205 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 2 Apr 2026 18:00:38 +0200 Subject: [PATCH 023/131] give a better example --- docs/extensibility.md | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index 1ea9b0704..00bf73478 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -17,28 +17,29 @@ You can implement your own strategy by subclassing it. The base class handles percentile filtering, adjacency transforms, and {class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. -### Example: mutual KNN +### Example: fast radius search with SNN -The built-in {class}`~squidpy.gr.neighbors.KNNBuilder` keeps all k-nearest -edges regardless of reciprocity. A mutual KNN graph retains an edge only when -both endpoints consider each other a neighbor, producing a sparser and more -conservative graph: +The built-in {class}`~squidpy.gr.neighbors.RadiusBuilder` uses scikit-learn's +``NearestNeighbors``. The [snnpy](https://github.com/nla-group/snn) library +provides a faster exact fixed-radius search based on PCA-based pruning. The +example below swaps the backend while keeping full compatibility with the rest +of the Squidpy graph pipeline: ```python import numpy as np from scipy.sparse import csr_matrix -from sklearn.neighbors import NearestNeighbors +from snnpy import build_snn_model from squidpy._constants._constants import CoordType from squidpy.gr.neighbors import GraphBuilder -class MutualKNNBuilder(GraphBuilder): - """KNN graph keeping only mutual (reciprocal) edges.""" +class SNNRadiusBuilder(GraphBuilder): + """Radius graph using the SNN fixed-radius search backend.""" - def __init__(self, n_neighs: int = 6, **kwargs): + def __init__(self, radius: float, **kwargs): super().__init__(**kwargs) - self.n_neighs = n_neighs + self.radius = radius @property def coord_type(self) -> CoordType: @@ -46,22 +47,21 @@ class MutualKNNBuilder(GraphBuilder): def build_graph(self, coords): N = coords.shape[0] - tree = NearestNeighbors(n_neighbors=self.n_neighs, metric="euclidean") - tree.fit(coords) - dists, indices = tree.kneighbors() - - row = np.repeat(np.arange(N), self.n_neighs) - col = indices.reshape(-1) - d = dists.reshape(-1) - - Adj = csr_matrix((np.ones_like(d, dtype=np.float32), (row, col)), shape=(N, N)) + model = build_snn_model(coords, verbose=0) + indices, dists = model.batch_query_radius( + coords, self.radius, return_distance=True, + ) + + row = np.repeat(np.arange(N), [len(idx) for idx in indices]) + col = np.concatenate(indices) + d = np.concatenate(dists).astype(np.float64) + + Adj = csr_matrix( + (np.ones(len(row), dtype=np.float32), (row, col)), + shape=(N, N), + ) Dst = csr_matrix((d, (row, col)), shape=(N, N)) - # keep only mutual edges - mutual = Adj.multiply(Adj.T) - Adj = mutual.tocsr() - Dst = Dst.multiply(mutual).tocsr() - Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) Dst.setdiag(0.0) return Adj, Dst @@ -72,5 +72,5 @@ Use it like any other builder: ```python import squidpy as sq -sq.gr.spatial_neighbors(adata, builder=MutualKNNBuilder(n_neighs=6)) +sq.gr.spatial_neighbors(adata, builder=SNNRadiusBuilder(radius=100.0)) ``` From 85bfdebd98a5a65b551f7cb88234ca59fe07f1ce Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 10:59:30 +0200 Subject: [PATCH 024/131] apply signature suggestion --- src/squidpy/gr/_build.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 203ecc0d7..5ef948ef9 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -447,6 +447,7 @@ def _spatial_neighbors( @d.dedent def spatial_neighbors_knn( adata: AnnData | SpatialData, + *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, @@ -497,6 +498,7 @@ def spatial_neighbors_knn( @d.dedent def spatial_neighbors_radius( adata: AnnData | SpatialData, + *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, @@ -553,6 +555,7 @@ def spatial_neighbors_radius( @d.dedent def spatial_neighbors_delaunay( adata: AnnData | SpatialData, + *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, @@ -604,6 +607,7 @@ def spatial_neighbors_delaunay( @d.dedent def spatial_neighbors_grid( adata: AnnData | SpatialData, + *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, From 79f584f82db52da62cdf264bae03c56cbed9f96b Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 11:49:08 +0200 Subject: [PATCH 025/131] reorganize abstractions --- src/squidpy/gr/_build.py | 76 +++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 5ef948ef9..bfc9aeadd 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,7 +3,6 @@ from __future__ import annotations import warnings -from functools import partial from typing import Any, NamedTuple, cast import geopandas as gpd @@ -377,6 +376,26 @@ def spatial_neighbors( ) +def _prepare_spatial_neighbors_input( + adata: AnnData | SpatialData, + *, + spatial_key: str, + elements_to_coordinate_systems: dict[str, str] | None, + table_key: str | None, + library_key: str | None, +) -> tuple[AnnData, str | None]: + """Resolve input data and validate the requested spatial basis.""" + adata, library_key = _resolve_spatial_data( + adata, + spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, + library_key=library_key, + ) + _assert_spatial_basis(adata, spatial_key) + return adata, library_key + + def _spatial_neighbors( adata: AnnData | SpatialData, *, @@ -397,7 +416,7 @@ def _spatial_neighbors( copy: bool = False, ) -> SpatialNeighborsResult | None: """Internal implementation of spatial_neighbors (no deprecation warning).""" - adata, library_key = _resolve_spatial_data( + adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, @@ -405,8 +424,6 @@ def _spatial_neighbors( library_key=library_key, ) - _assert_spatial_basis(adata, spatial_key) - if builder is not None: _validate_no_legacy_params( coord_type=coord_type, @@ -483,13 +500,18 @@ def spatial_neighbors_knn( transform=transform_enum, set_diag=set_diag, ) - return _spatial_neighbors( + adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, library_key=library_key, + ) + return _run_spatial_neighbors( + adata, builder=builder, + spatial_key=spatial_key, + library_key=library_key, key_added=key_added, copy=copy, ) @@ -540,13 +562,18 @@ def spatial_neighbors_radius( transform=transform_enum, set_diag=set_diag, ) - return _spatial_neighbors( + adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, library_key=library_key, + ) + return _run_spatial_neighbors( + adata, builder=builder, + spatial_key=spatial_key, + library_key=library_key, key_added=key_added, copy=copy, ) @@ -592,13 +619,18 @@ def spatial_neighbors_delaunay( transform=transform_enum, set_diag=set_diag, ) - return _spatial_neighbors( + adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, library_key=library_key, + ) + return _run_spatial_neighbors( + adata, builder=builder, + spatial_key=spatial_key, + library_key=library_key, key_added=key_added, copy=copy, ) @@ -653,13 +685,18 @@ def spatial_neighbors_grid( transform=transform_enum, set_diag=set_diag, ) - return _spatial_neighbors( + adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, library_key=library_key, + ) + return _run_spatial_neighbors( + adata, builder=builder, + spatial_key=spatial_key, + library_key=library_key, key_added=key_added, copy=copy, ) @@ -685,23 +722,17 @@ def _run_spatial_neighbors( start = logg.info( f"Creating graph using `{builder.coord_type}` coordinates and `{builder.transform}` transform and `{len(libs)}` libraries." ) - _build_fun = partial( - _spatial_neighbor, - spatial_key=spatial_key, - builder=builder, - ) - if library_key is not None: mats: list[tuple[spmatrix, spmatrix]] = [] ixs: list[int] = [] for lib in libs: ixs.extend(np.where(adata.obs[library_key] == lib)[0]) - mats.append(_build_fun(adata[adata.obs[library_key] == lib])) + mats.append(builder.build(adata[adata.obs[library_key] == lib].obsm[spatial_key])) ixs = cast(list[int], np.argsort(ixs).tolist()) Adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] Dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] else: - Adj, Dst = _build_fun(adata) + Adj, Dst = builder.build(adata.obsm[spatial_key]) neighs_key = Key.uns.spatial_neighs(key_added) conns_key = Key.obsp.spatial_conn(key_added) @@ -725,19 +756,6 @@ def _run_spatial_neighbors( _save_data(adata, attr="obsp", key=dists_key, data=Dst, prefix=False) _save_data(adata, attr="uns", key=neighs_key, data=neighbors_dict, prefix=False, time=start) - -def _spatial_neighbor( - adata: AnnData, - spatial_key: str = Key.obsm.spatial, - builder: GraphBuilder | None = None, -) -> tuple[csr_matrix, csr_matrix]: - if builder is None: - raise ValueError("No graph builder was provided.") - - coords = adata.obsm[spatial_key] - return builder.build(coords) - - @d.dedent def mask_graph( sdata: SpatialData, From 43cdbb64c280a40e6e32045a54d3e29d4647de0c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 09:49:32 +0000 Subject: [PATCH 026/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index bfc9aeadd..29b52269b 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -756,6 +756,7 @@ def _run_spatial_neighbors( _save_data(adata, attr="obsp", key=dists_key, data=Dst, prefix=False) _save_data(adata, attr="uns", key=neighs_key, data=neighbors_dict, prefix=False, time=start) + @d.dedent def mask_graph( sdata: SpatialData, From e632f74d7dc2bf6a808b1886d8c74124386cb995 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 11:53:09 +0200 Subject: [PATCH 027/131] remove unnecessary abstraction --- src/squidpy/gr/_build.py | 80 ++++++++++------------------------------ 1 file changed, 20 insertions(+), 60 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index bfc9aeadd..20eaf16b9 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -356,66 +356,6 @@ def spatial_neighbors( FutureWarning, stacklevel=2, ) - return _spatial_neighbors( - adata, - spatial_key=spatial_key, - elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, - library_key=library_key, - coord_type=coord_type, - n_neighs=n_neighs, - radius=radius, - delaunay=delaunay, - n_rings=n_rings, - percentile=percentile, - transform=transform, - set_diag=set_diag, - builder=builder, - key_added=key_added, - copy=copy, - ) - - -def _prepare_spatial_neighbors_input( - adata: AnnData | SpatialData, - *, - spatial_key: str, - elements_to_coordinate_systems: dict[str, str] | None, - table_key: str | None, - library_key: str | None, -) -> tuple[AnnData, str | None]: - """Resolve input data and validate the requested spatial basis.""" - adata, library_key = _resolve_spatial_data( - adata, - spatial_key=spatial_key, - elements_to_coordinate_systems=elements_to_coordinate_systems, - table_key=table_key, - library_key=library_key, - ) - _assert_spatial_basis(adata, spatial_key) - return adata, library_key - - -def _spatial_neighbors( - adata: AnnData | SpatialData, - *, - spatial_key: str = Key.obsm.spatial, - elements_to_coordinate_systems: dict[str, str] | None = None, - table_key: str | None = None, - library_key: str | None = None, - coord_type: str | CoordType | None = None, - n_neighs: int | None = None, - radius: float | tuple[float, float] | None = None, - delaunay: bool | None = None, - n_rings: int | None = None, - percentile: float | None = None, - transform: str | Transform | None = None, - set_diag: bool | None = None, - builder: GraphBuilder | None = None, - key_added: str = "spatial", - copy: bool = False, -) -> SpatialNeighborsResult | None: - """Internal implementation of spatial_neighbors (no deprecation warning).""" adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, @@ -461,6 +401,26 @@ def _spatial_neighbors( ) +def _prepare_spatial_neighbors_input( + adata: AnnData | SpatialData, + *, + spatial_key: str, + elements_to_coordinate_systems: dict[str, str] | None, + table_key: str | None, + library_key: str | None, +) -> tuple[AnnData, str | None]: + """Resolve input data and validate the requested spatial basis.""" + adata, library_key = _resolve_spatial_data( + adata, + spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, + library_key=library_key, + ) + _assert_spatial_basis(adata, spatial_key) + return adata, library_key + + @d.dedent def spatial_neighbors_knn( adata: AnnData | SpatialData, From c02686089318fcc79d2a8ba8e623b7993871e586 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 13:04:18 +0200 Subject: [PATCH 028/131] put the warning in resolve graph builder code --- src/squidpy/gr/_build.py | 44 +++++++++++++++------------ src/squidpy/gr/neighbors.py | 12 -------- tests/graph/test_spatial_neighbors.py | 4 +++ 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index b45a8c4c6..4186b5f51 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -75,15 +75,24 @@ def _validate_no_legacy_params(**kwargs: Any) -> None: def _resolve_graph_builder( *, coord_type: str | CoordType | None, - n_neighs: int, + n_neighs: int | None, radius: float | tuple[float, float] | None, - delaunay: bool, - n_rings: int, + delaunay: bool | None, + n_rings: int | None, percentile: float | None, transform: str | Transform | None, - set_diag: bool, + set_diag: bool | None, has_spatial_uns: bool = False, ) -> GraphBuilder: + n_neighs_was_set = n_neighs is not None + n_neighs = 6 if n_neighs is None else n_neighs + delaunay = False if delaunay is None else delaunay + n_rings = 1 if n_rings is None else n_rings + set_diag = False if set_diag is None else set_diag + + assert_positive(n_rings, name="n_rings") + assert_positive(n_neighs, name="n_neighs") + transform = Transform.NONE if transform is None else Transform(transform) if coord_type is None: if radius is not None: @@ -95,23 +104,26 @@ def _resolve_graph_builder( else: coord_type = CoordType(coord_type) - common: dict[str, Any] = { - "n_neighs": n_neighs, - "transform": transform, - "set_diag": set_diag, - } + common: dict[str, Any] = {"transform": transform, "set_diag": set_diag} if coord_type == CoordType.GRID: if percentile is not None: raise ValueError( "`percentile` is not supported for grid coordinates. It only applies to generic (non-grid) graphs." ) - return GridBuilder(**common, n_rings=n_rings, delaunay=delaunay) + return GridBuilder(n_neighs=n_neighs, **common, n_rings=n_rings, delaunay=delaunay) if delaunay: + # below check should be removed once legacy mode spatial_neighbors is deprecated + if n_neighs_was_set: + warnings.warn( + "Parameter `n_neighs` is ignored when `delaunay=True` and will be removed in squidpy v2.0.0.", + FutureWarning, + stacklevel=3, + ) return DelaunayBuilder(**common, radius=radius, percentile=percentile) if radius is not None: - return RadiusBuilder(**common, radius=radius, percentile=percentile) - return KNNBuilder(**common, percentile=percentile) + return RadiusBuilder(n_neighs=n_neighs, **common, radius=radius, percentile=percentile) + return KNNBuilder(n_neighs=n_neighs, **common, percentile=percentile) def _resolve_spatial_data( @@ -376,14 +388,6 @@ def spatial_neighbors( set_diag=set_diag, ) else: - n_neighs = n_neighs if n_neighs is not None else 6 - delaunay = delaunay if delaunay is not None else False - n_rings = n_rings if n_rings is not None else 1 - set_diag = set_diag if set_diag is not None else False - - assert_positive(n_rings, name="n_rings") - assert_positive(n_neighs, name="n_neighs") - builder = _resolve_graph_builder( coord_type=coord_type, n_neighs=n_neighs, diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 9a15ef3b8..800f1059a 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -177,24 +177,12 @@ class DelaunayBuilder(GraphBuilder): def __init__( self, radius: float | tuple[float, float] | None = None, - n_neighs: int | None = None, transform: str | Transform | None = None, set_diag: bool = False, percentile: float | None = None, ) -> None: - if n_neighs is not None: - warnings.warn( - "Parameter `n_neighs` of `DelaunayBuilder` is ignored by " - "Delaunay triangulation and will be removed in squidpy v2.0.0.", - FutureWarning, - stacklevel=2, - ) - else: - n_neighs = 6 - assert_positive(n_neighs, name="n_neighs") super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.radius = radius - self.n_neighs = n_neighs @property def coord_type(self) -> CoordType: diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 39be507cb..5317fe79d 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -295,6 +295,10 @@ def test_delaunay_mode_ignores_scalar_radius(self, non_visium_adata: AnnData): np.testing.assert_array_equal(default.connectivities.toarray(), ignored.connectivities.toarray()) np.testing.assert_allclose(default.distances.toarray(), ignored.distances.toarray()) + def test_delaunay_mode_warns_on_n_neighs(self, non_visium_adata: AnnData): + with pytest.warns(FutureWarning, match=r"Parameter `n_neighs` is ignored when `delaunay=True`"): + spatial_neighbors(non_visium_adata, coord_type="generic", delaunay=True, n_neighs=3, copy=True) + @pytest.mark.parametrize("percentile", [99.0, 95.0]) def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord_type="generic"): result = spatial_neighbors(adata_hne, coord_type=coord_type, copy=True) From 10cc2f0628350ba529cddd4c47d4035b495c37b7 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 13:08:53 +0200 Subject: [PATCH 029/131] remove n_neighs from RadiusBuilder --- src/squidpy/gr/_build.py | 17 ++++++++++------- src/squidpy/gr/neighbors.py | 5 +---- tests/graph/test_spatial_neighbors.py | 6 +++++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 4186b5f51..ca7d0b241 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -122,7 +122,14 @@ def _resolve_graph_builder( ) return DelaunayBuilder(**common, radius=radius, percentile=percentile) if radius is not None: - return RadiusBuilder(n_neighs=n_neighs, **common, radius=radius, percentile=percentile) + # below check should be removed once legacy mode spatial_neighbors is deprecated + if n_neighs_was_set: + warnings.warn( + "Parameter `n_neighs` is ignored when `radius` is set and will be removed in squidpy v2.0.0.", + FutureWarning, + stacklevel=3, + ) + return RadiusBuilder(**common, radius=radius, percentile=percentile) return KNNBuilder(n_neighs=n_neighs, **common, percentile=percentile) @@ -298,6 +305,7 @@ def spatial_neighbors( - Generic radius mode: ``coord_type='generic'``, ``delaunay=False``, ``radius`` set. Uses ``radius`` and builds a radius-based neighbor graph. + ``n_neighs`` is ignored and passing it is deprecated. If ``radius`` is a tuple, the graph is built with the maximum radius and then pruned to the interval ``[min(radius), max(radius)]``. @@ -331,7 +339,7 @@ def spatial_neighbors( A tuple ``radius`` is only used afterward as a pruning interval. A scalar ``radius`` is ignored. - Otherwise, if ``radius`` is set, radius mode is used. - In this mode ``n_neighs`` does not act as a second cutoff. + In this mode ``n_neighs`` is ignored (deprecated). - Otherwise, k-nearest-neighbor mode is used. Grid-specific behavior @@ -490,7 +498,6 @@ def spatial_neighbors_radius( table_key: str | None = None, library_key: str | None = None, radius: float | tuple[float, float] = 1.0, - n_neighs: int = 6, percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool = False, @@ -508,9 +515,6 @@ def spatial_neighbors_radius( radius Neighborhood radius. If a :class:`tuple`, the graph is built with the maximum radius and then pruned to the interval ``[min(radius), max(radius)]``. - n_neighs - Number of nearest neighbors used internally by the radius graph builder. - Defaults to ``6``. %(graph_common_params)s %(copy)s @@ -520,7 +524,6 @@ def spatial_neighbors_radius( """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = RadiusBuilder( - n_neighs=n_neighs, radius=radius, percentile=percentile, transform=transform_enum, diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 800f1059a..175a44b03 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -131,15 +131,12 @@ class RadiusBuilder(GraphBuilder): def __init__( self, radius: float | tuple[float, float], - n_neighs: int = 6, transform: str | Transform | None = None, set_diag: bool = False, percentile: float | None = None, ) -> None: - assert_positive(n_neighs, name="n_neighs") super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.radius = radius - self.n_neighs = n_neighs @property def coord_type(self) -> CoordType: @@ -148,7 +145,7 @@ def coord_type(self) -> CoordType: def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] r = self.radius if isinstance(self.radius, int | float) else max(self.radius) - tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=r, metric="euclidean") + tree = NearestNeighbors(radius=r, metric="euclidean") tree.fit(coords) dists, col_indices = tree.radius_neighbors() diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 5317fe79d..46d5a4a68 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -239,7 +239,7 @@ def test_builder_argument_conflict(self, non_visium_adata: AnnData): spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) def test_builder_rejects_any_legacy_args(self, non_visium_adata: AnnData): - builder = RadiusBuilder(radius=5.0, n_neighs=3, percentile=95.0, transform="cosine", set_diag=True) + builder = RadiusBuilder(radius=5.0, percentile=95.0, transform="cosine", set_diag=True) with pytest.raises(ValueError, match="must not be set"): spatial_neighbors( @@ -299,6 +299,10 @@ def test_delaunay_mode_warns_on_n_neighs(self, non_visium_adata: AnnData): with pytest.warns(FutureWarning, match=r"Parameter `n_neighs` is ignored when `delaunay=True`"): spatial_neighbors(non_visium_adata, coord_type="generic", delaunay=True, n_neighs=3, copy=True) + def test_radius_mode_warns_on_n_neighs(self, non_visium_adata: AnnData): + with pytest.warns(FutureWarning, match=r"Parameter `n_neighs` is ignored when `radius` is set"): + spatial_neighbors(non_visium_adata, coord_type="generic", radius=5.0, n_neighs=3, copy=True) + @pytest.mark.parametrize("percentile", [99.0, 95.0]) def test_percentile_filtering(self, adata_hne: AnnData, percentile: float, coord_type="generic"): result = spatial_neighbors(adata_hne, coord_type=coord_type, copy=True) From 6477f18d7b2a84d03a2eb2f36980f10b2b48c658 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 9 Apr 2026 13:20:49 +0200 Subject: [PATCH 030/131] definition of Adj, Dst and rename to adj, dst --- docs/extensibility.md | 26 ++++++-- src/squidpy/gr/_build.py | 32 +++++----- src/squidpy/gr/neighbors.py | 124 ++++++++++++++++++------------------ 3 files changed, 97 insertions(+), 85 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index 00bf73478..5797804b3 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -11,12 +11,24 @@ You can implement your own strategy by subclassing it. | Method / property | Required | Purpose | |---|---|---| | {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(Adj, Dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-processing on the raw ``Adj``/``Dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | +| {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-processing on the raw ``adj``/``dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | The base class handles percentile filtering, adjacency transforms, and {class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. +Here ``adj`` and ``dst`` are square sparse matrices of shape ``(n_obs, n_obs)`` +with matching sparsity structure: + +- ``adj`` is the connectivity / adjacency matrix. Non-zero entries mark edges in + the graph, and built-in builders typically use ``1.0`` for present edges. +- ``dst`` is the distance matrix for those same edges. For generic graphs this is + usually the Euclidean edge length. For grid builders it may instead encode + graph-distance semantics such as ring number. +- Both should be returned as {class}`~scipy.sparse.csr_matrix`. +- By convention, ``dst`` should have a zero diagonal, and ``adj`` should only + have a non-zero diagonal when ``set_diag=True``. + ### Example: fast radius search with SNN The built-in {class}`~squidpy.gr.neighbors.RadiusBuilder` uses scikit-learn's @@ -56,15 +68,15 @@ class SNNRadiusBuilder(GraphBuilder): col = np.concatenate(indices) d = np.concatenate(dists).astype(np.float64) - Adj = csr_matrix( + adj = csr_matrix( (np.ones(len(row), dtype=np.float32), (row, col)), shape=(N, N), ) - Dst = csr_matrix((d, (row, col)), shape=(N, N)) + dst = csr_matrix((d, (row, col)), shape=(N, N)) - Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) - Dst.setdiag(0.0) - return Adj, Dst + adj.setdiag(1.0 if self.set_diag else adj.diagonal()) + dst.setdiag(0.0) + return adj, dst ``` Use it like any other builder: diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index ca7d0b241..9f71c548c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -696,10 +696,10 @@ def _run_spatial_neighbors( ixs.extend(np.where(adata.obs[library_key] == lib)[0]) mats.append(builder.build(adata[adata.obs[library_key] == lib].obsm[spatial_key])) ixs = cast(list[int], np.argsort(ixs).tolist()) - Adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] - Dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] + adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] + dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] else: - Adj, Dst = builder.build(adata.obsm[spatial_key]) + adj, dst = builder.build(adata.obsm[spatial_key]) neighs_key = Key.uns.spatial_neighs(key_added) conns_key = Key.obsp.spatial_conn(key_added) @@ -717,10 +717,10 @@ def _run_spatial_neighbors( } if copy: - return SpatialNeighborsResult(connectivities=Adj, distances=Dst) + return SpatialNeighborsResult(connectivities=adj, distances=dst) - _save_data(adata, attr="obsp", key=conns_key, data=Adj) - _save_data(adata, attr="obsp", key=dists_key, data=Dst, prefix=False) + _save_data(adata, attr="obsp", key=conns_key, data=adj) + _save_data(adata, attr="obsp", key=dists_key, data=dst, prefix=False) _save_data(adata, attr="uns", key=neighs_key, data=neighbors_dict, prefix=False, time=start) @@ -782,11 +782,11 @@ def mask_graph( # get elements table = sdata.tables[table_key] coords = table.obsm[spatial_key] - Adj = table.obsp[conns_key] - Dst = table.obsp[dists_key] + adj = table.obsp[conns_key] + dst = table.obsp[dists_key] # convert edges to lines - lines_coords, idx_out = _get_lines_coords(Adj.indices, Adj.indptr, coords) + lines_coords, idx_out = _get_lines_coords(adj.indices, adj.indptr, coords) lines_coords, idx_out = np.array(lines_coords), np.array(idx_out) lines_df = gpd.GeoDataFrame(geometry=list(map(LineString, lines_coords))) @@ -800,12 +800,12 @@ def mask_graph( filt_idx_out = idx_out[filt_lines] # filter connectivities - Adj[filt_idx_out[:, 0], filt_idx_out[:, 1]] = 0 - Adj.eliminate_zeros() + adj[filt_idx_out[:, 0], filt_idx_out[:, 1]] = 0 + adj.eliminate_zeros() # filter_distances - Dst[filt_idx_out[:, 0], filt_idx_out[:, 1]] = 0 - Dst.eliminate_zeros() + dst[filt_idx_out[:, 0], filt_idx_out[:, 1]] = 0 + dst.eliminate_zeros() mask_conns_key = f"{key_added}_{conns_key}" mask_dists_key = f"{key_added}_{dists_key}" @@ -822,11 +822,11 @@ def mask_graph( } if copy: - return Adj, Dst + return adj, dst # save back to spatialdata - _save_data(table, attr="obsp", key=mask_conns_key, data=Adj) - _save_data(table, attr="obsp", key=mask_dists_key, data=Dst, prefix=False) + _save_data(table, attr="obsp", key=mask_conns_key, data=adj) + _save_data(table, attr="obsp", key=mask_dists_key, data=dst, prefix=False) _save_data(table, attr="uns", key=mask_neighs_key, data=neighbors_dict, prefix=False) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 175a44b03..9e55229d9 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -53,36 +53,36 @@ def coord_type(self) -> CoordType: def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: with warnings.catch_warnings(): warnings.simplefilter("ignore", SparseEfficiencyWarning) - Adj, Dst = self.build_graph(coords) + adj, dst = self.build_graph(coords) - self.apply_filters(Adj, Dst) - self._apply_percentile(Adj, Dst) - Adj.eliminate_zeros() - Dst.eliminate_zeros() + self.apply_filters(adj, dst) + self._apply_percentile(adj, dst) + adj.eliminate_zeros() + dst.eliminate_zeros() - return self._apply_transform(Adj), Dst + return self._apply_transform(adj), dst @abstractmethod def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: """Construct raw adjacency and distance matrices.""" - def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: """Apply builder-specific post-processing filters.""" return None - def _apply_percentile(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def _apply_percentile(self, adj: csr_matrix, dst: csr_matrix) -> None: if self.percentile is not None and self.coord_type == CoordType.GENERIC: - threshold = np.percentile(Dst.data, self.percentile) - Adj[Dst > threshold] = 0.0 - Dst[Dst > threshold] = 0.0 + threshold = np.percentile(dst.data, self.percentile) + adj[dst > threshold] = 0.0 + dst[dst > threshold] = 0.0 - def _apply_transform(self, Adj: csr_matrix) -> csr_matrix: + def _apply_transform(self, adj: csr_matrix) -> csr_matrix: if self.transform == Transform.SPECTRAL: - return cast(csr_matrix, _transform_a_spectral(Adj)) + return cast(csr_matrix, _transform_a_spectral(adj)) if self.transform == Transform.COSINE: - return cast(csr_matrix, _transform_a_cosine(Adj)) + return cast(csr_matrix, _transform_a_cosine(adj)) if self.transform == Transform.NONE: - return Adj + return adj raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") @@ -114,15 +114,15 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dists, col_indices = dists.reshape(-1), col_indices.reshape(-1) row_indices = np.repeat(np.arange(N), self.n_neighs) - Adj = csr_matrix( + adj = csr_matrix( (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), shape=(N, N), ) - Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) + dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) - Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) - Dst.setdiag(0.0) - return Adj, Dst + adj.setdiag(1.0 if self.set_diag else adj.diagonal()) + dst.setdiag(0.0) + return adj, dst class RadiusBuilder(GraphBuilder): @@ -153,19 +153,19 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dists = np.concatenate(dists) col_indices = np.concatenate(col_indices) - Adj = csr_matrix( + adj = csr_matrix( (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), shape=(N, N), ) - Dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) + dst = csr_matrix((dists, (row_indices, col_indices)), shape=(N, N)) - Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) - Dst.setdiag(0.0) - return Adj, Dst + adj.setdiag(1.0 if self.set_diag else adj.diagonal()) + dst.setdiag(0.0) + return adj, dst - def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) + _filter_by_radius_interval(adj, dst, self.radius) class DelaunayBuilder(GraphBuilder): @@ -189,7 +189,7 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tri = Delaunay(coords) indptr, indices = tri.vertex_neighbor_vertices - Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) + adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) # fmt: off dists = np.array(list(chain(*( @@ -198,15 +198,15 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: if len(indices[indptr[i] : indptr[i + 1]]) )))).squeeze() # fmt: on - Dst = csr_matrix((dists, indices, indptr), shape=(N, N)) + dst = csr_matrix((dists, indices, indptr), shape=(N, N)) - Adj.setdiag(1.0 if self.set_diag else Adj.diagonal()) - Dst.setdiag(0.0) - return Adj, Dst + adj.setdiag(1.0 if self.set_diag else adj.diagonal()) + dst.setdiag(0.0) + return adj, dst - def apply_filters(self, Adj: csr_matrix, Dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: if isinstance(self.radius, Iterable): - _filter_by_radius_interval(Adj, Dst, self.radius) + _filter_by_radius_interval(adj, dst, self.radius) class GridBuilder(GraphBuilder): @@ -233,26 +233,26 @@ def coord_type(self) -> CoordType: def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: if self.n_rings > 1: - Adj = self._base_adjacency(coords, set_diag=True) - Res, Walk = Adj, Adj + adj = self._base_adjacency(coords, set_diag=True) + res, walk = adj, adj for i in range(self.n_rings - 1): - Walk = Walk @ Adj - Walk[Res.nonzero()] = 0.0 - Walk.eliminate_zeros() - Walk.data[:] = i + 2.0 - Res = Res + Walk - Adj = Res - Adj.setdiag(float(self.set_diag)) - Adj.eliminate_zeros() - - Dst = Adj.copy() - Adj.data[:] = 1.0 + walk = walk @ adj + walk[res.nonzero()] = 0.0 + walk.eliminate_zeros() + walk.data[:] = i + 2.0 + res = res + walk + adj = res + adj.setdiag(float(self.set_diag)) + adj.eliminate_zeros() + + dst = adj.copy() + adj.data[:] = 1.0 else: - Adj = self._base_adjacency(coords, set_diag=self.set_diag) - Dst = Adj.copy() + adj = self._base_adjacency(coords, set_diag=self.set_diag) + dst = adj.copy() - Dst.setdiag(0.0) - return Adj, Dst + dst.setdiag(0.0) + return adj, dst def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: """KNN adjacency with median-distance correction for grid coordinates.""" @@ -260,7 +260,7 @@ def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: if self.delaunay: tri = Delaunay(coords) indptr, indices = tri.vertex_neighbor_vertices - Adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) + adj = csr_matrix((np.ones_like(indices, dtype=np.float32), indices, indptr), shape=(N, N)) else: tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") tree.fit(coords) @@ -272,13 +272,13 @@ def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: mask = dists < dist_cutoff row_indices, col_indices = row_indices[mask], col_indices[mask] - Adj = csr_matrix( + adj = csr_matrix( (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), shape=(N, N), ) - Adj.setdiag(1.0 if set_diag else Adj.diagonal()) - return Adj + adj.setdiag(1.0 if set_diag else adj.diagonal()) + return adj # --------------------------------------------------------------------------- @@ -287,17 +287,17 @@ def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: def _filter_by_radius_interval( - Adj: csr_matrix, - Dst: csr_matrix, + adj: csr_matrix, + dst: csr_matrix, radius: Iterable[float], ) -> None: minn, maxx = sorted(radius)[:2] - mask = (Dst.data < minn) | (Dst.data > maxx) - a_diag = Adj.diagonal() + mask = (dst.data < minn) | (dst.data > maxx) + a_diag = adj.diagonal() - Dst.data[mask] = 0.0 - Adj.data[mask] = 0.0 - Adj.setdiag(a_diag) + dst.data[mask] = 0.0 + adj.data[mask] = 0.0 + adj.setdiag(a_diag) @njit From 516b3e024079d8225f9f6297979c32e9230047d3 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 13:48:31 +0200 Subject: [PATCH 031/131] Generalize the base class for extensibility --- docs/extensibility.md | 34 ++++++++++------ src/squidpy/gr/_build.py | 6 +-- src/squidpy/gr/neighbors.py | 77 +++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index 5797804b3..5cdec8ba2 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -2,20 +2,29 @@ ## Custom graph builders -The `squidpy.gr.neighbors` module exposes a {class}`~squidpy.gr.neighbors.GraphBuilder` -base class that all built-in graph construction strategies inherit from. -You can implement your own strategy by subclassing it. +The `squidpy.gr.neighbors` module exposes two builder base classes: + +- {class}`~squidpy.gr.neighbors.GraphBuilder` is the generic builder pipeline. + Use it when you want to plug in a custom coordinate type or sparse-matrix backend. +- {class}`~squidpy.gr.neighbors.GraphBuilderCSR` is the CSR-specialized builder used + by the built-in graph construction strategies. Use it when your builder returns + {class}`~scipy.sparse.csr_matrix` objects and should reuse the default CSR-specific + percentile filtering, transform handling, and sparse warning suppression. ### What to override -| Method / property | Required | Purpose | +| Base class | Method / property | Required | Purpose | |---|---|---| -| {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` as {class}`~scipy.sparse.csr_matrix` pair. | -| {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-processing on the raw ``adj``/``dst`` (e.g. radius-interval pruning). Called before percentile filtering and transform. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` using the coordinate and matrix types of your custom backend. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_percentile` | no | Override percentile handling when the backend needs custom behavior. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_transform` | no | Override transform handling when the backend needs custom behavior. | +| {class}`~squidpy.gr.neighbors.GraphBuilderCSR` | {meth}`~squidpy.gr.neighbors.GraphBuilderCSR.build_graph` | yes | Construct and return ``(adj, dst)`` as a {class}`~scipy.sparse.csr_matrix` pair. | -The base class handles percentile filtering, adjacency transforms, and -{class}`~scipy.sparse.SparseEfficiencyWarning` suppression automatically. +The generic builder only defines the pipeline. The CSR-specialized builder adds the +built-in CSR behavior for percentile filtering, adjacency transforms, and +{class}`~scipy.sparse.SparseEfficiencyWarning` suppression. Here ``adj`` and ``dst`` are square sparse matrices of shape ``(n_obs, n_obs)`` with matching sparsity structure: @@ -25,7 +34,8 @@ with matching sparsity structure: - ``dst`` is the distance matrix for those same edges. For generic graphs this is usually the Euclidean edge length. For grid builders it may instead encode graph-distance semantics such as ring number. -- Both should be returned as {class}`~scipy.sparse.csr_matrix`. +- When subclassing {class}`~squidpy.gr.neighbors.GraphBuilderCSR`, both should be + returned as {class}`~scipy.sparse.csr_matrix`. - By convention, ``dst`` should have a zero diagonal, and ``adj`` should only have a non-zero diagonal when ``set_diag=True``. @@ -43,10 +53,10 @@ from scipy.sparse import csr_matrix from snnpy import build_snn_model from squidpy._constants._constants import CoordType -from squidpy.gr.neighbors import GraphBuilder +from squidpy.gr.neighbors import GraphBuilderCSR -class SNNRadiusBuilder(GraphBuilder): +class SNNRadiusBuilder(GraphBuilderCSR): """Radius graph using the SNN fixed-radius search backend.""" def __init__(self, radius: float, **kwargs): diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 9f71c548c..2ce017b10 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -83,7 +83,7 @@ def _resolve_graph_builder( transform: str | Transform | None, set_diag: bool | None, has_spatial_uns: bool = False, -) -> GraphBuilder: +) -> GraphBuilder[Any, Any]: n_neighs_was_set = n_neighs is not None n_neighs = 6 if n_neighs is None else n_neighs delaunay = False if delaunay is None else delaunay @@ -209,7 +209,7 @@ def spatial_neighbors( percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool | None = None, - builder: GraphBuilder | None = None, + builder: GraphBuilder[Any, Any] | None = None, key_added: str = "spatial", copy: bool = False, ) -> SpatialNeighborsResult | None: @@ -672,7 +672,7 @@ def spatial_neighbors_grid( def _run_spatial_neighbors( adata: AnnData, *, - builder: GraphBuilder, + builder: GraphBuilder[Any, Any], spatial_key: str = Key.obsm.spatial, library_key: str | None = None, key_added: str = "spatial", diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 9e55229d9..283ed4c89 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -9,7 +9,7 @@ from abc import ABC, abstractmethod from collections.abc import Iterable from itertools import chain -from typing import cast +from typing import Generic, TypeVar, cast import numpy as np from fast_array_utils import stats as fau_stats @@ -29,10 +29,14 @@ from squidpy._utils import NDArrayA from squidpy._validators import assert_positive -__all__ = ["GraphBuilder", "KNNBuilder", "RadiusBuilder", "DelaunayBuilder", "GridBuilder"] +__all__ = ["GraphBuilder", "GraphBuilderCSR", "KNNBuilder", "RadiusBuilder", "DelaunayBuilder", "GridBuilder"] -class GraphBuilder(ABC): +CoordT = TypeVar("CoordT") +GraphMatrixT = TypeVar("GraphMatrixT") + + +class GraphBuilder(ABC, Generic[CoordT, GraphMatrixT]): """Base class for spatial graph construction strategies.""" def __init__( @@ -50,44 +54,67 @@ def __init__( def coord_type(self) -> CoordType: """Coordinate system supported by this builder.""" + def build(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: + adj, dst = self.build_graph(coords) + adj, dst = self.apply_filters(adj, dst) + adj, dst = self.apply_percentile(adj, dst) + adj, dst = self.apply_transform(adj, dst) + return adj, dst + + + @abstractmethod + def build_graph(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: + """Construct raw adjacency and distance matrices.""" + + def apply_filters(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: + """Apply builder-specific post-processing filters.""" + return adj, dst + + def apply_percentile(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: + return adj, dst + + def apply_transform(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: + return adj, dst + + +class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): + """CSR-based graph construction strategy.""" + def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: with warnings.catch_warnings(): warnings.simplefilter("ignore", SparseEfficiencyWarning) - adj, dst = self.build_graph(coords) - - self.apply_filters(adj, dst) - self._apply_percentile(adj, dst) - adj.eliminate_zeros() - dst.eliminate_zeros() - - return self._apply_transform(adj), dst + return super().build(coords) @abstractmethod def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: """Construct raw adjacency and distance matrices.""" - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: """Apply builder-specific post-processing filters.""" - return None + return adj, dst - def _apply_percentile(self, adj: csr_matrix, dst: csr_matrix) -> None: + def apply_percentile(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: if self.percentile is not None and self.coord_type == CoordType.GENERIC: threshold = np.percentile(dst.data, self.percentile) adj[dst > threshold] = 0.0 dst[dst > threshold] = 0.0 + return adj, dst + + def apply_transform(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: + adj.eliminate_zeros() + dst.eliminate_zeros() - def _apply_transform(self, adj: csr_matrix) -> csr_matrix: if self.transform == Transform.SPECTRAL: - return cast(csr_matrix, _transform_a_spectral(adj)) + return cast(csr_matrix, _transform_a_spectral(adj)), dst if self.transform == Transform.COSINE: - return cast(csr_matrix, _transform_a_cosine(adj)) + return cast(csr_matrix, _transform_a_cosine(adj)), dst if self.transform == Transform.NONE: - return adj + return adj, dst raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") -class KNNBuilder(GraphBuilder): +class KNNBuilder(GraphBuilderCSR): """Build a generic k-nearest-neighbor spatial graph.""" def __init__( @@ -125,7 +152,7 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: return adj, dst -class RadiusBuilder(GraphBuilder): +class RadiusBuilder(GraphBuilderCSR): """Build a generic radius-based spatial graph.""" def __init__( @@ -163,12 +190,13 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: if isinstance(self.radius, Iterable): _filter_by_radius_interval(adj, dst, self.radius) + return adj, dst -class DelaunayBuilder(GraphBuilder): +class DelaunayBuilder(GraphBuilderCSR): """Build a generic spatial graph from a Delaunay triangulation.""" def __init__( @@ -204,12 +232,13 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> None: + def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: if isinstance(self.radius, Iterable): _filter_by_radius_interval(adj, dst, self.radius) + return adj, dst -class GridBuilder(GraphBuilder): +class GridBuilder(GraphBuilderCSR): """Build a grid-based spatial graph.""" def __init__( From 141fdb690562e84cf7cfeeaacbe59939be2bd610 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:48:44 +0000 Subject: [PATCH 032/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/neighbors.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 283ed4c89..eadac65d2 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -61,7 +61,6 @@ def build(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: adj, dst = self.apply_transform(adj, dst) return adj, dst - @abstractmethod def build_graph(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: """Construct raw adjacency and distance matrices.""" From 1fe316beb12ff04bff4e9127223f24be8db6eed3 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 13:50:55 +0200 Subject: [PATCH 033/131] update docs --- docs/api.md | 1 + docs/extensibility.md | 2 +- src/squidpy/gr/_build.py | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 60434e4be..bf2d39f4e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -126,6 +126,7 @@ See the {doc}`extensibility guide ` for how to implement a custo :toctree: api gr.neighbors.GraphBuilder + gr.neighbors.GraphBuilderCSR gr.neighbors.KNNBuilder gr.neighbors.RadiusBuilder gr.neighbors.DelaunayBuilder diff --git a/docs/extensibility.md b/docs/extensibility.md index 5cdec8ba2..f92e741ec 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -14,7 +14,7 @@ The `squidpy.gr.neighbors` module exposes two builder base classes: ### What to override | Base class | Method / property | Required | Purpose | -|---|---|---| +|---|---|---|---| | {class}`~squidpy.gr.neighbors.GraphBuilder` | {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` using the coordinate and matrix types of your custom backend. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 2ce017b10..a843dfb96 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -286,6 +286,9 @@ def spatial_neighbors( Advanced graph construction strategy. When provided, all other graph-construction arguments (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, ``percentile``, ``transform``, ``set_diag``) must be left as ``None``. + Built-in builders subclass {class}`~squidpy.gr.neighbors.GraphBuilderCSR`, + while custom backends can implement the more generic + {class}`~squidpy.gr.neighbors.GraphBuilder` interface directly. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s From bb1ec50c13ed9b67a889ad2843ea0db853b6d748 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 13:55:32 +0200 Subject: [PATCH 034/131] clarify delauney vs grid --- src/squidpy/gr/_build.py | 5 ++++- src/squidpy/gr/neighbors.py | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index a843dfb96..943a657cd 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -637,7 +637,10 @@ def spatial_neighbors_grid( n_rings Number of rings of neighbors. Defaults to ``1``. delaunay - Whether to compute the grid graph from Delaunay triangulation. + Whether to derive the base grid connectivity from a Delaunay triangulation. + This is still grid mode: unlike :func:`spatial_neighbors_delaunay`, the + resulting distance matrix encodes grid or ring distances rather than + Euclidean edge lengths. %(graph_common_params)s %(copy)s diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index eadac65d2..83a86feb9 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -196,7 +196,11 @@ def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, c class DelaunayBuilder(GraphBuilderCSR): - """Build a generic spatial graph from a Delaunay triangulation.""" + """Build a generic point-cloud graph from a Delaunay triangulation. + + Unlike ``GridBuilder(delaunay=True)``, this builder uses the triangulation as the + graph itself and stores real Euclidean edge lengths in ``dst``. + """ def __init__( self, @@ -238,7 +242,12 @@ def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, c class GridBuilder(GraphBuilderCSR): - """Build a grid-based spatial graph.""" + """Build a grid-based spatial graph. + + When ``delaunay=True``, Delaunay triangulation is used only to derive the base + grid connectivity. The resulting ``dst`` still encodes grid or ring distances, + not geometric Euclidean edge lengths. + """ def __init__( self, From 84cbdc4becc1e54b23df4e2c936fb74b87002a31 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 14:06:29 +0200 Subject: [PATCH 035/131] dtype rec --- docs/extensibility.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/extensibility.md b/docs/extensibility.md index f92e741ec..bfcb41ee6 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -36,6 +36,12 @@ with matching sparsity structure: graph-distance semantics such as ring number. - When subclassing {class}`~squidpy.gr.neighbors.GraphBuilderCSR`, both should be returned as {class}`~scipy.sparse.csr_matrix`. +- For CSR-based builders, ``adj`` often behaves like a boolean or indicator + matrix describing whether an edge is present, even if it is stored with a + numeric dtype such as ``float32``. ``dst`` stores edge-associated values such + as distances and will often use a floating-point dtype. The exact dtype choice + is left to the builder implementation and may depend on performance, memory, + and numerical accuracy requirements. - By convention, ``dst`` should have a zero diagonal, and ``adj`` should only have a non-zero diagonal when ``set_diag=True``. From 6d942ec8dbef664a3e986d53876d6b5a621a0150 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 14:12:42 +0200 Subject: [PATCH 036/131] be a bit more verbose on funcitons --- src/squidpy/gr/_build.py | 47 ++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 943a657cd..ccf83655c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -286,9 +286,9 @@ def spatial_neighbors( Advanced graph construction strategy. When provided, all other graph-construction arguments (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, ``percentile``, ``transform``, ``set_diag``) must be left as ``None``. - Built-in builders subclass {class}`~squidpy.gr.neighbors.GraphBuilderCSR`, + Built-in builders subclass {{class}}`~squidpy.gr.neighbors.GraphBuilderCSR`, while custom backends can implement the more generic - {class}`~squidpy.gr.neighbors.GraphBuilder` interface directly. + {{class}}`~squidpy.gr.neighbors.GraphBuilder` interface directly. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s @@ -453,6 +453,10 @@ def spatial_neighbors_knn( ) -> SpatialNeighborsResult | None: """Create a k-nearest-neighbor graph from spatial coordinates. + Each observation is connected to its ``n_neighs`` nearest observations in + Euclidean space. This mode is typically most useful for continuous + coordinates, where you want to control neighborhood size directly. + Parameters ---------- %(adata)s @@ -460,7 +464,8 @@ def spatial_neighbors_knn( %(sdata_params)s %(library_key)s n_neighs - Number of nearest neighbors. Defaults to ``6``. + Number of nearest neighbors. Defaults to ``6``. Smaller values produce a + sparser, more local graph; larger values connect broader neighborhoods. %(graph_common_params)s %(copy)s @@ -509,6 +514,10 @@ def spatial_neighbors_radius( ) -> SpatialNeighborsResult | None: """Create a radius-based graph from spatial coordinates. + Two observations are connected when their Euclidean distance falls within the + requested radius. This mode is useful when a physical interaction scale is + more meaningful than a fixed number of neighbors. + Parameters ---------- %(adata)s @@ -518,6 +527,9 @@ def spatial_neighbors_radius( radius Neighborhood radius. If a :class:`tuple`, the graph is built with the maximum radius and then pruned to the interval ``[min(radius), max(radius)]``. + In practice, a single value defines a disk around each observation, + whereas a tuple defines an annulus by keeping only edges within the + specified distance interval. %(graph_common_params)s %(copy)s @@ -566,6 +578,11 @@ def spatial_neighbors_delaunay( ) -> SpatialNeighborsResult | None: """Create a Delaunay triangulation graph from spatial coordinates. + Delaunay triangulation connects observations into triangles such that no + other observation lies inside the circumcircle of each triangle. In + practice, this yields an adaptive geometry-driven graph rather than one + based on a fixed ``k`` or radius, and ``dst`` stores Euclidean edge lengths. + Parameters ---------- %(adata)s @@ -574,7 +591,9 @@ def spatial_neighbors_delaunay( %(library_key)s radius If a :class:`tuple`, used as a post-construction pruning interval - ``[min(radius), max(radius)]``. + ``[min(radius), max(radius)]``. This does not change the triangulation + itself; it only removes Delaunay edges whose Euclidean lengths fall + outside the interval. %(graph_common_params)s %(copy)s @@ -625,6 +644,10 @@ def spatial_neighbors_grid( """Create a grid-based graph from spatial coordinates. This is the mode used for Visium-like grid coordinates. + It assumes observations lie on an approximately regular lattice, so it is + usually not appropriate for continuous coordinates such as Xenium point + clouds. On irregular coordinates, the resulting graph and ring distances may + not have a meaningful grid interpretation. Parameters ---------- @@ -633,14 +656,24 @@ def spatial_neighbors_grid( %(sdata_params)s %(library_key)s n_neighs - Number of neighboring tiles. Defaults to ``6``. + Number of neighboring tiles used to form the base grid connectivity. + Defaults to ``6``. On a Visium-like hexagonal grid, ``6`` corresponds to + the immediate surrounding spots, while smaller values such as ``3`` make + the first-ring graph deliberately sparser. n_rings - Number of rings of neighbors. Defaults to ``1``. + Number of rings of neighbors. Defaults to ``1``. ``n_rings=1`` keeps + only immediate neighbors; larger values add progressively more distant + shells and encode the shell number in ``dst``. For example, + ``n_neighs=3`` with ``n_rings=2`` on a Visium-like grid starts from a + sparse three-neighbor base graph and then adds a second graph-distance + ring relative to that base connectivity. delaunay Whether to derive the base grid connectivity from a Delaunay triangulation. This is still grid mode: unlike :func:`spatial_neighbors_delaunay`, the resulting distance matrix encodes grid or ring distances rather than - Euclidean edge lengths. + Euclidean edge lengths. In practice, this changes how the first-ring + connectivity is inferred, but not the meaning of the resulting + distances. %(graph_common_params)s %(copy)s From ae9a4e6727d1245ac6cea68c89671b6f86e7aa0a Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 14:41:24 +0200 Subject: [PATCH 037/131] spatial_neighbours_from_builder --- docs/api.md | 1 + docs/extensibility.md | 4 +- src/squidpy/gr/__init__.py | 2 + src/squidpy/gr/_build.py | 149 ++++++++++++++------------ tests/graph/test_spatial_neighbors.py | 43 ++------ 5 files changed, 92 insertions(+), 107 deletions(-) diff --git a/docs/api.md b/docs/api.md index bf2d39f4e..dd19bb18e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -18,6 +18,7 @@ import squidpy as sq :toctree: api gr.spatial_neighbors + gr.spatial_neighbors_from_builder gr.spatial_neighbors_knn gr.spatial_neighbors_radius gr.spatial_neighbors_delaunay diff --git a/docs/extensibility.md b/docs/extensibility.md index bfcb41ee6..aa2845bf5 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -20,7 +20,7 @@ The `squidpy.gr.neighbors` module exposes two builder base classes: | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_percentile` | no | Override percentile handling when the backend needs custom behavior. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_transform` | no | Override transform handling when the backend needs custom behavior. | -| {class}`~squidpy.gr.neighbors.GraphBuilderCSR` | {meth}`~squidpy.gr.neighbors.GraphBuilderCSR.build_graph` | yes | Construct and return ``(adj, dst)`` as a {class}`~scipy.sparse.csr_matrix` pair. | + The generic builder only defines the pipeline. The CSR-specialized builder adds the built-in CSR behavior for percentile filtering, adjacency transforms, and @@ -100,5 +100,5 @@ Use it like any other builder: ```python import squidpy as sq -sq.gr.spatial_neighbors(adata, builder=SNNRadiusBuilder(radius=100.0)) +sq.gr.spatial_neighbors_from_builder(adata, SNNRadiusBuilder(radius=100.0)) ``` diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index a5dd26973..fce615b66 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -8,6 +8,7 @@ mask_graph, spatial_neighbors, spatial_neighbors_delaunay, + spatial_neighbors_from_builder, spatial_neighbors_grid, spatial_neighbors_knn, spatial_neighbors_radius, @@ -30,6 +31,7 @@ "neighbors", "mask_graph", "spatial_neighbors", + "spatial_neighbors_from_builder", "spatial_neighbors_knn", "spatial_neighbors_radius", "spatial_neighbors_delaunay", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index ccf83655c..516dd5ed8 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -49,6 +49,7 @@ __all__ = [ "SpatialNeighborsResult", "spatial_neighbors", + "spatial_neighbors_from_builder", "spatial_neighbors_knn", "spatial_neighbors_radius", "spatial_neighbors_delaunay", @@ -63,15 +64,6 @@ class SpatialNeighborsResult(NamedTuple): distances: csr_matrix -def _validate_no_legacy_params(**kwargs: Any) -> None: - conflicts = [k for k, v in kwargs.items() if v is not None] - if conflicts: - raise ValueError( - "When `builder` is provided, graph-construction arguments must not be set. " - f"Got non-default values for: {', '.join(conflicts)}." - ) - - def _resolve_graph_builder( *, coord_type: str | CoordType | None, @@ -209,7 +201,6 @@ def spatial_neighbors( percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool | None = None, - builder: GraphBuilder[Any, Any] | None = None, key_added: str = "spatial", copy: bool = False, ) -> SpatialNeighborsResult | None: @@ -217,16 +208,14 @@ def spatial_neighbors( Create a graph from spatial coordinates. .. deprecated:: 1.6.0 - The flat-parameter API of ``spatial_neighbors`` is deprecated and will - be removed in squidpy v1.7.0. Use one of the mode-specific functions - instead: + ``spatial_neighbors`` is deprecated and will be removed in squidpy + v1.7.0. Use one of the mode-specific functions instead: - :func:`spatial_neighbors_knn` - :func:`spatial_neighbors_radius` - :func:`spatial_neighbors_delaunay` - :func:`spatial_neighbors_grid` - - Passing a ``builder`` instance directly remains supported. + - :func:`spatial_neighbors_from_builder` Parameters ---------- @@ -243,8 +232,7 @@ def spatial_neighbors( `adata` is a :class:`spatialdata.SpatialData`. %(library_key)s coord_type - Type of coordinate system. Must not be set when ``builder`` is given. - Valid options are: + Type of coordinate system. Valid options are: - `{c.GRID.s!r}` - grid coordinates. - `{c.GENERIC.s!r}` - generic coordinates. @@ -256,24 +244,23 @@ def spatial_neighbors( - `{c.GRID.s!r}` - number of neighboring tiles. - `{c.GENERIC.s!r}` - number of neighborhoods for non-grid data. Only used when ``delaunay = False``. - Defaults to ``6`` when no ``builder`` is provided. Must not be set when ``builder`` is given. + Defaults to ``6``. radius - Only available when ``coord_type = {c.GENERIC.s!r}``. Must not be set when ``builder`` is given. + Only available when ``coord_type = {c.GENERIC.s!r}``. Depending on the type: - :class:`float` - compute the graph based on neighborhood radius. - :class:`tuple` - prune the final graph to only contain edges in interval `[min(radius), max(radius)]`. delaunay Whether to compute the graph from Delaunay triangulation. Only used when ``coord_type = {c.GENERIC.s!r}``. - Defaults to ``False`` when no ``builder`` is provided. Must not be set when ``builder`` is given. + Defaults to ``False``. n_rings Number of rings of neighbors for grid data. Only used when ``coord_type = {c.GRID.s!r}``. - Defaults to ``1`` when no ``builder`` is provided. Must not be set when ``builder`` is given. + Defaults to ``1``. percentile Percentile of the distances to use as threshold. Only used when ``coord_type = {c.GENERIC.s!r}``. - Must not be set when ``builder`` is given. transform - Type of adjacency matrix transform. Must not be set when ``builder`` is given. + Type of adjacency matrix transform. Valid options are: - `{t.SPECTRAL.s!r}` - spectral transformation of the adjacency matrix. @@ -281,14 +268,7 @@ def spatial_neighbors( - `{t.NONE.v}` - no transformation of the adjacency matrix. set_diag Whether to set the diagonal of the spatial connectivities to `1.0`. - Defaults to ``False`` when no ``builder`` is provided. Must not be set when ``builder`` is given. - builder - Advanced graph construction strategy. When provided, all other graph-construction - arguments (``coord_type``, ``n_neighs``, ``radius``, ``delaunay``, ``n_rings``, - ``percentile``, ``transform``, ``set_diag``) must be left as ``None``. - Built-in builders subclass {{class}}`~squidpy.gr.neighbors.GraphBuilderCSR`, - while custom backends can implement the more generic - {{class}}`~squidpy.gr.neighbors.GraphBuilder` interface directly. + Defaults to ``False``. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s @@ -323,9 +303,6 @@ def spatial_neighbors( - ``percentile`` only affects generic graphs. - ``transform`` and ``set_diag`` apply to all modes. - - If ``builder`` is provided, it determines the mode directly. - All other graph-construction arguments must be left as - ``None``. - By default, observations are not treated as their own neighbors. The distance matrix always has a zero diagonal. The connectivity matrix only gets a nonzero diagonal when @@ -333,7 +310,7 @@ def spatial_neighbors( Argument precedence ------------------- - When ``builder`` is not provided, the mode is resolved as follows: + The mode is resolved as follows: - If ``coord_type`` resolves to ``'grid'``, grid mode is used. In that case ``radius`` is ignored. @@ -370,15 +347,14 @@ def spatial_neighbors( - :attr:`anndata.AnnData.obsp` ``['{{key_added}}_distances']`` - the spatial distances. - :attr:`anndata.AnnData.uns` ``['{{key_added}}']`` - :class:`dict` containing parameters. """ - if builder is None: - warnings.warn( - "Calling `spatial_neighbors` without a `builder` argument is deprecated " - "and will be removed in squidpy v1.7.0. Use one of the mode-specific " - "functions instead: `spatial_neighbors_knn`, `spatial_neighbors_radius`, " - "`spatial_neighbors_delaunay`, or `spatial_neighbors_grid`.", - FutureWarning, - stacklevel=2, - ) + warnings.warn( + "Calling `spatial_neighbors` is deprecated and will be removed in squidpy " + "v1.7.0. Use `spatial_neighbors_knn`, `spatial_neighbors_radius`, " + "`spatial_neighbors_delaunay`, `spatial_neighbors_grid`, or " + "`spatial_neighbors_from_builder` instead.", + FutureWarning, + stacklevel=2, + ) adata, library_key = _prepare_spatial_neighbors_input( adata, spatial_key=spatial_key, @@ -386,36 +362,73 @@ def spatial_neighbors( table_key=table_key, library_key=library_key, ) - - if builder is not None: - _validate_no_legacy_params( - coord_type=coord_type, - n_neighs=n_neighs, - radius=radius, - delaunay=delaunay, - n_rings=n_rings, - percentile=percentile, - transform=transform, - set_diag=set_diag, - ) - else: - builder = _resolve_graph_builder( - coord_type=coord_type, - n_neighs=n_neighs, - radius=radius, - delaunay=delaunay, - n_rings=n_rings, - percentile=percentile, - transform=transform, - set_diag=set_diag, - has_spatial_uns=Key.uns.spatial in adata.uns, - ) + builder = _resolve_graph_builder( + coord_type=coord_type, + n_neighs=n_neighs, + radius=radius, + delaunay=delaunay, + n_rings=n_rings, + percentile=percentile, + transform=transform, + set_diag=set_diag, + has_spatial_uns=Key.uns.spatial in adata.uns, + ) return _run_spatial_neighbors( adata, builder=builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy ) +@d.dedent +def spatial_neighbors_from_builder( + adata: AnnData | SpatialData, + builder: GraphBuilder[Any, Any], + *, + spatial_key: str = Key.obsm.spatial, + elements_to_coordinate_systems: dict[str, str] | None = None, + table_key: str | None = None, + library_key: str | None = None, + key_added: str = "spatial", + copy: bool = False, +) -> SpatialNeighborsResult | None: + """Create a graph from spatial coordinates using an explicit builder instance. + + Parameters + ---------- + %(adata)s + builder + Graph construction strategy to execute. Built-in builders subclass + {{class}}`~squidpy.gr.neighbors.GraphBuilderCSR`, while custom backends + can implement the more generic + {{class}}`~squidpy.gr.neighbors.GraphBuilder` interface directly. + %(spatial_key)s + %(sdata_params)s + %(library_key)s + key_added + Key which controls where the results are saved if ``copy = False``. + %(copy)s + + Returns + ------- + %(spatial_neighbors_returns)s + """ + adata, library_key = _prepare_spatial_neighbors_input( + adata, + spatial_key=spatial_key, + elements_to_coordinate_systems=elements_to_coordinate_systems, + table_key=table_key, + library_key=library_key, + ) + return _run_spatial_neighbors( + adata, + builder=builder, + spatial_key=spatial_key, + library_key=library_key, + key_added=key_added, + copy=copy, + ) + + def _prepare_spatial_neighbors_input( adata: AnnData | SpatialData, *, diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 46d5a4a68..150d211bb 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -12,7 +12,7 @@ from squidpy._constants._constants import Transform from squidpy._constants._pkg_constants import Key -from squidpy.gr import mask_graph, spatial_neighbors +from squidpy.gr import mask_graph, spatial_neighbors, spatial_neighbors_from_builder from squidpy.gr.neighbors import ( DelaunayBuilder, GridBuilder, @@ -214,7 +214,7 @@ def test_builder_module_export(self): ) def test_generic_builder_matches_legacy(self, non_visium_adata: AnnData, legacy_kwargs: dict, builder: object): legacy = spatial_neighbors(non_visium_adata, **legacy_kwargs, copy=True) - result = spatial_neighbors(non_visium_adata, builder=builder, copy=True) + result = spatial_neighbors_from_builder(non_visium_adata, builder=builder, copy=True) np.testing.assert_array_equal(legacy.connectivities.toarray(), result.connectivities.toarray()) np.testing.assert_allclose(legacy.distances.toarray(), result.distances.toarray()) @@ -229,51 +229,20 @@ def test_generic_builder_matches_legacy(self, non_visium_adata: AnnData, legacy_ ) def test_grid_builder_matches_legacy(self, adata_squaregrid: AnnData, legacy_kwargs: dict, builder: object): legacy = spatial_neighbors(adata_squaregrid, **legacy_kwargs, copy=True) - result = spatial_neighbors(adata_squaregrid, builder=builder, copy=True) + result = spatial_neighbors_from_builder(adata_squaregrid, builder=builder, copy=True) np.testing.assert_array_equal(legacy.connectivities.toarray(), result.connectivities.toarray()) np.testing.assert_allclose(legacy.distances.toarray(), result.distances.toarray()) - def test_builder_argument_conflict(self, non_visium_adata: AnnData): - with pytest.raises(ValueError, match="must not be set"): - spatial_neighbors(non_visium_adata, builder=RadiusBuilder(radius=5.0), delaunay=True) - - def test_builder_rejects_any_legacy_args(self, non_visium_adata: AnnData): - builder = RadiusBuilder(radius=5.0, percentile=95.0, transform="cosine", set_diag=True) - - with pytest.raises(ValueError, match="must not be set"): - spatial_neighbors( - non_visium_adata, - builder=builder, - coord_type="generic", - n_neighs=3, - radius=5.0, - percentile=95.0, - transform="cosine", - set_diag=True, - copy=True, - ) - - def test_builder_allows_none_legacy_args(self, non_visium_adata: AnnData): + def test_builder_explicit_entry_point(self, non_visium_adata: AnnData): builder = KNNBuilder(n_neighs=3, transform=Transform.NONE) - baseline = spatial_neighbors(non_visium_adata, builder=builder, copy=True) - matched = spatial_neighbors(non_visium_adata, builder=builder, transform=None, copy=True) + baseline = spatial_neighbors_from_builder(non_visium_adata, builder=builder, copy=True) + matched = spatial_neighbors_from_builder(non_visium_adata, builder=builder, copy=True) np.testing.assert_array_equal(baseline.connectivities.toarray(), matched.connectivities.toarray()) np.testing.assert_allclose(baseline.distances.toarray(), matched.distances.toarray()) - def test_builder_rejects_radius(self, non_visium_adata: AnnData): - builder = RadiusBuilder(radius=(4.0, 2.0)) - - with pytest.raises(ValueError, match="must not be set"): - spatial_neighbors( - non_visium_adata, - builder=builder, - radius=(2.0, 4.0), - copy=True, - ) - def test_grid_mode_ignores_radius(self, adata_squaregrid: AnnData): default = spatial_neighbors(adata_squaregrid, coord_type="grid", n_neighs=4, n_rings=2, copy=True) ignored = spatial_neighbors( From cd3f6fd7ea879bc6dbcc0e6ef154f4e016e1bb2c Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 15:36:35 +0200 Subject: [PATCH 038/131] update docs to refer to each other --- src/squidpy/gr/_build.py | 37 +++++++++++++++++++++++++++ src/squidpy/gr/neighbors.py | 51 +++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 516dd5ed8..bf99398e9 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -393,6 +393,14 @@ def spatial_neighbors_from_builder( ) -> SpatialNeighborsResult | None: """Create a graph from spatial coordinates using an explicit builder instance. + This function is the bridge between the high-level API (e.g., + :func:`spatial_neighbors_knn`, :func:`spatial_neighbors_radius`) and advanced + customization via builder classes. Use this when you need to: + + - Stack or chain builder behaviors + - Pass pre-configured builder instances multiple times + - Implement custom builders (see :doc:`/extensibility`) + Parameters ---------- %(adata)s @@ -411,6 +419,14 @@ def spatial_neighbors_from_builder( Returns ------- %(spatial_neighbors_returns)s + + See Also + -------- + spatial_neighbors_knn : k-nearest-neighbor graphs (wraps :class:`~squidpy.gr.neighbors.KNNBuilder`). + spatial_neighbors_radius : radius-based graphs (wraps :class:`~squidpy.gr.neighbors.RadiusBuilder`). + spatial_neighbors_delaunay : Delaunay triangulation graphs (wraps :class:`~squidpy.gr.neighbors.DelaunayBuilder`). + spatial_neighbors_grid : grid-based graphs (wraps :class:`~squidpy.gr.neighbors.GridBuilder`). + gr.neighbors.GraphBuilder : Base builder interface. Inherit from this or :class:`~squidpy.gr.neighbors.GraphBuilderCSR` to implement custom graph construction. """ adata, library_key = _prepare_spatial_neighbors_input( adata, @@ -485,6 +501,11 @@ def spatial_neighbors_knn( Returns ------- %(spatial_neighbors_returns)s + + See Also + -------- + spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.KNNBuilder` directly for advanced customization. + gr.neighbors.KNNBuilder : k-nearest-neighbor builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = KNNBuilder( @@ -549,6 +570,11 @@ def spatial_neighbors_radius( Returns ------- %(spatial_neighbors_returns)s + + See Also + -------- + spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.RadiusBuilder` directly for advanced customization. + gr.neighbors.RadiusBuilder : radius-based builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = RadiusBuilder( @@ -613,6 +639,11 @@ def spatial_neighbors_delaunay( Returns ------- %(spatial_neighbors_returns)s + + See Also + -------- + spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.DelaunayBuilder` directly for advanced customization. + gr.neighbors.DelaunayBuilder : Delaunay triangulation builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = DelaunayBuilder( @@ -693,6 +724,11 @@ def spatial_neighbors_grid( Returns ------- %(spatial_neighbors_returns)s + + See Also + -------- + spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.GridBuilder` directly for advanced customization. + gr.neighbors.GridBuilder : grid-based builder class. """ assert_positive(n_rings, name="n_rings") assert_positive(n_neighs, name="n_neighs") @@ -774,6 +810,7 @@ def _run_spatial_neighbors( _save_data(adata, attr="obsp", key=conns_key, data=adj) _save_data(adata, attr="obsp", key=dists_key, data=dst, prefix=False) _save_data(adata, attr="uns", key=neighs_key, data=neighbors_dict, prefix=False, time=start) + return None @d.dedent diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 83a86feb9..2a1e80605 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -77,7 +77,22 @@ def apply_transform(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMa class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): - """CSR-based graph construction strategy.""" + """CSR-based graph construction strategy. + + Specializes :class:`GraphBuilder` for sparse CSR matrix output. Adds built-in handling + for percentile-based edge pruning, adjacency transforms (spectral/cosine), and + SparseEfficiencyWarning suppression. All built-in concrete builders + (:class:`KNNBuilder`, :class:`RadiusBuilder`, :class:`DelaunayBuilder`, :class:`GridBuilder`) + inherit from this class. + + Subclass this (not the generic :class:`GraphBuilder`) when implementing a builder + that returns CSR matrices. + + See Also + -------- + GraphBuilder : Generic builder interface for custom coordinate/matrix types. + KNNBuilder : Example of a concrete CSR-based builder. + """ def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: with warnings.catch_warnings(): @@ -114,7 +129,12 @@ def apply_transform(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, class KNNBuilder(GraphBuilderCSR): - """Build a generic k-nearest-neighbor spatial graph.""" + """Build a generic k-nearest-neighbor spatial graph. + + Each observation is connected to its k nearest neighbors. See + :func:`~squidpy.gr.spatial_neighbors_knn` for the user-facing API or + :func:`~squidpy.gr.spatial_neighbors_from_builder` for direct builder usage. + """ def __init__( self, @@ -152,7 +172,13 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: class RadiusBuilder(GraphBuilderCSR): - """Build a generic radius-based spatial graph.""" + """Build a generic radius-based spatial graph. + + Two observations are connected when their Euclidean distance falls within + the specified radius. See :func:`~squidpy.gr.spatial_neighbors_radius` for the + user-facing API or :func:`~squidpy.gr.spatial_neighbors_from_builder` for + direct builder usage. + """ def __init__( self, @@ -198,8 +224,13 @@ def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, c class DelaunayBuilder(GraphBuilderCSR): """Build a generic point-cloud graph from a Delaunay triangulation. - Unlike ``GridBuilder(delaunay=True)``, this builder uses the triangulation as the - graph itself and stores real Euclidean edge lengths in ``dst``. + Delaunay triangulation connects observations into triangles such that no + other observation lies inside the circumcircle of each triangle. Unlike + ``GridBuilder(delaunay=True)``, this builder uses geometry-based connectivity + and stores real Euclidean edge lengths. + + See :func:`~squidpy.gr.spatial_neighbors_delaunay` for the user-facing API or + :func:`~squidpy.gr.spatial_neighbors_from_builder` for direct builder usage. """ def __init__( @@ -244,9 +275,13 @@ def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, c class GridBuilder(GraphBuilderCSR): """Build a grid-based spatial graph. - When ``delaunay=True``, Delaunay triangulation is used only to derive the base - grid connectivity. The resulting ``dst`` still encodes grid or ring distances, - not geometric Euclidean edge lengths. + Assumes observations lie on an approximately regular lattice (e.g., Visium). + When ``delaunay=True``, Delaunay triangulation is used only to derive the + base connectivity; the distance matrix still encodes grid/ring distances, + not Euclidean lengths. + + See :func:`~squidpy.gr.spatial_neighbors_grid` for the user-facing API or + :func:`~squidpy.gr.spatial_neighbors_from_builder` for direct builder usage. """ def __init__( From 95012dc54ec158044d06fbf8bf54bdec2e53cec5 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Fri, 10 Apr 2026 15:40:23 +0200 Subject: [PATCH 039/131] refer to the classes properly --- src/squidpy/gr/_build.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index bf99398e9..3d1df5c9a 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -426,7 +426,7 @@ def spatial_neighbors_from_builder( spatial_neighbors_radius : radius-based graphs (wraps :class:`~squidpy.gr.neighbors.RadiusBuilder`). spatial_neighbors_delaunay : Delaunay triangulation graphs (wraps :class:`~squidpy.gr.neighbors.DelaunayBuilder`). spatial_neighbors_grid : grid-based graphs (wraps :class:`~squidpy.gr.neighbors.GridBuilder`). - gr.neighbors.GraphBuilder : Base builder interface. Inherit from this or :class:`~squidpy.gr.neighbors.GraphBuilderCSR` to implement custom graph construction. + squidpy.gr.neighbors.GraphBuilder : Base builder interface. Inherit from this or :class:`~squidpy.gr.neighbors.GraphBuilderCSR` to implement custom graph construction. """ adata, library_key = _prepare_spatial_neighbors_input( adata, @@ -505,7 +505,7 @@ def spatial_neighbors_knn( See Also -------- spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.KNNBuilder` directly for advanced customization. - gr.neighbors.KNNBuilder : k-nearest-neighbor builder class. + squidpy.gr.neighbors.KNNBuilder : k-nearest-neighbor builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = KNNBuilder( @@ -574,7 +574,7 @@ def spatial_neighbors_radius( See Also -------- spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.RadiusBuilder` directly for advanced customization. - gr.neighbors.RadiusBuilder : radius-based builder class. + squidpy.gr.neighbors.RadiusBuilder : radius-based builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = RadiusBuilder( @@ -643,7 +643,7 @@ def spatial_neighbors_delaunay( See Also -------- spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.DelaunayBuilder` directly for advanced customization. - gr.neighbors.DelaunayBuilder : Delaunay triangulation builder class. + squidpy.gr.neighbors.DelaunayBuilder : Delaunay triangulation builder class. """ transform_enum = Transform.NONE if transform is None else Transform(transform) builder = DelaunayBuilder( @@ -728,7 +728,7 @@ def spatial_neighbors_grid( See Also -------- spatial_neighbors_from_builder : Use :class:`~squidpy.gr.neighbors.GridBuilder` directly for advanced customization. - gr.neighbors.GridBuilder : grid-based builder class. + squidpy.gr.neighbors.GridBuilder : grid-based builder class. """ assert_positive(n_rings, name="n_rings") assert_positive(n_neighs, name="n_neighs") From 5a02b5958eae9d85aee489caa71d242950dd5053 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 07:46:25 +0200 Subject: [PATCH 040/131] mark as TODO's --- src/squidpy/gr/_build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 3d1df5c9a..b50b5262b 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -105,7 +105,7 @@ def _resolve_graph_builder( ) return GridBuilder(n_neighs=n_neighs, **common, n_rings=n_rings, delaunay=delaunay) if delaunay: - # below check should be removed once legacy mode spatial_neighbors is deprecated + # TODO: below check should be removed once legacy mode spatial_neighbors is deprecated if n_neighs_was_set: warnings.warn( "Parameter `n_neighs` is ignored when `delaunay=True` and will be removed in squidpy v2.0.0.", @@ -114,7 +114,7 @@ def _resolve_graph_builder( ) return DelaunayBuilder(**common, radius=radius, percentile=percentile) if radius is not None: - # below check should be removed once legacy mode spatial_neighbors is deprecated + # TODO: below check should be removed once legacy mode spatial_neighbors is deprecated if n_neighs_was_set: warnings.warn( "Parameter `n_neighs` is ignored when `radius` is set and will be removed in squidpy v2.0.0.", From 3af98ccd23a4adea348b88f317d7016b5aa6f18b Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 07:55:40 +0200 Subject: [PATCH 041/131] rename sugg --- src/squidpy/gr/_build.py | 51 +++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index b50b5262b..3da4cf9a5 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -126,7 +126,7 @@ def _resolve_graph_builder( def _resolve_spatial_data( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str, elements_to_coordinate_systems: dict[str, str] | None, @@ -134,20 +134,21 @@ def _resolve_spatial_data( library_key: str | None, ) -> tuple[AnnData, str | None]: """Resolve SpatialData to AnnData, returning (adata, library_key).""" - if isinstance(adata, SpatialData): + if isinstance(data, SpatialData): + sdata = data assert elements_to_coordinate_systems is not None, ( - "Since `adata` is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." + "Since `data` is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." ) assert table_key is not None, ( - "Since `adata` is a :class:`spatialdata.SpatialData`, `table_key` must not be `None`." + "Since `data` is a :class:`spatialdata.SpatialData`, `table_key` must not be `None`." ) - elements, table = match_element_to_table(adata, list(elements_to_coordinate_systems), table_key) - assert table.obs_names.equals(adata.tables[table_key].obs_names), ( + elements, table = match_element_to_table(sdata, list(elements_to_coordinate_systems), table_key) + assert table.obs_names.equals(sdata.tables[table_key].obs_names), ( "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." ) - regions, region_key, instance_key = get_table_keys(adata.tables[table_key]) + regions, region_key, instance_key = get_table_keys(sdata.tables[table_key]) regions = [regions] if isinstance(regions, str) else regions - ordered_regions_in_table = adata.tables[table_key].obs[region_key].unique() + ordered_regions_in_table = sdata.tables[table_key].obs[region_key].unique() # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 remove_centroids = {} @@ -163,7 +164,7 @@ def _resolve_spatial_data( elem_instances.append(element_instances) element_instances = pd.concat(elem_instances) - if (not np.all(element_instances.values == adata.tables[table_key].obs[instance_key].values)) or ( + if (not np.all(element_instances.values == sdata.tables[table_key].obs[instance_key].values)) or ( not np.all(ordered_regions_in_table == regions) ): raise ValueError( @@ -172,16 +173,18 @@ def _resolve_spatial_data( centroids = [] for region_ in ordered_regions_in_table: cs = elements_to_coordinate_systems[region_] - centroid = get_centroids(adata[region_], coordinate_system=cs)[["x", "y"]].compute() + centroid = get_centroids(sdata[region_], coordinate_system=cs)[["x", "y"]].compute() # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 if remove_centroids[region_]: centroid = centroid[1:].copy() centroids.append(centroid) - adata.tables[table_key].obsm[spatial_key] = np.concatenate(centroids) - adata = adata.tables[table_key] + sdata.tables[table_key].obsm[spatial_key] = np.concatenate(centroids) + adata = sdata.tables[table_key] library_key = region_key + else: + adata = data return adata, library_key @@ -381,7 +384,7 @@ def spatial_neighbors( @d.dedent def spatial_neighbors_from_builder( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, builder: GraphBuilder[Any, Any], *, spatial_key: str = Key.obsm.spatial, @@ -429,7 +432,7 @@ def spatial_neighbors_from_builder( squidpy.gr.neighbors.GraphBuilder : Base builder interface. Inherit from this or :class:`~squidpy.gr.neighbors.GraphBuilderCSR` to implement custom graph construction. """ adata, library_key = _prepare_spatial_neighbors_input( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, @@ -446,7 +449,7 @@ def spatial_neighbors_from_builder( def _prepare_spatial_neighbors_input( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str, elements_to_coordinate_systems: dict[str, str] | None, @@ -455,7 +458,7 @@ def _prepare_spatial_neighbors_input( ) -> tuple[AnnData, str | None]: """Resolve input data and validate the requested spatial basis.""" adata, library_key = _resolve_spatial_data( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, @@ -467,7 +470,7 @@ def _prepare_spatial_neighbors_input( @d.dedent def spatial_neighbors_knn( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, @@ -515,7 +518,7 @@ def spatial_neighbors_knn( set_diag=set_diag, ) adata, library_key = _prepare_spatial_neighbors_input( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, @@ -533,7 +536,7 @@ def spatial_neighbors_knn( @d.dedent def spatial_neighbors_radius( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, @@ -584,7 +587,7 @@ def spatial_neighbors_radius( set_diag=set_diag, ) adata, library_key = _prepare_spatial_neighbors_input( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, @@ -602,7 +605,7 @@ def spatial_neighbors_radius( @d.dedent def spatial_neighbors_delaunay( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, @@ -653,7 +656,7 @@ def spatial_neighbors_delaunay( set_diag=set_diag, ) adata, library_key = _prepare_spatial_neighbors_input( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, @@ -671,7 +674,7 @@ def spatial_neighbors_delaunay( @d.dedent def spatial_neighbors_grid( - adata: AnnData | SpatialData, + data: AnnData | SpatialData, *, spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, @@ -741,7 +744,7 @@ def spatial_neighbors_grid( set_diag=set_diag, ) adata, library_key = _prepare_spatial_neighbors_input( - adata, + data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, table_key=table_key, From e8a19d7ef4209fef188b87013c9d0201fdbe7468 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 08:39:47 +0200 Subject: [PATCH 042/131] add combine into API --- src/squidpy/gr/_build.py | 22 +++++++++------------- src/squidpy/gr/neighbors.py | 22 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 3da4cf9a5..e006fb7d2 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,7 +3,7 @@ from __future__ import annotations import warnings -from typing import Any, NamedTuple, cast +from typing import Any, Generic, NamedTuple, TypeVar import geopandas as gpd import numpy as np @@ -11,11 +11,6 @@ from anndata import AnnData from anndata.utils import make_index_unique from numba import njit -from scipy.sparse import ( - block_diag, - csr_matrix, - spmatrix, -) from shapely import LineString, MultiPolygon, Polygon from spatialdata import SpatialData from spatialdata._core.centroids import get_centroids @@ -57,11 +52,14 @@ ] -class SpatialNeighborsResult(NamedTuple): +GraphMatrixT = TypeVar("GraphMatrixT") + + +class SpatialNeighborsResult(NamedTuple, Generic[GraphMatrixT]): """Result of spatial_neighbors function.""" - connectivities: csr_matrix - distances: csr_matrix + connectivities: GraphMatrixT + distances: GraphMatrixT def _resolve_graph_builder( @@ -781,14 +779,12 @@ def _run_spatial_neighbors( f"Creating graph using `{builder.coord_type}` coordinates and `{builder.transform}` transform and `{len(libs)}` libraries." ) if library_key is not None: - mats: list[tuple[spmatrix, spmatrix]] = [] + mats: list[tuple[Any, Any]] = [] ixs: list[int] = [] for lib in libs: ixs.extend(np.where(adata.obs[library_key] == lib)[0]) mats.append(builder.build(adata[adata.obs[library_key] == lib].obsm[spatial_key])) - ixs = cast(list[int], np.argsort(ixs).tolist()) - adj = block_diag([m[0] for m in mats], format="csr")[ixs, :][:, ixs] - dst = block_diag([m[1] for m in mats], format="csr")[ixs, :][:, ixs] + adj, dst = builder.combine(mats, ixs) else: adj, dst = builder.build(adata.obsm[spatial_key]) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 2a1e80605..eee55b61d 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -7,7 +7,7 @@ import warnings from abc import ABC, abstractmethod -from collections.abc import Iterable +from collections.abc import Iterable, Sequence from itertools import chain from typing import Generic, TypeVar, cast @@ -16,6 +16,7 @@ from numba import njit, prange from scipy.sparse import ( SparseEfficiencyWarning, + block_diag, csr_array, csr_matrix, isspmatrix_csr, @@ -75,6 +76,15 @@ def apply_percentile(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphM def apply_transform(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: return adj, dst + def combine( + self, + mats: Sequence[tuple[GraphMatrixT, GraphMatrixT]], + ixs: Sequence[int], + ) -> tuple[GraphMatrixT, GraphMatrixT]: + raise NotImplementedError( + "Using `library_key` with this graph builder is not implemented yet." + ) + class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): """CSR-based graph construction strategy. @@ -127,6 +137,16 @@ def apply_transform(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") + def combine( + self, + mats: Sequence[tuple[csr_matrix, csr_matrix]], + ixs: Sequence[int], + ) -> tuple[csr_matrix, csr_matrix]: + order = cast(list[int], np.argsort(ixs).tolist()) + adj = block_diag([m[0] for m in mats], format="csr")[order, :][:, order] + dst = block_diag([m[1] for m in mats], format="csr")[order, :][:, order] + return cast(csr_matrix, adj), cast(csr_matrix, dst) + class KNNBuilder(GraphBuilderCSR): """Build a generic k-nearest-neighbor spatial graph. From ac682a3c12e1ce897c51641c449918d8043a1f40 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 06:40:28 +0000 Subject: [PATCH 043/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/neighbors.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index eee55b61d..b6c404548 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -81,9 +81,7 @@ def combine( mats: Sequence[tuple[GraphMatrixT, GraphMatrixT]], ixs: Sequence[int], ) -> tuple[GraphMatrixT, GraphMatrixT]: - raise NotImplementedError( - "Using `library_key` with this graph builder is not implemented yet." - ) + raise NotImplementedError("Using `library_key` with this graph builder is not implemented yet.") class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): From 0f3329cf93325e9b91e94ffdfc8a0f22f4b7ad92 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 08:43:30 +0200 Subject: [PATCH 044/131] update docs --- docs/extensibility.md | 6 ++++-- src/squidpy/gr/_build.py | 3 +++ src/squidpy/gr/neighbors.py | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index aa2845bf5..798d8c714 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -20,10 +20,12 @@ The `squidpy.gr.neighbors` module exposes two builder base classes: | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_percentile` | no | Override percentile handling when the backend needs custom behavior. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_transform` | no | Override transform handling when the backend needs custom behavior. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.combine` | no | Combine per-library results when using ``library_key``. If you do not need ``library_key`` support, leaving this unimplemented is fine. | -The generic builder only defines the pipeline. The CSR-specialized builder adds the -built-in CSR behavior for percentile filtering, adjacency transforms, and +The generic builder only defines the pipeline. The CSR-specialized builder adds +the built-in CSR behavior for percentile filtering, adjacency transforms, +multi-library ``library_key`` combination, and {class}`~scipy.sparse.SparseEfficiencyWarning` suppression. Here ``adj`` and ``dst`` are square sparse matrices of shape ``(n_obs, n_obs)`` diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index e006fb7d2..25dfd0826 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -410,6 +410,9 @@ def spatial_neighbors_from_builder( {{class}}`~squidpy.gr.neighbors.GraphBuilderCSR`, while custom backends can implement the more generic {{class}}`~squidpy.gr.neighbors.GraphBuilder` interface directly. + Custom builders only need to implement multi-library support when using + ``library_key``; otherwise leaving + :meth:`~squidpy.gr.neighbors.GraphBuilder.combine` unimplemented is fine. %(spatial_key)s %(sdata_params)s %(library_key)s diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index b6c404548..94724a010 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -38,7 +38,12 @@ class GraphBuilder(ABC, Generic[CoordT, GraphMatrixT]): - """Base class for spatial graph construction strategies.""" + """Base class for spatial graph construction strategies. + + Custom builders must implement :meth:`build_graph`. Overriding + :meth:`combine` is optional and only needed to support multi-library graph + construction via ``library_key``. + """ def __init__( self, @@ -81,15 +86,23 @@ def combine( mats: Sequence[tuple[GraphMatrixT, GraphMatrixT]], ixs: Sequence[int], ) -> tuple[GraphMatrixT, GraphMatrixT]: - raise NotImplementedError("Using `library_key` with this graph builder is not implemented yet.") + """Combine per-library results into a single graph. + + Override this only if the builder should support multi-library graph + construction via ``library_key``. + """ + raise NotImplementedError( + "Using `library_key` with this graph builder is not implemented yet." + ) class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): """CSR-based graph construction strategy. Specializes :class:`GraphBuilder` for sparse CSR matrix output. Adds built-in handling - for percentile-based edge pruning, adjacency transforms (spectral/cosine), and - SparseEfficiencyWarning suppression. All built-in concrete builders + for percentile-based edge pruning, adjacency transforms (spectral/cosine), + SparseEfficiencyWarning suppression, and multi-library ``library_key`` + combination. All built-in concrete builders (:class:`KNNBuilder`, :class:`RadiusBuilder`, :class:`DelaunayBuilder`, :class:`GridBuilder`) inherit from this class. From 00ec2b068525aabadaab5d04adb43f5725138f74 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 06:46:13 +0000 Subject: [PATCH 045/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/neighbors.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 94724a010..b5f2aad16 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -91,9 +91,7 @@ def combine( Override this only if the builder should support multi-library graph construction via ``library_key``. """ - raise NotImplementedError( - "Using `library_key` with this graph builder is not implemented yet." - ) + raise NotImplementedError("Using `library_key` with this graph builder is not implemented yet.") class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): From 042f749c37d9c8d39eb0caef6b51a2f07c3a7e28 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:51:22 +0200 Subject: [PATCH 046/131] [pre-commit.ci] pre-commit autoupdate (#1149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/biomejs/pre-commit: v2.4.9 → v2.4.10](https://github.com/biomejs/pre-commit/compare/v2.4.9...v2.4.10) - [github.com/tox-dev/pyproject-fmt: v2.20.0 → v2.21.0](https://github.com/tox-dev/pyproject-fmt/compare/v2.20.0...v2.21.0) - [github.com/astral-sh/ruff-pre-commit: v0.15.8 → v0.15.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.8...v0.15.9) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- pyproject.toml | 42 ++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 118cb1b8a..401c82b0f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,16 +7,16 @@ default_stages: minimum_pre_commit_version: 2.16.0 repos: - repo: https://github.com/biomejs/pre-commit - rev: v2.4.9 + rev: v2.4.10 hooks: - id: biome-format exclude: ^\.cruft\.json$ # inconsistent indentation with cruft - file never to be modified manually. - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.20.0 + rev: v2.21.0 hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.8 + rev: v0.15.9 hooks: - id: ruff-check types_or: [python, pyi, jupyter] diff --git a/pyproject.toml b/pyproject.toml index 1e264c809..06e9dfc5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,6 +133,27 @@ build.targets.wheel.packages = [ "src/squidpy" ] metadata.allow-direct-references = true version.source = "vcs" +[tool.pixi] +workspace.channels = [ "conda-forge" ] +workspace.platforms = [ "linux-64", "osx-arm64" ] +dependencies.python = ">=3.11" +pypi-dependencies.squidpy = { path = ".", editable = true } +tasks.format = "ruff format ." +tasks.kernel-install = 'python -m ipykernel install --user --name pixi-dev --display-name "squidpy (dev)"' +tasks.lab = "jupyter lab" +tasks.lint = "ruff check ." +tasks.pre-commit = "pre-commit run" +tasks.pre-commit-install = "pre-commit install" +tasks.test = "pytest -v --color=yes --tb=short --durations=10" +feature.py311.dependencies.python = "3.11.*" +feature.py313.dependencies.python = "3.13.*" +environments.default = { features = [ "py313" ], solve-group = "py313" } +environments.dev-py311 = { features = [ "dev", "test", "py311" ], solve-group = "py311" } +environments.dev-py313 = { features = [ "dev", "test", "py313" ], solve-group = "py313" } +environments.docs-py311 = { features = [ "docs", "py311" ], solve-group = "py311" } +environments.docs-py313 = { features = [ "docs", "py313" ], solve-group = "py313" } +environments.test-py313 = { features = [ "test", "py313" ], solve-group = "py313" } + [tool.ruff] line-length = 120 exclude = [ @@ -278,24 +299,3 @@ skip = [ "docs/references.md", "docs/notebooks/example.ipynb", ] - -[tool.pixi] -dependencies.python = ">=3.11" -environments.dev-py311 = { features = [ "dev", "test", "py311" ], solve-group = "py311" } -environments.docs-py311 = { features = [ "docs", "py311" ], solve-group = "py311" } -environments.default = { features = [ "py313" ], solve-group = "py313" } -environments.dev-py313 = { features = [ "dev", "test", "py313" ], solve-group = "py313" } -environments.docs-py313 = { features = [ "docs", "py313" ], solve-group = "py313" } -environments.test-py313 = { features = [ "test", "py313" ], solve-group = "py313" } -feature.py311.dependencies.python = "3.11.*" -feature.py313.dependencies.python = "3.13.*" -pypi-dependencies.squidpy = { path = ".", editable = true } -tasks.lab = "jupyter lab" -tasks.kernel-install = 'python -m ipykernel install --user --name pixi-dev --display-name "squidpy (dev)"' -tasks.test = "pytest -v --color=yes --tb=short --durations=10" -tasks.lint = "ruff check ." -tasks.format = "ruff format ." -tasks.pre-commit-install = "pre-commit install" -tasks.pre-commit = "pre-commit run" -workspace.channels = [ "conda-forge" ] -workspace.platforms = [ "osx-arm64", "linux-64" ] From 1a87841815549635e10e55c27e52dd8db33fa059 Mon Sep 17 00:00:00 2001 From: Tim Treis Date: Fri, 10 Apr 2026 21:33:40 +0000 Subject: [PATCH 047/131] Functions to QC histopathology images (#1036) --- src/squidpy/experimental/im/__init__.py | 4 + src/squidpy/experimental/im/_detect_tissue.py | 6 +- .../experimental/im/_intensity_metrics.py | 134 ++++++ src/squidpy/experimental/im/_make_tiles.py | 175 +------ src/squidpy/experimental/im/_qc_image.py | 438 ++++++++++++++++++ src/squidpy/experimental/im/_qc_metrics.py | 98 ++++ .../experimental/im/_sharpness_metrics.py | 94 ++++ src/squidpy/experimental/im/_utils.py | 199 +++++++- src/squidpy/experimental/pl/__init__.py | 4 +- src/squidpy/experimental/pl/_qc_image.py | 199 ++++++++ tests/_images/QCImage_calc_qc_image_hne.png | Bin 0 -> 21230 bytes .../_images/QCImage_calc_qc_image_not_hne.png | Bin 0 -> 21063 bytes tests/_images/QCImage_plot_qc_image.png | Bin 0 -> 17741 bytes .../_images/QCSharpness_calc_qc_sharpness.png | Bin 19379 -> 0 bytes .../_images/QCSharpness_plot_qc_sharpness.png | Bin 30929 -> 0 bytes tests/experimental/test_qc_image.py | 216 +++++++++ 16 files changed, 1406 insertions(+), 161 deletions(-) create mode 100644 src/squidpy/experimental/im/_intensity_metrics.py create mode 100644 src/squidpy/experimental/im/_qc_image.py create mode 100644 src/squidpy/experimental/im/_qc_metrics.py create mode 100644 src/squidpy/experimental/im/_sharpness_metrics.py create mode 100644 src/squidpy/experimental/pl/_qc_image.py create mode 100644 tests/_images/QCImage_calc_qc_image_hne.png create mode 100644 tests/_images/QCImage_calc_qc_image_not_hne.png create mode 100644 tests/_images/QCImage_plot_qc_image.png delete mode 100644 tests/_images/QCSharpness_calc_qc_sharpness.png delete mode 100644 tests/_images/QCSharpness_plot_qc_sharpness.png create mode 100644 tests/experimental/test_qc_image.py diff --git a/src/squidpy/experimental/im/__init__.py b/src/squidpy/experimental/im/__init__.py index b88006688..24f7cf6ee 100644 --- a/src/squidpy/experimental/im/__init__.py +++ b/src/squidpy/experimental/im/__init__.py @@ -7,12 +7,16 @@ detect_tissue, ) from ._make_tiles import make_tiles, make_tiles_from_spots +from ._qc_image import qc_image +from ._qc_metrics import QCMetric __all__ = [ "BackgroundDetectionParams", "FelzenszwalbParams", + "QCMetric", "WekaParams", "detect_tissue", "make_tiles", "make_tiles_from_spots", + "qc_image", ] diff --git a/src/squidpy/experimental/im/_detect_tissue.py b/src/squidpy/experimental/im/_detect_tissue.py index 4b8bdc98a..c8aa34590 100644 --- a/src/squidpy/experimental/im/_detect_tissue.py +++ b/src/squidpy/experimental/im/_detect_tissue.py @@ -24,7 +24,7 @@ from squidpy._utils import _ensure_dim_order, _get_scale_factors, _yx_from_shape -from ._utils import _flatten_channels, _get_element_data +from ._utils import flatten_channels, get_element_data class DetectTissueMethod(enum.Enum): @@ -357,7 +357,7 @@ def detect_tissue( # Load smallest available or explicit scale img_node = sdata.images[image_key] - img_da = _get_element_data(img_node, scale if manual_scale else "auto", "image", image_key) + img_da = get_element_data(img_node, scale if manual_scale else "auto", "image", image_key) img_src = _ensure_dim_order(img_da, "yxc") src_h = int(img_src.sizes["y"]) src_w = int(img_src.sizes["x"]) @@ -375,7 +375,7 @@ def detect_tissue( # Channel flattening (greyscale) for threshold-based methods img_grey = None if method != DetectTissueMethod.WEKA: - img_grey_da: xr.DataArray = _flatten_channels(img=img_src, channel_format=channel_format) + img_grey_da: xr.DataArray = flatten_channels(img=img_src, channel_format=channel_format) if need_downscale: logger.info("Downscaling for faster computation.") img_grey = _downscale_with_dask(img_grey=img_grey_da, target_pixels=auto_max_pixels) diff --git a/src/squidpy/experimental/im/_intensity_metrics.py b/src/squidpy/experimental/im/_intensity_metrics.py new file mode 100644 index 000000000..2657bb41a --- /dev/null +++ b/src/squidpy/experimental/im/_intensity_metrics.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +import numpy as np + +# --- Intensity metrics (grayscale input) --- + + +def brightness_mean(block: np.ndarray) -> np.ndarray: + """Mean pixel intensity of a grayscale tile.""" + return np.array([[float(block.mean())]], dtype=np.float32) + + +def brightness_std(block: np.ndarray) -> np.ndarray: + """Standard deviation of pixel intensity of a grayscale tile.""" + return np.array([[float(block.std())]], dtype=np.float32) + + +def entropy(block: np.ndarray) -> np.ndarray: + """Shannon entropy of pixel intensity histogram.""" + arr = block.ravel() + lo, hi = float(arr.min()), float(arr.max()) + if hi - lo < 1e-10: + return np.array([[0.0]], dtype=np.float32) + # Quantize to 256 bins directly without storing intermediate normalized array + bins = np.clip(((arr - lo) * (255.0 / (hi - lo))).astype(np.int32), 0, 255) + counts = np.bincount(bins, minlength=256) + probs = counts[counts > 0].astype(np.float64) + probs /= probs.sum() + ent = -float(np.dot(probs, np.log2(probs))) + return np.array([[ent]], dtype=np.float32) + + +# --- Staining metrics (RGB input, H&E only) --- + + +def rgb_to_hed(block_rgb: np.ndarray) -> np.ndarray: + """Convert RGB tile to HED colour space using Beer-Lambert deconvolution. + + Parameters + ---------- + block_rgb + (ty, tx, 3) float32 array in [0, 1]. + + Returns + ------- + (ty, tx, 3) float64 array with channels H, E, D. + """ + from skimage.color import rgb2hed + + rgb_clipped = np.clip(block_rgb, 0.0, 1.0) + return rgb2hed(rgb_clipped) + + +def hed_metrics(block: np.ndarray) -> np.ndarray: + """Return all HED-derived metrics for one RGB tile.""" + hed = rgb_to_hed(block) + h = hed[..., 0] + e = hed[..., 1] + + return np.array( + [ + [ + [ + float(h.mean()), + float(h.std()), + float(e.mean()), + float(e.std()), + float(np.abs(h).mean() / (np.abs(e).mean() + 1e-10)), + ] + ] + ], + dtype=np.float32, + ) + + +def hematoxylin_mean(block: np.ndarray) -> np.ndarray: + """Mean hematoxylin channel intensity.""" + hed = rgb_to_hed(block) + return np.array([[float(hed[..., 0].mean())]], dtype=np.float32) + + +def hematoxylin_std(block: np.ndarray) -> np.ndarray: + """Std of hematoxylin channel intensity.""" + hed = rgb_to_hed(block) + return np.array([[float(hed[..., 0].std())]], dtype=np.float32) + + +def eosin_mean(block: np.ndarray) -> np.ndarray: + """Mean eosin channel intensity.""" + hed = rgb_to_hed(block) + return np.array([[float(hed[..., 1].mean())]], dtype=np.float32) + + +def eosin_std(block: np.ndarray) -> np.ndarray: + """Std of eosin channel intensity.""" + hed = rgb_to_hed(block) + return np.array([[float(hed[..., 1].std())]], dtype=np.float32) + + +def he_ratio(block: np.ndarray) -> np.ndarray: + """Ratio of hematoxylin to eosin mean intensity.""" + hed = rgb_to_hed(block) + h_mean = float(np.abs(hed[..., 0]).mean()) + e_mean = float(np.abs(hed[..., 1]).mean()) + ratio = h_mean / (e_mean + 1e-10) + return np.array([[ratio]], dtype=np.float32) + + +# --- Artifact metrics (RGB input, H&E only) --- + + +def fold_fraction(block: np.ndarray) -> np.ndarray: + """Fraction of pixels identified as tissue folds. + + Uses HSV thresholds tuned for H&E staining: saturation > 0.4 and + value < 0.3 captures the dark, saturated appearance of folded tissue. + """ + from skimage.color import rgb2hsv + + rgb_clipped = np.clip(block, 0.0, 1.0) + hsv = rgb2hsv(rgb_clipped) + sat = hsv[..., 1] + val = hsv[..., 2] + fold_mask = (sat > 0.4) & (val < 0.3) + frac = float(fold_mask.sum()) / max(fold_mask.size, 1) + return np.array([[frac]], dtype=np.float32) + + +# --- Tissue coverage (mask input) --- + + +def tissue_fraction(block: np.ndarray) -> np.ndarray: + """Fraction of pixels that are tissue (nonzero) in a binary mask tile.""" + return np.array([[float(block.mean())]], dtype=np.float32) diff --git a/src/squidpy/experimental/im/_make_tiles.py b/src/squidpy/experimental/im/_make_tiles.py index ef04d6302..6323c48ad 100644 --- a/src/squidpy/experimental/im/_make_tiles.py +++ b/src/squidpy/experimental/im/_make_tiles.py @@ -1,9 +1,5 @@ from __future__ import annotations -import itertools -from typing import Literal - -import dask.array as da import geopandas as gpd import numpy as np import pandas as pd @@ -18,101 +14,16 @@ from squidpy._utils import _yx_from_shape from squidpy._validators import assert_in_range, assert_key_in_sdata, assert_positive - -from ._utils import _get_element_data +from squidpy.experimental.im._utils import ( + TileGrid, + get_element_data, + get_mask_materialized, + save_tile_grid_to_shapes, +) __all__ = ["make_tiles", "make_tiles_from_spots"] -class _TileGrid: - """Immutable tile grid definition with cached bounds and centroids.""" - - def __init__( - self, - H: int, - W: int, - tile_size: Literal["auto"] | tuple[int, int] = "auto", - target_tiles: int = 100, - offset_y: int = 0, - offset_x: int = 0, - ): - self.H = H - self.W = W - if tile_size == "auto": - size = max(min(self.H // target_tiles, self.W // target_tiles), 100) - self.ty = int(size) - self.tx = int(size) - else: - self.ty = int(tile_size[0]) - self.tx = int(tile_size[1]) - self.offset_y = offset_y - self.offset_x = offset_x - # Calculate number of tiles needed to cover entire image, accounting for offset - # The grid starts at offset_y, offset_x (can be negative) - # We need tiles from min(0, offset_y) to at least H - # So total coverage needed is from min(0, offset_y) to H - grid_start_y = min(0, self.offset_y) - grid_start_x = min(0, self.offset_x) - total_h_needed = self.H - grid_start_y - total_w_needed = self.W - grid_start_x - self.tiles_y = (total_h_needed + self.ty - 1) // self.ty - self.tiles_x = (total_w_needed + self.tx - 1) // self.tx - # Cache immutable derived values - self._indices = np.array([[iy, ix] for iy in range(self.tiles_y) for ix in range(self.tiles_x)], dtype=int) - self._names = [f"tile_x{ix}_y{iy}" for iy in range(self.tiles_y) for ix in range(self.tiles_x)] - self._bounds = self._compute_bounds() - self._centroids_polys = self._compute_centroids_and_polygons() - - def indices(self) -> np.ndarray: - return self._indices - - def names(self) -> list[str]: - return self._names - - def bounds(self) -> np.ndarray: - return self._bounds - - def _compute_bounds(self) -> np.ndarray: - b: list[list[int]] = [] - for iy, ix in itertools.product(range(self.tiles_y), range(self.tiles_x)): - y0 = iy * self.ty + self.offset_y - x0 = ix * self.tx + self.offset_x - y1 = ((iy + 1) * self.ty + self.offset_y) if iy < self.tiles_y - 1 else self.H - x1 = ((ix + 1) * self.tx + self.offset_x) if ix < self.tiles_x - 1 else self.W - # Clamp bounds to image dimensions - y0 = max(0, min(y0, self.H)) - x0 = max(0, min(x0, self.W)) - y1 = max(0, min(y1, self.H)) - x1 = max(0, min(x1, self.W)) - b.append([y0, x0, y1, x1]) - return np.array(b, dtype=int) - - def centroids_and_polygons(self) -> tuple[np.ndarray, list[Polygon]]: - return self._centroids_polys - - def _compute_centroids_and_polygons(self) -> tuple[np.ndarray, list[Polygon]]: - cents: list[list[float]] = [] - polys: list[Polygon] = [] - for y0, x0, y1, x1 in self._bounds: - cy = (y0 + y1) / 2 - cx = (x0 + x1) / 2 - cents.append([cy, cx]) - polys.append(Polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0)])) - return np.array(cents, dtype=float), polys - - def rechunk_and_pad(self, arr_yx: da.Array) -> da.Array: - if arr_yx.ndim != 2: - raise ValueError("Expected a 2D array shaped (y, x).") - pad_y = self.tiles_y * self.ty - int(arr_yx.shape[0]) - pad_x = self.tiles_x * self.tx - int(arr_yx.shape[1]) - a = arr_yx.rechunk((self.ty, self.tx)) - return da.pad(a, ((0, pad_y), (0, pad_x)), mode="edge") if (pad_y > 0 or pad_x > 0) else a - - def coarsen(self, arr_yx: da.Array, reduce: Literal["mean", "sum"] = "mean") -> da.Array: - reducer = np.mean if reduce == "mean" else np.sum - return da.coarsen(reducer, arr_yx, {0: self.ty, 1: self.tx}, trim_excess=False) - - class _SpotTileGrid: """Tile container for Visium spots, used with ``_filter_tiles``.""" @@ -176,9 +87,9 @@ def _get_largest_scale_dimensions( """ img_node = sdata.images[image_key] - # Use _get_element_data with "scale0" which is always the largest scale + # Use get_element_data with "scale0" which is always the largest scale # It handles both datatree (multiscale) and single-scale images - img_da = _get_element_data(img_node, "scale0", "image", image_key) + img_da = get_element_data(img_node, "scale0", "image", image_key) # Get spatial dimensions (y, x) if "y" in img_da.sizes and "x" in img_da.sizes: @@ -208,34 +119,12 @@ def _choose_label_scale_for_image(label_node: Labels2DModel, target_hw: tuple[in def _save_tiles_to_shapes( sdata: sd.SpatialData, - tg: _TileGrid, + tg: TileGrid, image_key: str, shapes_key: str, ) -> None: """Save a TileGrid to sdata.shapes as a GeoDataFrame.""" - tile_indices = tg.indices() - pixel_bounds = tg.bounds() - _, polys = tg.centroids_and_polygons() - - tile_gdf = gpd.GeoDataFrame( - { - "tile_id": tg.names(), - "tile_y": tile_indices[:, 0], - "tile_x": tile_indices[:, 1], - "pixel_y0": pixel_bounds[:, 0], - "pixel_x0": pixel_bounds[:, 1], - "pixel_y1": pixel_bounds[:, 2], - "pixel_x1": pixel_bounds[:, 3], - "geometry": polys, - }, - geometry="geometry", - ) - - sdata.shapes[shapes_key] = ShapesModel.parse(tile_gdf) - # we know that a) the element exists and b) it has at least an Identity transformation - transformations = get_transformation(sdata.images[image_key], get_all=True) - set_transformation(sdata.shapes[shapes_key], transformations, set_all=True) - logger.info(f"Saved tile grid as 'sdata.shapes[\"{shapes_key}\"]'") + save_tile_grid_to_shapes(sdata, tg, shapes_key, copy_transforms_from_key=image_key) def _save_spot_tiles_to_shapes( @@ -375,7 +264,7 @@ def make_tiles( mask_key_for_grid = default_mask_key else: try: - from ._detect_tissue import detect_tissue + from squidpy.experimental.im._detect_tissue import detect_tissue detect_tissue( sdata, @@ -420,7 +309,7 @@ def make_tiles( classification_mask_key, ) try: - from ._detect_tissue import detect_tissue + from squidpy.experimental.im._detect_tissue import detect_tissue detect_tissue( sdata, @@ -568,7 +457,7 @@ def make_tiles_from_spots( classification_mask_key, ) try: - from ._detect_tissue import detect_tissue + from squidpy.experimental.im._detect_tissue import detect_tissue detect_tissue( sdata, @@ -639,7 +528,7 @@ def make_tiles_from_spots( def _filter_tiles( sdata: sd.SpatialData, - tg: _TileGrid, + tg: TileGrid, image_key: str | None, *, tissue_mask_key: str | None = None, @@ -691,7 +580,7 @@ def _filter_tiles( else: raise ValueError("tissue_mask_key must be provided when image_key is None.") assert_key_in_sdata(sdata, mask_key, attr="labels") - mask = _get_mask_from_labels(sdata, mask_key, scale) + mask = get_mask_materialized(sdata, mask_key, scale) H_mask, W_mask = mask.shape # Check tissue coverage for each tile @@ -756,7 +645,7 @@ def _make_tiles( tile_size: tuple[int, int] = (224, 224), center_grid_on_tissue: bool = False, scale: str = "auto", -) -> _TileGrid: +) -> TileGrid: """Construct a tile grid for an image, optionally centered on a tissue mask. Callers must validate *image_key* before calling this helper. @@ -768,14 +657,14 @@ def _make_tiles( # Path 1: Regular grid starting from top-left if not center_grid_on_tissue or image_mask_key is None: - return _TileGrid(H, W, tile_size=tile_size) + return TileGrid(H, W, tile_size=tile_size) # Path 2: Center grid on tissue mask centroid assert_key_in_sdata(sdata, image_mask_key, attr="labels") # Get mask and compute centroid label_node = sdata.labels[image_mask_key] - mask_da = _get_element_data(label_node, scale, "label", image_mask_key) + mask_da = get_element_data(label_node, scale, "label", image_mask_key) # Convert to numpy array if needed if is_dask_collection(mask_da): @@ -807,7 +696,7 @@ def _make_tiles( mask_bool = mask > 0 if not mask_bool.any(): logger.warning("Mask is empty. Using regular grid starting from top-left.") - return _TileGrid(H, W, tile_size=tile_size) + return TileGrid(H, W, tile_size=tile_size) # Calculate centroid using center of mass y_coords, x_coords = np.where(mask_bool) @@ -822,7 +711,7 @@ def _make_tiles( offset_y = int(round(centroid_y - tile_center_y_standard)) offset_x = int(round(centroid_x - tile_center_x_standard)) - return _TileGrid(H, W, tile_size=tile_size, offset_y=offset_y, offset_x=offset_x) + return TileGrid(H, W, tile_size=tile_size, offset_y=offset_y, offset_x=offset_x) def _get_spot_coordinates( @@ -878,27 +767,3 @@ def _derive_tile_size_from_spots(coords: np.ndarray) -> tuple[int, int]: ) side = max(1, int(np.floor(row_spacing))) return side, side - - -def _get_mask_from_labels(sdata: sd.SpatialData, mask_key: str, scale: str) -> np.ndarray: - """Extract a 2D mask array from ``sdata.labels`` at the requested scale. - - Callers must validate *mask_key* before calling this helper. - """ - label_node = sdata.labels[mask_key] - mask_da = _get_element_data(label_node, scale, "label", mask_key) - - if is_dask_collection(mask_da): - mask_da = mask_da.compute() - - if isinstance(mask_da, xr.DataArray): - mask = np.asarray(mask_da.data) - else: - mask = np.asarray(mask_da) - - if mask.ndim > 2: - mask = mask.squeeze() - if mask.ndim != 2: - raise ValueError(f"Expected 2D mask with shape (y, x), got shape {mask.shape}") - - return mask diff --git a/src/squidpy/experimental/im/_qc_image.py b/src/squidpy/experimental/im/_qc_image.py new file mode 100644 index 000000000..a8010e87f --- /dev/null +++ b/src/squidpy/experimental/im/_qc_image.py @@ -0,0 +1,438 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import Literal + +import dask +import dask.array as da +import numpy as np +import pandas as pd +import xarray as xr +from anndata import AnnData +from dask.diagnostics import ProgressBar +from spatialdata import SpatialData +from spatialdata._logging import logger +from spatialdata.models import TableModel + +from squidpy._utils import _ensure_dim_order +from squidpy.experimental.im._intensity_metrics import hed_metrics +from squidpy.experimental.im._qc_metrics import _HNE_METRICS, InputKind, QCMetric, get_metric_info +from squidpy.experimental.im._utils import ( + TileGrid, + get_element_data, + get_mask_dask, + resolve_tissue_mask, + save_tile_grid_to_shapes, +) + +_DEFAULT_HNE_METRICS: list[QCMetric] = [ + QCMetric.TENENGRAD, + QCMetric.VAR_OF_LAPLACIAN, + QCMetric.ENTROPY, + QCMetric.BRIGHTNESS_MEAN, + QCMetric.HEMATOXYLIN_MEAN, + QCMetric.EOSIN_MEAN, +] + +_DEFAULT_GENERIC_METRICS: list[QCMetric] = [ + QCMetric.TENENGRAD, + QCMetric.VAR_OF_LAPLACIAN, + QCMetric.ENTROPY, + QCMetric.BRIGHTNESS_MEAN, +] + + +def qc_image( + sdata: SpatialData, + image_key: str, + *, + scale: str = "scale0", + metrics: QCMetric | list[QCMetric] | None = None, + tile_size: Literal["auto"] | tuple[int, int] = "auto", + is_hne: bool = True, + detect_outliers: bool = True, + detect_tissue: bool = True, + outlier_threshold: float = 0.1, + progress: bool = True, + tissue_mask_key: str | None = None, + preview: bool = True, +) -> None: + """ + Perform quality control analysis on an image. + + Computes tile-based QC metrics including sharpness, intensity, staining + (H&E only), artifact detection, and tissue coverage. + + Parameters + ---------- + sdata + SpatialData object containing the image. + image_key + Key of the image in ``sdata.images`` to analyze. + scale + Scale level to use for processing. Defaults to ``"scale0"``. + metrics + QC metrics to compute. Can be a single metric or list of metrics. + If ``None``, uses sensible defaults based on ``is_hne``. + tile_size + Size of tiles for analysis. If ``"auto"``, automatically determines size. + is_hne + Whether the image is H&E stained. Controls which metrics are available + and which defaults are used. If ``False`` and H&E-specific metrics are + explicitly requested, raises ``ValueError``. + detect_outliers + Whether to detect outlier tiles based on QC scores. + detect_tissue + Whether to detect tissue regions for context-aware outlier detection. + outlier_threshold + Percentile threshold (0-1) for outlier detection. A tile is flagged as + an outlier if its within-tissue percentile rank falls below this value. + For example, ``0.1`` flags the bottom 10% of tissue tiles (by their + worst sharpness metric) as outliers. Default is ``0.1``. + progress + Whether to show progress bar during computation. + tissue_mask_key + Key of the tissue mask in ``sdata.labels`` to use. If ``None``, the function will + check if ``"{image_key}_tissue"`` already exists in ``sdata.labels`` and reuse it. + If it doesn't exist, tissue detection will be performed and the mask will be added + to ``sdata.labels`` with key ``"{image_key}_tissue"``. If provided, the existing + mask at this key will be used. + preview + If ``True``, render a preview showing the image overlaid with outlier tiles + highlighted in red. Only shown when ``detect_outliers=True``. + + Returns + ------- + None + Results are stored in the following locations: + + - ``sdata.tables[f"qc_img_{image_key}"]``: AnnData object with QC scores + - ``sdata.shapes[f"qc_img_{image_key}_grid"]``: GeoDataFrame with tile geometries + - ``sdata.tables[...].uns["qc_image"]``: Metadata about the analysis + """ + # Parameter validation + if image_key not in sdata.images: + raise KeyError(f"Image key '{image_key}' not found in sdata.images") + + if metrics is None: + metrics = list(_DEFAULT_HNE_METRICS if is_hne else _DEFAULT_GENERIC_METRICS) + elif isinstance(metrics, QCMetric): + metrics = [metrics] + else: + metrics = list(metrics) + + if not isinstance(metrics, list) or not all(isinstance(m, QCMetric) for m in metrics): + available = ", ".join(m.value for m in QCMetric) + raise TypeError(f"metrics must be QCMetric or list of QCMetric. Available: {available}") + + # Validate H&E constraint + if not is_hne: + hne_requested = _HNE_METRICS & set(metrics) + if hne_requested: + names = ", ".join(m.value for m in hne_requested) + raise ValueError( + f"H&E-specific metrics ({names}) cannot be used when is_hne=False. " + f"Set is_hne=True or remove these metrics." + ) + + if not 0 < outlier_threshold < 1: + raise ValueError(f"outlier_threshold must be in (0, 1), got {outlier_threshold}") + + # Compute QC metrics + img_node = sdata.images[image_key] + img_da = get_element_data(img_node, scale, "image", image_key) + img_yxc = _ensure_dim_order(img_da, "yxc") + gray = _to_gray_dask_yx(img_yxc) + H, W = int(gray.shape[0]), int(gray.shape[1]) + + tg = TileGrid(H, W, tile_size) + obs_names = tg.names() + + logger.info("Quantifying image quality.") + logger.info(f"- Input image (x, y): ({W}, {H})") + logger.info(f"- Tile size (x, y): ({tg.tx}, {tg.ty})") + logger.info(f"- Number of tiles (n_x, n_y): ({tg.tiles_x}, {tg.tiles_y})") + + # Group metrics by InputKind + groups: dict[InputKind, list[QCMetric]] = defaultdict(list) + for m in metrics: + kind, _fn = get_metric_info(m) + groups[kind].append(m) + + out_chunks = ((1,) * tg.tiles_y, (1,) * tg.tiles_x) + + # Prepare inputs lazily (only for needed kinds) + prepared_inputs: dict[InputKind, da.Array] = {} + + _tissue_binary_da: da.Array | None = None + if InputKind.MASK in groups or detect_tissue: + mask_key_resolved = resolve_tissue_mask(sdata, image_key, scale, tissue_mask_key) + raw_mask = get_mask_dask(sdata, mask_key_resolved, scale) + _tissue_binary_da = (raw_mask > 0).astype(np.float32).rechunk((tg.ty, tg.tx)) + + if InputKind.GRAYSCALE in groups: + prepared_inputs[InputKind.GRAYSCALE] = gray.rechunk((tg.ty, tg.tx)) + + if InputKind.RGB in groups: + src_dtype = img_yxc.data.dtype + rgb_arr = img_yxc.data[..., :3].astype(np.float32, copy=False) + # Normalize integer images to [0, 1] + if np.issubdtype(src_dtype, np.integer): + rgb_arr = rgb_arr / float(np.iinfo(src_dtype).max) + prepared_inputs[InputKind.RGB] = rgb_arr.rechunk((tg.ty, tg.tx, 3)) + + if InputKind.MASK in groups and _tissue_binary_da is not None: + prepared_inputs[InputKind.MASK] = _tissue_binary_da + + # Build all dask graphs lazily + delayed_scores: dict[str, da.Array] = {} + hed_metric_indices = { + QCMetric.HEMATOXYLIN_MEAN: 0, + QCMetric.HEMATOXYLIN_STD: 1, + QCMetric.EOSIN_MEAN: 2, + QCMetric.EOSIN_STD: 3, + QCMetric.HE_RATIO: 4, + } + hed_scores: da.Array | None = None + + for m in metrics: + if m in hed_metric_indices: + if hed_scores is None: + hed_scores = da.map_blocks( + hed_metrics, + prepared_inputs[InputKind.RGB], + dtype=np.float32, + chunks=out_chunks + ((5,),), + drop_axis=2, + new_axis=2, + ) + delayed_scores[m.value] = hed_scores[..., hed_metric_indices[m]] + logger.info(f"- Calculating metric: '{m.value}'") + continue + + kind, metric_func = get_metric_info(m) + source = prepared_inputs[kind] + + if kind == InputKind.RGB: + delayed_scores[m.value] = da.map_blocks( + metric_func, source, dtype=np.float32, chunks=out_chunks, drop_axis=2 + ) + else: + delayed_scores[m.value] = da.map_blocks(metric_func, source, dtype=np.float32, chunks=out_chunks) + logger.info(f"- Calculating metric: '{m.value}'") + + # Single dask.compute() across all metric types + if progress: + with ProgressBar(): + results = dask.compute(*delayed_scores.values()) + else: + results = dask.compute(*delayed_scores.values()) + all_scores: dict[str, np.ndarray] = dict(zip(delayed_scores.keys(), results, strict=True)) + + # Build AnnData + metric_names = [m.value for m in metrics] + first = next(iter(all_scores.values())) + cents, _ = tg.centroids_and_polygons() + n_tiles = first.size + X = np.column_stack([all_scores[n].ravel() for n in metric_names]) + var_names = [f"qc_{n}" for n in metric_names] + + adata = AnnData(X=X) + adata.var_names = var_names + adata.obs_names = obs_names + adata.obs["centroid_y"] = cents[:, 0] + adata.obs["centroid_x"] = cents[:, 1] + adata.obsm["spatial"] = cents + + tissue = np.zeros(n_tiles, dtype=bool) + back = ~tissue + outlier_labels = np.ones(n_tiles, dtype=int) + unfocus_scores: np.ndarray | None = None + + if detect_outliers: + if detect_tissue: + tissue, back = _classify_tiles_by_tissue( + tg, + sdata=sdata, + image_key=image_key, + scale=scale, + tissue_mask_key=tissue_mask_key, + binary_mask_da=_tissue_binary_da, + ) + logger.info(f"- Classified tiles: background: {back.sum()}, tissue: {tissue.sum()}.") + + if detect_tissue and tissue.any(): + tissue_scores = _compute_unfocus_scores(X[tissue], var_names) + unfocus_scores = np.full(n_tiles, np.nan, dtype=np.float32) + unfocus_scores[tissue] = tissue_scores + outlier_labels = np.ones(n_tiles, dtype=int) + outlier_labels[tissue] = np.where(tissue_scores >= 1 - outlier_threshold, -1, 1) + else: + all_scores_arr = _compute_unfocus_scores(X, var_names) + unfocus_scores = all_scores_arr + outlier_labels = np.where(all_scores_arr >= 1 - outlier_threshold, -1, 1) + + adata.obs["qc_outlier"] = pd.Categorical((outlier_labels == -1).astype(str), categories=["False", "True"]) + if detect_tissue: + adata.obs["is_tissue"] = pd.Categorical(tissue.astype(str), categories=["False", "True"]) + adata.obs["is_background"] = pd.Categorical(back.astype(str), categories=["False", "True"]) + adata.obs["unfocus_score"] = unfocus_scores + + logger.info(f"- Detected {int((outlier_labels == -1).sum())} outlier tiles.") + + adata.uns["qc_image"] = { + "metrics": list(all_scores.keys()), + "tile_size_y": tg.ty, + "tile_size_x": tg.tx, + "image_height": H, + "image_width": W, + "n_tiles_y": tg.tiles_y, + "n_tiles_x": tg.tiles_x, + "image_key": image_key, + "scale": scale, + "is_hne": is_hne, + "detect_tissue": detect_tissue, + "outlier_threshold": outlier_threshold, + "n_tissue_tiles": int(tissue.sum()), + "n_background_tiles": int(back.sum()), + "n_outlier_tiles": int((outlier_labels == -1).sum()), + } + + table_key = f"qc_img_{image_key}" + shapes_key = f"qc_img_{image_key}_grid" + + # Build shapes first (need the index for tile_id linkage) + save_tile_grid_to_shapes(sdata, tg, shapes_key, copy_transforms_from_key=image_key) + + # Set spatialdata linkage on adata BEFORE TableModel.parse + adata.obs["grid_name"] = pd.Categorical([shapes_key] * len(adata)) + adata.obs["tile_id"] = sdata.shapes[shapes_key].index + adata.uns["spatialdata_attrs"] = { + "region": shapes_key, + "region_key": "grid_name", + "instance_key": "tile_id", + } + + sdata.tables[table_key] = TableModel.parse(adata) + logger.info(f"- Saved QC scores as 'sdata.tables[\"{table_key}\"]'") + + if preview and detect_outliers and "qc_outlier" in adata.obs.columns: + try: + ( + sdata.pl.render_images(image_key) + .pl.render_shapes( + shapes_key, + color="qc_outlier", + groups="True", + palette="red", + fill_alpha=0.5, + table_name=table_key, + ) + .pl.show() + ) + except (AttributeError, KeyError, ValueError) as e: + logger.warning(f"Could not generate preview plot: {e}") + + +_LUMINANCE_WEIGHTS = da.from_array(np.array([0.2126, 0.7152, 0.0722], dtype=np.float32), chunks=(3,)) + + +def _to_gray_dask_yx(img_yxc: xr.DataArray) -> da.Array: + """Convert multi-channel image to grayscale using ITU-R BT.709 luminance weights.""" + arr = img_yxc.data + if arr.ndim != 3: + raise ValueError(f"Expected image with shape `(y, x, c)`, found `{arr.shape}`.") + c = arr.shape[2] + if c == 1: + return arr[..., 0].astype(np.float32, copy=False) + rgb = arr[..., :3].astype(np.float32, copy=False) + gray = da.tensordot(rgb, _LUMINANCE_WEIGHTS, axes=([2], [0])) + return gray.astype(np.float32, copy=False) + + +def _classify_tiles_by_tissue( + tg: TileGrid, + *, + sdata: SpatialData, + image_key: str, + scale: str = "scale0", + tissue_mask_key: str | None = None, + binary_mask_da: da.Array | None = None, +) -> tuple[np.ndarray, np.ndarray]: + """Classify tiles as tissue or background based on a binary mask. + + Returns + ------- + tissue, background + Boolean arrays of shape ``(n_tiles,)``. + """ + if binary_mask_da is None: + mask_key_resolved = resolve_tissue_mask(sdata, image_key, scale, tissue_mask_key) + raw_mask = get_mask_dask(sdata, mask_key_resolved, scale) + binary_mask_da = (raw_mask > 0).astype(np.float32).rechunk((tg.ty, tg.tx)) + + H, W = binary_mask_da.shape + tiles_y = (H + tg.ty - 1) // tg.ty + tiles_x = (W + tg.tx - 1) // tg.tx + + def _mean_block(block: np.ndarray) -> np.ndarray: + return np.array([[float(block.mean())]], dtype=np.float32) + + frac_da = da.map_blocks( + _mean_block, + binary_mask_da, + dtype=np.float32, + chunks=((1,) * tiles_y, (1,) * tiles_x), + ) + n_tiles = tg.tiles_y * tg.tiles_x + frac = frac_da.compute().ravel()[:n_tiles] + + tissue = frac > 0.5 + return tissue, ~tissue + + +def _compute_unfocus_scores(X: np.ndarray, var_names: list[str]) -> np.ndarray: + """Compute per-tile unfocus scores using within-group percentile ranks. + + For each sharpness metric, every tile is ranked among its peers (the tiles + in ``X``) and assigned a percentile in [0, 1] where 0 = lowest value = + worst quality and 1 = highest. The final ``unfocus_score`` for a tile is + ``1 - min_rank`` across all *sharpness* metrics, so a tile that scores + poorly on *any* sharpness axis gets a high unfocus score. + + Only gradient-based sharpness metrics (tenengrad, var_of_laplacian) + contribute to the score. Other metrics like entropy, wavelet energy, + or FFT energy correlate more with tissue structure than with actual + optical focus and are therefore excluded. + + Parameters + ---------- + X + Score matrix of shape ``(n_tiles, n_metrics)``. + var_names + Column names matching ``X`` (``qc_`` prefix expected). + + Returns + ------- + np.ndarray of shape ``(n_tiles,)`` with values in [0, 1]. + """ + from scipy.stats import rankdata + + _SHARPNESS_KEYWORDS = {"tenengrad", "laplacian"} + + sharpness_cols = [i for i, name in enumerate(var_names) if any(kw in name.lower() for kw in _SHARPNESS_KEYWORDS)] + if not sharpness_cols: + sharpness_cols = list(range(X.shape[1])) + + n = X.shape[0] + if n <= 1: + return np.zeros(n, dtype=np.float32) + + # Percentile rank per metric: 0 = worst, 1 = best + ranks = np.column_stack([(rankdata(X[:, col], method="average") - 1) / (n - 1) for col in sharpness_cols]) + + # Worst (minimum) rank across sharpness metrics governs the score + min_rank = ranks.min(axis=1) + + # Invert: high unfocus_score = low quality + return (1.0 - min_rank).astype(np.float32) diff --git a/src/squidpy/experimental/im/_qc_metrics.py b/src/squidpy/experimental/im/_qc_metrics.py new file mode 100644 index 000000000..05c6567bc --- /dev/null +++ b/src/squidpy/experimental/im/_qc_metrics.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Callable +from enum import StrEnum + +import numpy as np + +from squidpy.experimental.im._intensity_metrics import ( + brightness_mean, + brightness_std, + entropy, + eosin_mean, + eosin_std, + fold_fraction, + he_ratio, + hematoxylin_mean, + hematoxylin_std, + tissue_fraction, +) +from squidpy.experimental.im._sharpness_metrics import ( + fft_high_freq_energy, + haar_wavelet_energy, + laplacian_variance, + pop_variance, + tenengrad_mean, +) + +MetricFn = Callable[[np.ndarray], np.ndarray] + + +class InputKind(StrEnum): + GRAYSCALE = "grayscale" # (ty, tx) float32 + RGB = "rgb" # (ty, tx, 3) float32 in [0,1] + MASK = "mask" # (ty, tx) binary float32 + + +class QCMetric(StrEnum): + # Sharpness (grayscale input) + TENENGRAD = "tenengrad" + VAR_OF_LAPLACIAN = "var_of_laplacian" + VARIANCE = "variance" + FFT_HIGH_FREQ_ENERGY = "fft_high_freq_energy" + HAAR_WAVELET_ENERGY = "haar_wavelet_energy" + # Intensity (grayscale input) + BRIGHTNESS_MEAN = "brightness_mean" + BRIGHTNESS_STD = "brightness_std" + ENTROPY = "entropy" + # Staining (RGB input, H&E only) + HEMATOXYLIN_MEAN = "hematoxylin_mean" + HEMATOXYLIN_STD = "hematoxylin_std" + EOSIN_MEAN = "eosin_mean" + EOSIN_STD = "eosin_std" + HE_RATIO = "he_ratio" + # Artifacts (RGB input, H&E only) + FOLD_FRACTION = "fold_fraction" + # Tissue coverage (mask input) + TISSUE_FRACTION = "tissue_fraction" + + +_HNE_METRICS: set[QCMetric] = { + QCMetric.HEMATOXYLIN_MEAN, + QCMetric.HEMATOXYLIN_STD, + QCMetric.EOSIN_MEAN, + QCMetric.EOSIN_STD, + QCMetric.HE_RATIO, + QCMetric.FOLD_FRACTION, +} + + +# --- Registry --- + +_METRIC_REGISTRY: dict[QCMetric, tuple[InputKind, MetricFn]] = { + # Sharpness (grayscale) + QCMetric.TENENGRAD: (InputKind.GRAYSCALE, tenengrad_mean), + QCMetric.VAR_OF_LAPLACIAN: (InputKind.GRAYSCALE, laplacian_variance), + QCMetric.VARIANCE: (InputKind.GRAYSCALE, pop_variance), + QCMetric.FFT_HIGH_FREQ_ENERGY: (InputKind.GRAYSCALE, fft_high_freq_energy), + QCMetric.HAAR_WAVELET_ENERGY: (InputKind.GRAYSCALE, haar_wavelet_energy), + # Intensity (grayscale) + QCMetric.BRIGHTNESS_MEAN: (InputKind.GRAYSCALE, brightness_mean), + QCMetric.BRIGHTNESS_STD: (InputKind.GRAYSCALE, brightness_std), + QCMetric.ENTROPY: (InputKind.GRAYSCALE, entropy), + # Staining (RGB, H&E only) + QCMetric.HEMATOXYLIN_MEAN: (InputKind.RGB, hematoxylin_mean), + QCMetric.HEMATOXYLIN_STD: (InputKind.RGB, hematoxylin_std), + QCMetric.EOSIN_MEAN: (InputKind.RGB, eosin_mean), + QCMetric.EOSIN_STD: (InputKind.RGB, eosin_std), + QCMetric.HE_RATIO: (InputKind.RGB, he_ratio), + # Artifacts (RGB, H&E only) + QCMetric.FOLD_FRACTION: (InputKind.RGB, fold_fraction), + # Tissue coverage (mask) + QCMetric.TISSUE_FRACTION: (InputKind.MASK, tissue_fraction), +} + + +def get_metric_info(metric: QCMetric) -> tuple[InputKind, MetricFn]: + """Look up the input kind and callable for a QCMetric.""" + return _METRIC_REGISTRY[metric] diff --git a/src/squidpy/experimental/im/_sharpness_metrics.py b/src/squidpy/experimental/im/_sharpness_metrics.py new file mode 100644 index 000000000..f48f2aeaf --- /dev/null +++ b/src/squidpy/experimental/im/_sharpness_metrics.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import numpy as np +from scipy.fft import fft2, fftfreq +from skimage.filters import laplace, sobel_h, sobel_v + + +def to_f32_2d(x: np.ndarray) -> np.ndarray: + if x.ndim != 2: + raise ValueError("block must be 2D") + return np.ascontiguousarray(x.astype(np.float32, copy=False)) + + +def tenengrad_mean(block: np.ndarray) -> np.ndarray: + """Mean Tenengrad energy (sum of squared Sobel gradients).""" + b = to_f32_2d(block) + energy = sobel_h(b) ** 2 + sobel_v(b) ** 2 + return np.array([[float(energy.mean())]], dtype=np.float32) + + +def laplacian_variance(block: np.ndarray) -> np.ndarray: + """Population variance of Laplacian response.""" + b = to_f32_2d(block) + lap = laplace(b) + var_val = float(np.var(lap)) + return np.array([[max(var_val, 0.0)]], dtype=np.float32) + + +def pop_variance(block: np.ndarray) -> np.ndarray: + """Population variance of pixel intensities.""" + b = to_f32_2d(block) + return np.array([[float(np.var(b))]], dtype=np.float32) + + +def fft_high_freq_mask(h: int, w: int) -> np.ndarray: + """Frequency-domain mask for high-frequency energy (cached per tile shape).""" + fy = fftfreq(h) + fx = fftfreq(w) + ry, rx = np.meshgrid(fy, fx, indexing="ij") + return np.hypot(ry, rx) > 0.1 + + +def fft_high_freq_energy(block: np.ndarray) -> np.ndarray: + x = to_f32_2d(block).astype(np.float64, copy=False) + m = float(x.mean()) + s = float(x.std()) + x = (x - m) / s if s > 0.0 else (x - m) + + F = fft2(x) + mag2 = (F.real * F.real) + (F.imag * F.imag) + + mask = fft_high_freq_mask(x.shape[0], x.shape[1]) + + total = float(mag2.sum()) + if not np.isfinite(total) or total <= 1e-12: + ratio = 0.0 + else: + hi = float(mag2[mask].sum()) + ratio = hi / total if np.isfinite(hi) else 0.0 + ratio = np.clip(ratio, 0.0, 1.0) + return np.array([[ratio]], dtype=np.float32) + + +def haar_wavelet_energy(block: np.ndarray) -> np.ndarray: + """Detail-band (LH+HL+HH) energy ratio of a single-level Haar transform.""" + x = to_f32_2d(block).astype(np.float64, copy=False) + m = float(x.mean()) + s = float(x.std()) + x = (x - m) / s if s > 0.0 else (x - m) + + h, w = x.shape + if h % 2 == 1: + x = np.vstack([x, x[-1:, :]]) + h += 1 + if w % 2 == 1: + x = np.hstack([x, x[:, -1:]]) + w += 1 + + cA_h = (x[::2, :] + x[1::2, :]) / 2.0 + cH_h = (x[::2, :] - x[1::2, :]) / 2.0 + + cA = (cA_h[:, ::2] + cA_h[:, 1::2]) / 2.0 # LL + cH = (cA_h[:, ::2] - cA_h[:, 1::2]) / 2.0 # LH + cV = (cH_h[:, ::2] + cH_h[:, 1::2]) / 2.0 # HL + cD = (cH_h[:, ::2] - cH_h[:, 1::2]) / 2.0 # HH + + total = float((cA * cA).sum() + (cH * cH).sum() + (cV * cV).sum() + (cD * cD).sum()) + if not np.isfinite(total) or total <= 1e-12: + ratio = 0.0 + else: + detail = float((cH * cH).sum() + (cV * cV).sum() + (cD * cD).sum()) + ratio = detail / total if np.isfinite(detail) else 0.0 + ratio = np.clip(ratio, 0.0, 1.0) + return np.array([[ratio]], dtype=np.float32) diff --git a/src/squidpy/experimental/im/_utils.py b/src/squidpy/experimental/im/_utils.py index 8ce954e04..11467da49 100644 --- a/src/squidpy/experimental/im/_utils.py +++ b/src/squidpy/experimental/im/_utils.py @@ -2,11 +2,102 @@ from typing import Any, Literal +import dask.array as da +import geopandas as gpd +import numpy as np import xarray as xr +from shapely import box +from spatialdata import SpatialData from spatialdata._logging import logger +from spatialdata.models import ShapesModel +from spatialdata.transformations import get_transformation, set_transformation -def _get_element_data( +class TileGrid: + """Immutable tile grid definition with cached bounds and centroids.""" + + def __init__( + self, + H: int, + W: int, + tile_size: Literal["auto"] | tuple[int, int] = "auto", + target_tiles: int = 100, + offset_y: int = 0, + offset_x: int = 0, + ): + self.H = int(H) + self.W = int(W) + if tile_size == "auto": + size = max(min(self.H // target_tiles, self.W // target_tiles), 100) + self.ty = int(size) + self.tx = int(size) + else: + self.ty = int(tile_size[0]) + self.tx = int(tile_size[1]) + self.offset_y = offset_y + self.offset_x = offset_x + # Calculate number of tiles needed to cover entire image, accounting for offset + grid_start_y = min(0, self.offset_y) + grid_start_x = min(0, self.offset_x) + total_h_needed = self.H - grid_start_y + total_w_needed = self.W - grid_start_x + self.tiles_y = (total_h_needed + self.ty - 1) // self.ty + self.tiles_x = (total_w_needed + self.tx - 1) // self.tx + # Cache immutable derived values (vectorized) + iy = np.repeat(np.arange(self.tiles_y), self.tiles_x) + ix = np.tile(np.arange(self.tiles_x), self.tiles_y) + self._indices = np.column_stack([iy, ix]) + self._names = [f"tile_x{x}_y{y}" for y, x in zip(iy, ix, strict=True)] + self._bounds = self._compute_bounds(iy, ix) + self._centroids, self._polys = self._compute_centroids_and_polygons() + + def indices(self) -> np.ndarray: + return self._indices + + def names(self) -> list[str]: + return self._names + + def bounds(self) -> np.ndarray: + return self._bounds + + def _compute_bounds(self, iy: np.ndarray, ix: np.ndarray) -> np.ndarray: + y0 = iy * self.ty + self.offset_y + x0 = ix * self.tx + self.offset_x + y1 = (iy + 1) * self.ty + self.offset_y + x1 = (ix + 1) * self.tx + self.offset_x + # Last row/column extends to image edge + y1[iy == self.tiles_y - 1] = self.H + x1[ix == self.tiles_x - 1] = self.W + # Clamp to image dimensions + y0 = np.clip(y0, 0, self.H) + x0 = np.clip(x0, 0, self.W) + y1 = np.clip(y1, 0, self.H) + x1 = np.clip(x1, 0, self.W) + return np.column_stack([y0, x0, y1, x1]).astype(int) + + def centroids_and_polygons(self) -> tuple[np.ndarray, list]: + return self._centroids, self._polys + + def _compute_centroids_and_polygons(self) -> tuple[np.ndarray, list]: + y0, x0, y1, x1 = self._bounds[:, 0], self._bounds[:, 1], self._bounds[:, 2], self._bounds[:, 3] + centroids = np.column_stack([(y0 + y1) / 2.0, (x0 + x1) / 2.0]) + polys = list(box(x0, y0, x1, y1)) + return centroids, polys + + def rechunk_and_pad(self, arr_yx: da.Array) -> da.Array: + if arr_yx.ndim != 2: + raise ValueError("Expected a 2D array shaped (y, x).") + pad_y = self.tiles_y * self.ty - int(arr_yx.shape[0]) + pad_x = self.tiles_x * self.tx - int(arr_yx.shape[1]) + a = arr_yx.rechunk((self.ty, self.tx)) + return da.pad(a, ((0, pad_y), (0, pad_x)), mode="edge") if (pad_y > 0 or pad_x > 0) else a + + def coarsen(self, arr_yx: da.Array, reduce: Literal["mean", "sum"] = "mean") -> da.Array: + reducer = np.mean if reduce == "mean" else np.sum + return da.coarsen(reducer, arr_yx, {0: self.ty, 1: self.tx}, trim_excess=False) + + +def get_element_data( element_node: Any, scale: str | Literal["auto"], element_type: str = "element", @@ -58,7 +149,7 @@ def _idx(k: str) -> int: return data -def _flatten_channels( +def flatten_channels( img: xr.DataArray, channel_format: Literal["infer", "rgb", "rgba", "multichannel"] = "infer", ) -> xr.DataArray: @@ -129,3 +220,107 @@ def _flatten_channels( raise ValueError( f"Invalid channel_format: {channel_format}. Must be one of 'infer', 'rgb', 'rgba', 'multichannel'." ) + + +def get_mask_dask(sdata: SpatialData, mask_key: str, scale: str) -> da.Array: + """Extract mask as a lazy dask array from ``sdata.labels``.""" + if mask_key not in sdata.labels: + raise KeyError(f"Mask key '{mask_key}' not found in sdata.labels") + label_node = sdata.labels[mask_key] + mask_xr = get_element_data(label_node, scale, "label", mask_key) + + arr = mask_xr.data if hasattr(mask_xr, "data") else mask_xr + if not isinstance(arr, da.Array): + arr = da.from_array(np.asarray(arr)) + + if arr.ndim > 2: + arr = arr.squeeze() + if arr.ndim != 2: + raise ValueError(f"Expected 2D mask with shape (y, x), got shape {arr.shape}") + return arr + + +def get_mask_materialized(sdata: SpatialData, mask_key: str, scale: str) -> np.ndarray: + """Extract a 2D mask array from ``sdata.labels`` at the requested scale (materialized).""" + arr = get_mask_dask(sdata, mask_key, scale) + return np.asarray(arr.compute()) + + +def resolve_tissue_mask( + sdata: SpatialData, + image_key: str, + scale: str, + tissue_mask_key: str | None = None, +) -> str: + """Return the key of a tissue mask in ``sdata.labels``, creating one if needed. + + If *tissue_mask_key* is given and exists, it is returned as-is. + Otherwise falls back to ``f"{image_key}_tissue"``, running ``detect_tissue`` + to create it when missing. + + Raises + ------ + KeyError + If *tissue_mask_key* is given but not found in ``sdata.labels``. + Exception + Any exception raised by ``detect_tissue`` if auto-creation fails. + Callers needing graceful fallback should wrap this call in try/except. + """ + if tissue_mask_key is not None: + if tissue_mask_key not in sdata.labels: + raise KeyError(f"Tissue mask key '{tissue_mask_key}' not found in sdata.labels") + return tissue_mask_key + + mask_key = f"{image_key}_tissue" + if mask_key not in sdata.labels: + from squidpy.experimental.im._detect_tissue import detect_tissue + + detect_tissue(sdata=sdata, image_key=image_key, scale=scale, inplace=True, new_labels_key=mask_key) + logger.info(f"Saved tissue mask as 'sdata.labels[\"{mask_key}\"]'") + return mask_key + + +def save_tile_grid_to_shapes( + sdata: SpatialData, + tg: TileGrid, + shapes_key: str, + *, + copy_transforms_from_key: str | None = None, +) -> None: + """Save a TileGrid to ``sdata.shapes`` as a GeoDataFrame. + + Parameters + ---------- + sdata + SpatialData object. + tg + TileGrid whose bounds/centroids are persisted. + shapes_key + Key under which to store the shapes. + copy_transforms_from_key + If given, copy the transformations from ``sdata.images[copy_transforms_from_key]`` + onto the new shapes element. + """ + tile_indices = tg.indices() + pixel_bounds = tg.bounds() + _, polys = tg.centroids_and_polygons() + + tile_gdf = gpd.GeoDataFrame( + { + "tile_id": tg.names(), + "tile_y": tile_indices[:, 0], + "tile_x": tile_indices[:, 1], + "pixel_y0": pixel_bounds[:, 0], + "pixel_x0": pixel_bounds[:, 1], + "pixel_y1": pixel_bounds[:, 2], + "pixel_x1": pixel_bounds[:, 3], + "geometry": polys, + }, + geometry="geometry", + ) + + sdata.shapes[shapes_key] = ShapesModel.parse(tile_gdf) + if copy_transforms_from_key is not None: + transformations = get_transformation(sdata.images[copy_transforms_from_key], get_all=True) + set_transformation(sdata.shapes[shapes_key], transformations, set_all=True) + logger.info(f"- Saved tile grid as 'sdata.shapes[\"{shapes_key}\"]'") diff --git a/src/squidpy/experimental/pl/__init__.py b/src/squidpy/experimental/pl/__init__.py index 4d21ee850..cdb8a56d1 100644 --- a/src/squidpy/experimental/pl/__init__.py +++ b/src/squidpy/experimental/pl/__init__.py @@ -1,3 +1,5 @@ from __future__ import annotations -__all__ = [] +from ._qc_image import qc_image + +__all__ = ["qc_image"] diff --git a/src/squidpy/experimental/pl/_qc_image.py b/src/squidpy/experimental/pl/_qc_image.py new file mode 100644 index 000000000..bf369b753 --- /dev/null +++ b/src/squidpy/experimental/pl/_qc_image.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +from typing import Any + +import matplotlib.pyplot as plt +import numpy as np +from scipy.stats import gaussian_kde +from spatialdata import SpatialData +from spatialdata._logging import logger as logg + +from squidpy.experimental.im._qc_metrics import QCMetric + + +def qc_image( + sdata: SpatialData, + image_key: str, + metrics: QCMetric | list[QCMetric] | None = None, + figsize: tuple[int, int] | None = None, + return_ax: bool = False, + **kwargs: Any, +) -> np.ndarray | None: + """ + Plot a summary view of QC metrics from qc_image results. + + Automatically scans adata.uns for calculated metrics and plots the values. + Creates a multi-panel plot: one panel per calculated metric. + Each panel shows: spatial view, KDE plot, and statistics. + + Parameters + ---------- + sdata : SpatialData + SpatialData object containing QC results. + image_key : str + Image key used in qc_image function. + metrics : QCMetric or list of QCMetric, optional + Specific metrics to plot. If None, plots all calculated metrics. + figsize : tuple, optional + Figure size (width, height). Auto-calculated if None. + return_ax : bool + Whether to return the axes array. Default is False. + **kwargs + Additional arguments passed to render_shapes(). + + Returns + ------- + axes : numpy.ndarray of matplotlib.axes.Axes or None + The axes array if return_ax=True, otherwise None. + """ + table_key = f"qc_img_{image_key}" + shapes_key = f"qc_img_{image_key}_grid" + + if table_key not in sdata.tables: + raise ValueError(f"No QC data found for image '{image_key}'. Run sq.exp.im.qc_image() first.") + + adata = sdata.tables[table_key] + + if "qc_image" not in adata.uns: + raise ValueError("No qc_image metadata found. Run sq.exp.im.qc_image() first.") + + calculated_metrics = adata.uns["qc_image"]["metrics"] + + if not calculated_metrics: + raise ValueError("No QC metrics found in metadata.") + + if metrics is not None: + metrics_list = metrics if isinstance(metrics, list) else [metrics] + metrics_to_plot = [] + for metric in metrics_list: + metric_name = metric.value if isinstance(metric, QCMetric) else metric + if metric_name not in calculated_metrics: + raise ValueError(f"Metric '{metric_name}' not found. Available: {calculated_metrics}") + metrics_to_plot.append(metric_name) + else: + metrics_to_plot = calculated_metrics + + logg.info(f"Plotting {len(metrics_to_plot)} QC metrics: {metrics_to_plot}") + + n_metrics = len(metrics_to_plot) + ncols = 3 + nrows = n_metrics + + if figsize is None: + figsize = (12, 4 * nrows) + + _fig, axes = plt.subplots(nrows, ncols, figsize=figsize) + + if nrows == 1: + axes = axes.reshape(1, -1) + if ncols == 1: + axes = axes.reshape(-1, 1) + + var_name_to_idx = {name: idx for idx, name in enumerate(adata.var_names)} + + for i, metric_name in enumerate(metrics_to_plot): + var_name = f"qc_{metric_name}" + if var_name not in var_name_to_idx: + logg.warning(f"Variable '{var_name}' not found in adata.var_names. Skipping.") + continue + + raw_values = adata.X[:, var_name_to_idx[var_name]] + + ax_spatial = axes[i, 0] + ax_hist = axes[i, 1] + ax_stats = axes[i, 2] + + # Panel 1: Spatial plot + try: + ( + sdata.pl.render_shapes(shapes_key, color=var_name, table_name=table_key, **kwargs).pl.show( + ax=ax_spatial, title=f"{metric_name.replace('_', ' ').title()}" + ) + ) + except (ValueError, KeyError, AttributeError) as e: + logg.warning(f"Error plotting spatial view for {metric_name}: {e}") + ax_spatial.text( + 0.5, 0.5, f"Error plotting\n{metric_name}", ha="center", va="center", transform=ax_spatial.transAxes + ) + ax_spatial.set_title(f"{metric_name.replace('_', ' ').title()}") + + # Panel 2: KDE plot + x_min, x_max = float(np.min(raw_values)), float(np.max(raw_values)) + + if x_min < x_max: + x_range = np.linspace(x_min, x_max, 200) + + if "is_tissue" in adata.obs: + is_tissue = adata.obs["is_tissue"].astype(str) == "True" + tissue_values = raw_values[is_tissue] + background_values = raw_values[~is_tissue] + + if len(background_values) > 1 and np.std(background_values) > 0: + kde_background = gaussian_kde(background_values) + density_background = kde_background(x_range) + ax_hist.plot(x_range, density_background, label="Background", alpha=0.7) + ax_hist.fill_between(x_range, density_background, alpha=0.3) + + if len(tissue_values) > 1 and np.std(tissue_values) > 0: + kde_tissue = gaussian_kde(tissue_values) + density_tissue = kde_tissue(x_range) + ax_hist.plot(x_range, density_tissue, label="Tissue", alpha=0.7) + ax_hist.fill_between(x_range, density_tissue, alpha=0.3) + + ax_hist.legend() + + elif len(raw_values) > 1 and np.std(raw_values) > 0: + kde = gaussian_kde(raw_values) + density = kde(x_range) + ax_hist.plot(x_range, density, alpha=0.7) + ax_hist.fill_between(x_range, density, alpha=0.3) + else: + ax_hist.text( + 0.5, + 0.5, + f"Constant value: {x_min:.4f}", + ha="center", + va="center", + transform=ax_hist.transAxes, + ) + + ax_hist.set_xlabel(f"{metric_name.replace('_', ' ').title()}") + ax_hist.set_ylabel("Density") + ax_hist.set_title("Distribution") + ax_hist.grid(True, alpha=0.3) + + # Panel 3: Statistics + ax_stats.axis("off") + stats_text = ( + f"Raw {metric_name.replace('_', ' ').title()} Statistics:\n" + f"\n" + f"Count: {len(raw_values):,}\n" + f"Mean: {np.mean(raw_values):.4f}\n" + f"Std: {np.std(raw_values):.4f}\n" + f"Min: {np.min(raw_values):.4f}\n" + f"Max: {np.max(raw_values):.4f}\n" + f"\n" + f"Percentiles:\n" + f"5%: {np.percentile(raw_values, 5):.4f}\n" + f"25%: {np.percentile(raw_values, 25):.4f}\n" + f"50%: {np.percentile(raw_values, 50):.4f}\n" + f"75%: {np.percentile(raw_values, 75):.4f}\n" + f"95%: {np.percentile(raw_values, 95):.4f}\n" + f"\n" + f"Non-zero: {np.count_nonzero(raw_values):,}\n" + f"Zero: {np.sum(raw_values == 0):,}" + ) + + ax_stats.text( + 0.05, + 0.95, + stats_text, + transform=ax_stats.transAxes, + fontsize=9, + verticalalignment="top", + fontfamily="monospace", + ) + + plt.tight_layout() + + return axes if return_ax else None diff --git a/tests/_images/QCImage_calc_qc_image_hne.png b/tests/_images/QCImage_calc_qc_image_hne.png new file mode 100644 index 0000000000000000000000000000000000000000..c02d403082d6f656fb7ebf0c8ce609eba7424ea3 GIT binary patch literal 21230 zcmb4~Q*(2i^-1qxXRWH@OR@drY z{q6nzc7&3GBoaI>JQx@llC+eV%D*-5zXAL0-yAh#!SHY4aTV8cRdq0TbvJT01Cuv$ zb+mPGwY4%P{%PjyV&!1Z%E-dZ$VE?V>FVm}!o$R5_kTMW9h@zg`gGl<|J?=7QA*nd z4D1`#e*;`zZFK|;OhrdpOjyk$>#_^3%GOGo@x97(Nw4`A>*Hz3Z>!$0NBvr%jEqbr=2k7{X^XLpnD-ji`Batt0mko zzN;r+o9c>2#V4!Y8l>Od)2ICE)jH?8S6(B>zWSBakr0*5IX7t`AoiiP{9cu=+YADC%)D8`ZHXYd?bl`f`#Wg8lHOne1GGLlE<^Oxp|7&|( z-QV9=0-Z{9l^=wI_r``; zCDJcZzPpT>BFDIO*+bHLMr(!hZgt*ai zm}{ne`uo#E;5pO)HR)|@`(x`lSnwJ0{bT!!9~lKjoNHxe zg=oNzt9?S}0eYsk=Y66FkTm{qVIipd-nH~fiY{ag#dDFZ0OS70lS==wAxrqME4<#p86IB1_O;zuO&OvfFP5lBjoi zIrUkhTH_qyBcbwvyXM9tdc&cI;CMQFAT1h9zs(UGMev<@I)|Tb&Y0ixZ4E{sifYpb zoS8?{=bg5r>eG>bqC($bF;+a3u}@U_O*Am8N8pxvIpNpAQ|#eroM(c8oBg3Ap{Ks& zkD0ypA6M**e%5Y(mK4QbtT&Fhbw}^+&k%I9p8mMh`g|m!_g=l^TqQ^W5k))qzSqWY z`#xy;H~Tba;VXKL+nsrLao-yB#WWszzu5KppJAr0#k~?3w7nf^y(b>F<5&8{i36HT zjZ*pz{mPIvJ`KM9xao1HbxIxDe0bbCvFBS1>e0hX8^uxx5>Ftk{=6JScyRc1zIR{l ze_8$<9~c*<7-_5w4qv)Cu%U#|%05;momrvZAocBojt5<3U-=x*`Dbl-Q`kz3Mh8ru zBK}dE!#!go^7<$hfg+Y|Aw$DR6;{p$GA{p#;&_;Bna90z!CVfEK; z8zRNHQ?Z$m?q9;y%l=l|_{jSK;nFSf+-_UqRKC7sehOLUqPo6}-`#z4nB+pgtb#FT zZ6fnMzo$%|(b-z!L9u-BR{pMX-MzXR-ZaBAZoY<3BgbsC0pBxq0ts`MdE_XDy|!n# z9n@H~oZvoTv*bMqnOw5gIh!F%`(?28>~*1eGNXRX>|nX{wd6fDzB*RItUHS<7y8z)66LTH`C$AwnjZ|SscLSfI+QtUVwJ*_cUQsR%$2P&# zskjo>L8r)p;0RdcxSm#CwX1e#u#uMfE;r&J$2!4OAtRm)Bh0%n@HsYd>~E`U0%En> z{mX~GP3uS(f04ukmbj0wkxIiY%6%~7272rqh$58;@uPR!VzVGanL)$p3R*5Lb*;R# zwd&au0(vXMDHGm_!|U5bW88N<1={p0isxw0IeM%_Mb($0XjLCwEYmsL$u_&)W#<~} znSyutF_TVL=MR@j`QQ^f3;nh{!RPIk&2}u_Q(1`^_O$7)9d6=k-0$M)4|dnK#|<020?#Aqgyfif|O;x+`-G#DZPvMcN< z;jqS?62lJeO`9rdM=xCuf<rWz@0qhGoEI#VAvWs8$pCQ7bSiE6 z!Lv6*2ddt)P1+j7BL_B(;td;x@MTb9jU#6gsMtY$Hr-j4eZ^~Fs zp6_yBx(*XXQFOh?}^mpO4ye(I7=cKPWF{N5_~snlIQ2Cc$+lUa8f;c4rh>8x6Lw_I

O6WTvE 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 UOPaB~rzdf=)bvzqlS*ESY3yPSB5&;GWasE+ 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 IlrazeAIscLn*aa+ literal 0 HcmV?d00001 diff --git a/tests/_images/QCImage_plot_qc_image.png b/tests/_images/QCImage_plot_qc_image.png new file mode 100644 index 0000000000000000000000000000000000000000..d896a4b705a02c2b6c57fa954cde2e114ee2a514 GIT binary patch literal 17741 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 literal 0 HcmV?d00001 diff --git a/tests/_images/QCSharpness_calc_qc_sharpness.png b/tests/_images/QCSharpness_calc_qc_sharpness.png deleted file mode 100644 index 46971dccb2873b8a13fbb80cfd22f797f100a88e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19379 zcmbSSV|Qdtw2p1t$;7s8Cmq|y#I~JGY}>Xqv27<4+c)q1aDTx4P+h%x)v2@gKGo|y z*gHZ|UIHEl2L=cT2wqB3RQYGl`)@!&{Cq~unA81C+|FVe&MNk1&TfXkO@ZVLogHlK zooy_Qh+Iv7J6YP>vCuOy(sR%fSvWg8IB_#D*#6%Ndi&qz46^m4sz19xJ4k9e0Rch$ z`fmWssjiL!0m%?bi3+K@XI*xC#M`a_cD~mJv9A^fy%J5wV~uEk8SL*085|0w&`yXL z3W`Yz;ZcS~w?WBERzLxyr~xMaB<$#b&;lr8CNUdHWT>?MF$h|lAYWzzlc-frEQJPB zIfbp-_t=xH?v?Af=9%_q*Jm%~z@iO#PeAv|)=5t14F7voKOm+==0Q= zdlbUpO$@}~-VfVxko!7m-f^&^@5lGwH=vh}+V_sD%^BaPM(>*-!ow78yS>cb*UZ=X z)Sib$`7fHElsbg;oLBZl#`*j+2J-$mAk^pinv3&A^GEc18RUImn}OKf_PV~D_E~Ps z8P?u;Q}ew#@r`xsxzFZ2$=P4?Go4TR>u>LSpV@vpg8Q04_?qzRo>>9wv%2@O^`-Uu zY3HtEr5nMYb^_>S#QsCPqGBJt)*bx6uwPs3-`Rn#dcS@3KF`&i_6ET1+V&vqIxpGv zo;y}z6KD!03##6KG}b}dh7;3=2Xe>87{KM3}K0h z9zNFkeX0*-_0FG3CHcGU_;9pt{)PqO*m6cC{JIIt{cmYgYwKtX$MzUJ$7WxY-UCTZ z$9aXE!r8ayox*b8`};fZwZ{yo0V_^@Z!cn>uG`k|AfkY5?~XYm7O!UNw?Ij2*Ad;; zwcD1<3WWLH;k*g+o^|h&b^CFKH9L3QuNYuAS65C>PN3bJoA`^G&YJgsPPy-Rgl}VE zoNiy6rs7C_20(_*eu+}K&Aqj`y&vtKA1Hnq$;QX0z{S4yKu`;7|AMWvS?jpiCz8y3 z=iTHBWc+@6V|@A&`j0!mrR{Woe=(?Fa0%SEZy?g*l3av8s#D*OXC zllRo{G%EU5EyydKk_n0o#l_d3S0?vbACg)BI)VP;x|>b{BhDmJfSnZCNUCWiF% z{G4K({ZIpRaNBP@{a&8;YYO5c)cyqmBMO{j+nsdA@9UnyP$8Yv51WsoDkAdeKr1=q z_u)>Ws0r6Hd5y~-FjILh?#%>9%3t0@Ta$-FGR-^4QqQZpRoTWkGR{}?^1^BmoOcQ& zZ$7}J5+`PA9`7%oStOqGWJG;_FNoiJjyY@mKwJed3r>-N^%C3ess-UyUpmRZ--xQ& z-UMTOkq+*lPtB{Om^85+0E*4g^L#VT-E0w7M8W>e6jFJG3VzuvJkVBx!ojTS#yFVWl(4V$DOa$E67Ij>ztvkYld)R82GHMnFjMntR$4U=i| zm~aw4826Ds*H%DW(=BVAcjT)hXwq7lHG)UA$D#@kna5(00G9MBOLiF2qRk&5$(IU7 z%n?9?ksPO$mYTOEmUdaf!hKWBV+RMZgM7!GJMvZ-$|^E=XTpg|1XH*$qB8Vq4N|5x zX{j3~NtMD$9{vK~uLX8kjx|IN22nAI8zE)2+FZkNJeM&Dm6D()p1cuop2U;oN7si# zO>;p6p%_09qqUc+AHzwIE0K8QC~*YP-cx%hmWM&5yRj1Qcaw|vYsu8=?`9X%vWSX~ zLj2W>WeTNz*0ApPe~!fh)jUp{qTsY&3T+I<3aPN_Xn+12jYZn62Yf1H6f=w!Fp;81+@pKF0d{|9s_1q<9FUYED{bQ0!(ESKyc} zSe4f(L~w7H!Pu=Bac5VORf2v7NVg!VRy=xwFBSA-m1VjL;V+D5U0wnM04#VPtS<=atwApNIpDoY*T`)v1hxQoDXMKe%RJpOOEi?L-U2|te#S8ge z@UalyRRMA-X9p2Vr_VA!p*Jm+YAyb|LVzY#PT^t_k7kO7@WE&VXR0tudzW6RMmY2s zG0>=abw5Uv?3-dTC(0A#=6~J00U?vm753Dg`;V6O>+r{o1}w4cDZSwB9oQxl(iTD7yva)i-19c z)xkXZnDrcT&11)D5+w`aK5|6z;P7XJ6MX&vG}(?7e<^Al z<`$=d(gkp_bgD*ek$TqVa{GLxW} z&fp@o`XG#d-z3p$JV)sH-7fpAR+8IecP5|#hr5l&A2p-Lu_ciYZ|qDcCLSdO#wlI; z#$!=<*Z97tWrJA_lS}RY&<)_ki=wWZTXLVd!@9;|x9H9YqLK?u6|O1o34lF|ndOuY zo0t?oTcn;z(Zphj{$0f;R#t7lIxHG<(zd(T#l_oLGaaU12f|rWl!mhZ1E-;@fU%~F z@(|NASp;B@bH<;#>qU7{k$PGnsGP1kYg&{Y&6_WJMVs*ktq+Ukl;wFjO1S z!j+73-E-h8{UkfG;!eN1w+pt@t8|iDu^KEfO4(_h4IZ=U z==0DOhzN9xEtpgY+Nl>W^#_(~NL|c?6$0#9dE36eC-^&Y3`qO0SZu~ajo?-cM!tq* ze6jJpscZ7BoP$Os*QG5}jX<%hH`7E(_GBAoT}G29kzu8oX5A(>h%UF4nck}n=tthB z7}Gn^j#T;iKH!es-ok}aCQNcxxqy){fwxV~?e2$K4#Jh)6$ntSc|FAQKI?1}vS9uB zca7iRWf$y(^H=aNxU2_4C(}|6Y@e41;#fc9z)9kJ<|$o3O9g=?GU1vm6}b!nm*Sa# zM5tIi3ruImP)5YncKqd@WgsMS``)uHzc7b&RaS&rV|heNUTV20%QC3%%)jCS@Y?d$ z4FENvI!ZCG`gR>B%Y$0hjP5g1YHVif>lhQKeG9rP16iZAjR#f%ky&iJeuVw` zzgSxfMhEEC&bs4O#+j3&21qj_=cj`s2zZ2J$y~4b=zSJ@y?)N+lJa(so?`O>T9x{! zSAwaPuh~|agQVeowXp_h{WS(iP-vjM65awbM%eJa#>H%QhzUMgNH?a!F}!Dz9(8_g zXkoD0mK01}k`JnCX)DeMWKd~Nc~7ik&!@v(zKo_ZGPywkpWu&RRY;RUehMZ0lh&PS zvvRYEr^ki-=!4|%`ws}o=)WoZevJBMb<}ta;%@g%=shuSKee<1JKXMz>3zSg-2b4( zKRj1$p#JYyopJvm;;w(D3>QRKzY>c6C^eU{$}L+&D=?yowIoI|OBG=G_|h0-8Bc3Z+qz-!Q*Ev%wdyp5?a=U z;wzbKn^dVi@HYAEJ*uB3W7={%B7=}VX)2L@4~#5JGR@XJ?%Y#mexNFzknS@Vcwg(f z_PHjVangJJtN!~aS^f7QJjYF}TUXG3DBgR;%?DH9QAJ>Q=at7V?7yEjT{mpIo|nauxma|42?=oOrbwaq4pr|`z5aZ=PAE#Uw`U(b`uoNg`YY9Efx5*%=!K}0fq6_Dek=L zi1NDbPW7#wxtc~WD67mMxi*CRca<}!If#A&Ki#^Z{U|v>cANYk@J)#*>V|<2|9v3P}HxR z-mmlU#sh)t5-1+b$tJ~ZemDr1ENf|Ft!{rfM&~-R|F=SFe*@h$ISsGysD931F`ccu zi=u#QcUJVnV}*$j1oIhD9-QpLan1h+*L$^HbzH3O?3i0y!)g>{AtE4P#fV#v8bxop zsrw0tBQs_MHi(1Hik+p&9^2S9TbwoL4JmzQM%#(_(wA<_7=Xf#dJ6GItdM5Isen1wQ;%cA|O8N9|=jDs-3?}z&8lCYXW&7>Uj z4H_Er#w+(?yMj?zo^h}vH<_P99zDc@D}jF|!*wVBaz7kuIu>4HHKBUOQ|8}vo|elL z=y@pgs5oW9zKPQRAQ5;g6!^=t^D6KA66WU)f7I33IJmrw5f&B}Enaw)rkim66kcTy zT%P-~_-nPZtrSMw;CfEPvpW!yz&%Y*;AE~_Vr>l{zrTcH$y6RkePr5zQMXXEG}{3jFRDdwSno^tG&#$vhzJV; z>pT3XHB9y(f3W2c4%9u=G?5(9h30+}FyvwAemZ$~rDvNB-|??BQi4ZE4BcVq3M-BY zRPFbnzP%U!ZVALO<{5`d1r-kOv#9rfIXfj|EM$DW3xcW5U_UHk=bmq8P~l@pfe{bE z<2Fiv(rf2kPc%5r@8f{?glpz3cIw0l0TJ=+`CaFSj{V15PZA^u+HJPNn8fFZs4h)n zh#9)f1iCJa3}oUh?g%#0QqrbrpSSm;zf7x@EYbYSGPqSEQII!qP6bJWEr}MD06KKD zP|WB$tg8@REQcF8|6t-SwZny1t%>{z+wH(JyK%}%>XpY)fEVr;u~zABX8s*Oaxn+p zmX6|i`~LkfTAD)C9I%C_!*F~|gkOVxP<@D>vxv9PU)wteEgNaRy{XpEguz;>iS9vEa1<(ljrCIsCTEq1ZJ!+@^&%x3k{Z@<(3M8k zMnRW+ifK|NnE9W#x{euTZq7jq-Xp7#7ebd(a#oLZc0VoHabPqlF-}D{M9J{8S@_AKrYszU;q!2R<$Mk%DIE_@jz)7S6gCw*&(lC6%__68SE$JLzn%k7}fk&IMaWV@z zD5xJnAeh3zfsuHodqg@@QpxN=1>v)YVH2`X;q%bXw}9Z{8#y~rHLItdJ%Bg6F%rS@ z{BW+Cljt(8@TsGZr}rzF_eF6i83UD7mY7<@0w>wEg$WESRWOUc6%08eB>pHR?<+Wz zliI~3T0O!TM#ctAQ*;2$K^&l=I!YRi?hy?YB_&8LRM!?TsEW*^Tw+eqW;?nsEGI}= zHQJVPyUrMOc4eCm+RIy=gKR82hFvImPmzeO2`R=S*HmvYtGobpa2s7=g+hcnVjQx| zaM@Q}#4`}PWsp=gD!h1+M4>zb%2Zwj=P)k#mqqNC_9AhD7E!?@YDgWD@RHtSnR8<( z1?4Uvk#3h6K2oM(SjX;mgMf;5c!4DnKW+-{b%EE2SUgYd43ows^6nmcMC7_6vBlSz z56PA@r?dOdfEGdRMd+Dxtb+SMNd%1K&6TqL8kTJ|uuHWIc&JDWaEe%te;M{t!PPl& zhkccF4`~p1K-V=?Oa-oSpD3WWWj4gEScH~Sq{FE{s-MdtFjQkUR~ec)E|^;wKSBIv zTi6cSJW++zWCKGRhmN`;*c~Z8-QYK$`CD-$l5-#mtI1(l>_3>=lh{IL&%&~+ZP0NvyHHLeUWnsh!4?P5wKw3vj4^a+@I|zn* z)`%vjjbIB`J6Q)-03xiUhqQJDi51so_iAof!vfxjEM%EEhQg>R=^W`L4b#5%u-;i` zmK(#YHBlDvbZ;Y7`ow4VtCPy;E?5n!q)g$2@XJze)d1t6XR53IVVI>`M#XGP55V=v1z1_?6c$KXY#Py_G%%I(5`CC|4GyU1gP{sqvijqB#=;1GjWcPA zHptd%)tE;#mdU15$xd*JI4L#a&O@YKNYc`dV4rJ}@uvkIB+YV>Fk#7puAvhMI0ScR zobN@t+YWiAx)0&O_g{S0b_FEzGCPT~sYXSaOT@xFsrm z{sRW9#yw@Hh|*$q4>B1YnvI$%YD#bIM!3^gxhjAJl19nu`M(hm1=TW!QIsz|m66fRs|cTKl$6X91t?j)A$`P80ZjKGlT zq{d(k6aSt6`@=<2T0p$uhm-N_8KPL)&E6Is_vLi1=T!y zq~_>V=KP1KZ4xPcAzQtqDu2ak5g>{a zPc$Spbd)il)NX-6h7+jLyo1pZ=15ecVbU1hwn@^AL=(`{9}T*f0Dkn^mH9sB!Yw^I z$f8Tu@jj_FVW3!omZ!;c&pJyL@6X7eWmIk2Q$`t?b*4(Qo#jM>SDI6V0aL7+Wg&RE zq~8^`he>c8Yh8QNW6mo=zCf~O3Sr6^5ETZ0BF;r0&W##aGKqow+vk*?Bb@C44{cf5 zeb9Niu2r9f8h;x1_eFR!5;YwZ+jNETLfK{M@HNB^M_PNCR;*QSsQ8T!ppias$5HjZ z%ylcDu~Tu*Ppf@Dx{AhdFcL_1a;xH>n)!;$;ERK}lG#w%9+q{AB$g9&=r)*b3NSdM zI1g}9!x(q@b_c?0GJ7k89DceyH!a;ckKVIYiGCWBUzzy%A4uy5V-DxGO~zcY$b>3es{3wRI^!^lJfqiB6&FEkDk++mpstC;tp^U0pIMxM2U2-c!WPdWWdJwLswK(*~5E$f=4D2!~oz$2*N30j|)NxTPepgGvb(&NeUnRK*#VT{~0urk(9xc6i?;a$2v{e z0YZ5Hnw%vMOu7sDLa0VfF%^yr8NYJKFSL1MW&_=oO{WKg!{c zq8Z%Rey?o>o4#aEnZ5){GJY}3no&JV+`edtT0X$jyX!RY>pw>;0+@Q+Z4;% zpa#mwy5Ak}NL(|p8&YMC{e*97*~V#S5oo4zH>oV<<49)FvwD)zP^ppth%bA!JZ<;| zksO|nOeUBmdLH(ZI#)U#ZmzUr6mnPMWYb^OP4rZwt!e6$X6Q_!2n_OAgwv;wuz&UD z=(Pq$>~g>fRlvXh&3w_?yAR^JPiSxdHrm+gyzY5-3{c%Tj=1n&)a_%$u|rlS!uZ2> zPdjL8_sJ&rh*pVK2x0HVfW_$7G(ypQ(v?3=`sgu{j3HYHyyHLs$n`0WRuaPE(17i6 zcvDojsmi8l1uo1}VdaxB(_9>|!NWSjSP*TgMLHLMZzW3)zYlFI?l(V3MSvE%Tv+7H zA3VZ_T^72X3^a4k(u1)pDtLYXUY;W!gzZ^Vg!t~IZlRU!C}Bgo?LAAPI#S z)3o|aI7O=V>H@26#kMC2%F6@eo0(^Sm6Wq48U7Zr`IAmXOAJ$@3~kvhEoC1vd6+>6 zf&VMw5F^8skqW(Zb##dh*!?9xjEi@6x92&w1R70j#9&sWq@qUw6pxTV1jP4TC3pWn zQ?S-+>!R%>$16>~wClP18hqF1+KceVQ2RW9C@1)}u*S6gUc;sZ$AnJOY`9{2b*hXJ zb(_-9^4>PUObJ)$gng7Ek|D_guQf%{N1`kTB688@S~;3`gB75;<*>J(bvq0rfZ8G} zzT;_jA1a2vI59=5ioB5bE$SeCYRtSe)1WP=n833PPkvNQt##9|3-d~ug+(;_S zhY-#*q^Wby!);&(b35b?E$Qp2l&yYasb})=1|Af^?DVHt0-v-LB+*Y3;^Wu%VeU=N zb641p0l9Ms&fpF#@De28UUhk8+q(7CCRj0Arp=eXGVxe9B#(g>dE5*F>A&Z8&hL$;pi?;1| zt4Zw08Pu-1fpt8!6)ywSzJESBQkjK=k_h({rxD#lx0$S{GB=`X$T3E=7}-(*)>)VP zMg|JRxm1xy(_ErIx?C5GDx2g0Bf(yub8NE}d$|8I!3iGb^Dk9Ewjlp6HoqM>zAMgM z`_E;2;L(k%V~^>X?~j>U{EfA>J>6^171L2`Opx6hhrb4j%$ampw$D(+4Ni8V8x?Ab z42)!9gT;yxn2zD`Z17WEW@fm@;-?;U#fAY$nDB#!;%qDK**b6UWq)%becCi%oDNi^ctOm>Wcp4I+Q37djI2n_5Guu+-fCcBLEgC2|;|H$f~ zggullBJq(>hbHp%81zJh(?L(;a#z;580y{f^`=R4oZZhtpcN z3YZ|v)O{;nJIxV1cR=jYxC{yy2CV09+t)w3F!w|uCW7*>g=DAVn2=)7YH8-1Nywhb z3&J-rKVQ`YoWYkQV~X5tC4qIMk_^a;?1@v|Y~xneO(SJG$tn^fqKnE@8|KU(HYq<3 zRm?I8emu_Wge6-TJ608IqF0>ROoS2+HAlT!Df13JWC4|IpmGV*0v=t!AY9T+D{6so z{xlK*Sd1!qXU}#qnnH2k>nfyVIsP%x#KfQih70D$gk?(oUk!p#93HzqCkAg}rZh==W*4nN6Q}{Rx zk*i&>bP>Unme>C~32CYu?zr7_-d`@+84zs2raQ_@23TkBYGPf8Pt)wy&31($ljz65 zuv{!5t)NC|3(YlMda-j;W1-J`e({WS%hk+pHW>#SBhrWPgN zwoGt5k@IPwk>)atk~BY3R?rUg#J;`Do2hh<2VG4w@e(3CD1JOu_H6UX$om%I%x58a zP%?dIc7s^7Mwyh3_|U!plrHiQ8fpINGAe;yEa+t z&_PZl3Sd-pxZQVzB<&2W1opFixDjn{J4e2#TdnWC&{*$X=($~ZI46-EaqsX$(5U*z zKPg4a-BRFpde~DGJWHMnrbx!cF5`*PT-6p_rT2MLHo=^jQz%jwer12G5Bz1At$Vpj zsoKBtifElmlDG~;wg#+=Gh7auZs}hk8aGizg6GO4luRDN_;k+0HnZFJKcxm>!uw=) z9`$m*dVBgv(8D?hkVzNg*pa|Cp2ue~b?j8!RP#_|t3A{Y=(aErrPxNL@j)`J;S@2} z)eH%idQh#I(w8FJT0raz8kW))&jf);gV7Z2y5AH_SvAd=LKgg+6xLFExDBRgk+PZT z#FzEBH>#(U%L5L8ZvWGi{ENWteicXDOAafNMv5lnK}+#amc`M_Jvw zpPYx0mR0>Z%1^GwU*XaqrmmYLcKdO)4t~w`Z~U%s1pm2d^LKUWy+hAaHMH|-+#wdB-*EiQ_-3>IPE`6 zldmjjg;hDE0wGDEkm3nRuTm|!Q9%mhNWADoN7k^tFAcAVi_$1wh5$MxG|QU1MDmE4uY#Rgr}M~Vlu$l1FPA1ja%?u#}*d%LpSqd9AsAqu09$#Jnlt=#JQV@80S3hV2>6n_%9>?70EI z>VDSqeQ)*4LQaTV}?4OrKda$dSX8)D|B3 z?U4%G^D+EPyVi$%0&Nv1jV^^Cmeb9)F@d!?{rX~Ff7t(#OK)KVJ2|g_cM0q?#TwM|$#>luHtD4l&*#{+4e1d!Y{+mcH!@*trlUjpO$UlV@V{;l1Ywjb-YpE%E3ZiIf{*WW(dAIBW`g+D5MYS*M` zcD!5f`<5&Ps$KgLhTd(YI%B%**CM`hTvc*KZPTzNTtL^@%{ECwKYyNA2bf zHA0V~rPr2nCU|-oQD9xTq}@^yy4<~?Dh^IAUP8JViB3*OmRZG{NHuw;P}72iXd(-U zA%Vk)OL(GIJ;a|*J>!U^vSz|`VhY33;oI|1anX0=NF)#wP=z!w)3M-+lwkPi`pe_> z(3(kR%;~?}@zlqb6C=eyn7C41asU*xAh39$b%Yy0+niTg(OyOLUsBhf z5_psNJ{= zN*WKQsc8$uBHj7i+S!6GkeM?D+&n$8|5ru^RO>!W^YTzC;3IW&c3wZ>BFQH`f(7wO zFka4W+SC@B6_OC! z&2x&a%J8!!7Gkl417(aN0a|Q0Pg)E%IY4c;+qj%{&!cw!C8F;X+YQ~$?=^F%n`m?1 zpw^zp>dl|ZNB{N6?>GH#Yu_74zj(ioxbIu?UN=qXm!Cqh;QxFGpv@mwuj@?Ce!u(L zhhfHR`#v^6LH|Bu1@9u>bBgW1sY6usTj7b}N<{cM7O_;LaEdyLxzrM;CDRacTsYQZ z`vDFtJogI~A!=-JzAznpGliY1!GckPW?oecrh_z*43UF2kpmx`itXNz9KB4PstDBk zl?5^B5#NL=s6IR`e=v1@;dp>lQ4Z8&Cq6Z$P4UsTDnNQHG{p)>^^Pm(IkZl4Et(A2 zwgK~iGoGhTVS>_UO=`zfwXY~rs4@7Yb~XeSGi`SEFOOXR~YgfyFm#eD@ z|78HPV*PVTA8^b=@|p8kWq!Nd+T(J$+WX4!eZ0zO@AtmJ;21!ztH#@XD#lP?iDTFE zYF4s9^}S2@5%jB00=>M&dEamp>K%zOcQy;&V?C9gk;$2N6`hNg;KqH&gc6d@ z9_ymA#N}wJG|h0j2`sm1O#;8l z9Yh6QIO5dMJ`IJr#wA$3kqcDSAHz7AOUuzK+&k;qVlfnNRAqUXpb4!^q8Y*naXo_Q=#a5f!Qx+m-T-g zxvu$EDmfGN1P91`;7#qSI;HYtrkh~DS{gk{lE6QOJi3H!=rGFszV}@i3UMx6wUuG# zG)?m;Od);)0PH1+6Aubi!Ux%9)f7rX1wD$xv%%~GW++aHZ!h_D-p0Qv@eWV$G-u9V zFhMzOM=z?0Sa_s7R{GB@DE_)OCbGRtvKdWN;}miQ!eSnlchc6jnJ6iG88G9t~9LBUS4pl~lY; z(~FK~2;0dLr;-J5z?MOk;cw1C4~ZP}Ef3q#Pq~G1Akyd-7u>ggK|+h1(#y*U9n1`4 zglpvwYulpuVXefWXk}67Mfci8SMS1%4 z`5OLx{S&PIXS#xuoo52p>=c6SIwqJ%Um1SdVqLvB%{DW9ceo%QG;0i!tPy3C8fl4~ z-J)KyyjbN)x$+pd2p{Z34#U0wj2xsz-ds&N9WM>LmHPU8Vlqaqu(0p0gHNTIC=!{v zR|}rW%lraPU!`j%S7@a)L-?Gh;q{WM^S1H|ybJ2`pQ~f^n{xj{T(d^5``19MVA>Vo z^SQFpDYS)18RoC$$yBt&m+V>}Ca+w)q>~aFVFf~}`C<$@Sp`1LeHays0`|QJ96%IB zvXhm#NpUd*cW9v65^){8$ui3E`K$D-ZNw7;+2WD9oz$!xaQD3hhqMB_Xji5CP#EQVLcOSTcgFWjt+&J zgqNs0VXM>x@s|q7>%omg$NUYmVV#Y8b`05S-P)qLqJW_^yx0fR%-xsq_HvQMR0Cuc zNIH0zrWKg+C(x{BC@SZ~#J2#6J`G}Vz_lYiQD}+O*gs`ps0JjXzal(+ zc3f3q-W>Y+)xn@$Xrwr3Qmi=1vZXA48-_ICnU(<6M2MW|laN0uR4tFAbYK~4%yEeM z!Z{R-XvZSpg6qO??{64^ArdCk>?CMOHx?2@aRU#h@gj?qjJu!J>S&gH|< zx@bYC%~*QQGJ7KaYli6kTKRrU0+U=i76oh-W!*$$k-Wbj)KJ?!ZWh+s!U=b2SdS31 zD8ibwT#VAD6LX7%TSkS4ttDf-nov$-;-H{tdfd)ak1=C; zCZehIu$7b7j%p5?FdOq%%BOxEF=5?oUHft{_`OcNZtZ;PHqajYSI(8AA!wx)G2BHm zfd<;vT{t!FHWPzZ;#ZKPQImHY>Xm!X#;a;$OmnHMKLa9HjSJlZzQYPr&Mco^0S!Xn zou93I!A&v}pDItj!CU5(Kt1`PXw7#M>Rm+DT_VNS!xtpgN$8B7(ZrK@3$ehFmO#GY zXqCi1zw{@#%=nPc)>-cwu+Cg${0GqL*UW9=a-ZP>BUe;|BbG`ipZ-%W4nh7Y)K1XM z1@8Q#=~mRfRUjyG6U4nH3B3MCJ5r4O7qx%pA0HuEUC+Z(&`N@e-+cWFvSD8Q-%F+E zL-?X_Ge_ZCW%+Y6`#d^^J|%q}0f*?^orHQteVoc3s+CwXr9081nUs&VmLYCt@UYC> zNx?~-TDNKH?%T2^owM8eUFsib-XR$^sHE3>7IajQ3p{gVscS@YK+*Ng|kwcq!<$CXp=#D7f}UC(iqxaS8T zMJYb6%29eqB?nx2+Rl$&;P)QG2CFKZZ*wl%9_)?;RJqzHH1TLTmaS{dlIP6@aCv_~ zI;aeAMQvHV;4|2y43?H1x%@77O> zp0LoInZ9EytSP zXvr{3DYMuv0mYXxPipZgWtiI5i6$37xkIicr!(g^X5aU(t2NJ8tHVljBACzhiiE2I zg6>VfCh_TP9>~6Sr<0ef>#wU{Xz?AbwV6~5$LPeO!r_PQ zKQR4o1JT51ge}>+d7&YK;jN+=n;jD9Bd>6b8I$s|up~jPQha&8vzK$*!2Bd=dI>!% zIVNlBMJr#klJhj_v%WutRUYR`br+Ov9@ko8kx^N@!NT7RbhDxWvu$=BjuaXWX*RPnJYw({uku5HC?_v3CwkAA zDfzDcXQf~A_nrP1EXR&F%k!oaqTf;NNAfSEco;G^ji*RE`?g#6(4W%!K}*a;%ke5d zbf%lc9Z!Lsr_A1Ix36t{-h+U!_;w_xGRy?e3ZxG+4Ar;1Zgrz#3nO*v1y1X7k)b~r zQd0wszL~jd5Q7zN7s~W&8sWB=c92K&;4Wgq91@Sd(6g>04H!2mX7rPU6=j4uDtby* z6G-ziOW>*K^d+s8(JkB9E_`1re5$3CS+k;d11g8u2ZPvAVoOrHVBKw-2Y%A04JZz7 zZmItX1JMVZSQgn(K<}8_J+z|>(ywhiV_Ey!HlFM~z&S=lNk$}08nQ%yRG!rcj zWb^8M1Y_!V$tMFAmCPoDzWiNu2oyN!UkwYkdRe7?fs?U z^lp=IR_m0aBbgSw@#hG(RMh||xc!Yr-omU=)X8QQ&erGh=^pe#Yy@n z@=Lf3J=DH|EJoM}Gf5BBlaKof!hG2@CfRZIM~FbP=V6x~h^naZ!O&Ie>h9vqCkfWi+m0Ue$pBL=`|(G(f2MYeXZw z=JGo6iu5*&^D-+hkeSJqZt|p@eHi9*a#y6!l+pW%l1bmFct}K|&}U0b^3$x~Dh2G9 zBolD-ZkHXdHXY9KJ(}$nE~>U)Xq?_Xt@q362?d2agGz;I(S1djvs$3YWC#5MS+YKtN`w4r4V42AMC01o%-6=9>)zXiNYO*(T8> z);wXjvcLG&C6;^cQG%Hp7wHD4ShLMLpD-cp(b5cjDlXuqjDYK7;igX>a%=pT&kE6_ z!l=DrKFp?-O{Cmb0^yOuO_meIgy`8hAzdd(7a%w$v3#c_cTy2_D&6}3;?w!jW7mju z;u#LeP@WvPI&vcwD*oa6X#?}9W|)rpH>AWal_?<#s~VM2Q2{M9#w?+`B7>{APBK{B zJzs&C6#cx+nL3XW$PLCy<&EMGC)Osp?lNDXYJ?lLjWTI{I}G_Kr?M zg>BI#rV+uMu9$6%)<~MpHw3^yt%N@?}-}P9tm)O+x3t z)-oCril(*rpO#(mK<5qU)7aRKbZNknuCUJ%f=;t*_%0#mP#kFE=0OZM%3|mU)V>t> zsb`JlJDv88$mnT>un^3bd(*~IXg`Z5T&oOH{*^t`POYZ5V~Ln5`@EM~`R+o%XtG^7 zpmTNe2y|rPz_u(zet@tUvC}@>`B)~?SFi9eD_YzK0io1HZy{`1t0W2gv;Sj!NJk;*ib8UZTRS_&eqgQpn;9*ne@1-FVR(aj8wq2?V_H}i~p!l zJ4-c)Qz;?QZWgt^;XxIl+%efwz_REg5$Y7lx;K7k`#Kk?{&^W{&`og(y` zYOK%CcS{~@>+AoD9~-B)l^SO{E$v)PR~JO z(vCr@=E1HmW4>URjp!vz)~nX<{F`SsJZBM-g%=gqGC#vr`A7(&y#Wd_NI=p-x@1|% z27wnN-gUxvy%&mW2ed0f_=dFYwf+6|e4h~K9Slk6eG_57FmbonTu*OJmk&+D)L1NS zwewZ`S}3qD#Ibw7=8;jd_n~g@c$Mx`k^&<2i zueBu8EaO5}+8edyUeLg12*Q+Bp`deoA(-WIe^@q$qSKFq@Lj(CK+jY3)S11A zxe&SeQ=qWxB++f*KEmRr0#|2S+=9Y`_HtnFNjLNP=`|Lr&HLf9;u`U0Y7;|`N34XO zpaI#?BQeia9k0FP;vEt#r7OX(Cy2+U(#d&LabJ&j(cpVI*QMe0wVqN@4c~D04if`G z%2*y#C(HVsiPfzBboQJ|?QVbV45Z9p4SIEbegBhCM2&*%=jZ3|#j;cKGmv9bh^P+h zDqS=#xveT7<+xzen02Gvj@yt4wXV^eO3zN)Q6<67?FAT&g%BtCU9uEVJqo0G5O9<1 zGX|I3)KXHow>0H*WxeJe#ZMur=2;A6`e8t(I15Q>1U*k?c6K%!*n`W8uG_y=YHCkb z96k!_4$vasbJTy=*T5A86vkW9Oz;K}t+9)H~jK@7w$MzVCC^I%}`B_y6qu`~BfdKUG7p{nn-l(oS-mSHZsQ=9ciC zh#boVAFU(BgoH_K?lXCApEEz<^6jcL`13q1du8LzjZK@#zVk~`RrSo49;JAhqff=e zWG9I$a0afpde6p^;$SLs9-)bm`oK2+s|!{4PxYj~gTctO2TYQ-DgaD{vhu%- z%DTdQ_ed63=bKN`m-9hGc8?$?4wZCbKFrhALg*%g&FUjf3O*ltY&2+lq?D}ptt2?km0x~*yR~gxqxyln1m}%0}PILC~ zB`_C8VZVkX3IVI>lj0xdF}rK98T#g$M5=h`!er!u>$AUcOn;ENyph9f8p}EdGZWOU ze(Yl35Trj^N@%`%$2W|8dBz_6&q_<4tL-VmQ839ajc-vwYJe&Y9;|MLraPC8%_77J%)e>^6o<^{~0YH+(3S~?0TGUE6KkaT91`VaM0ao=~ zpNJ_*a>B?JG$A%h8NO{{Bu3?g7^m1~WN%m%4A_?0r6W|9ij|9O zdhxPc7-PkSM>n$@V4r{3NAgfFP|LkgXT{dYMFd`G z0w0L0#nkp^yc^dsn&ZVEmv38km?TMp6ko&Ec!C_6{VvpVh*H55g{Dj^8@}^ zde>3R;P^P0X~>-MQfF+B9XQzWX2&KQwXDSpiNh*yr_8PW(t+Yl_nuPFZnm$uv(}a8`<9; zNHcwLE-($sl}Y97{S&p?y94E!MPIr>&P9s%!Xgb`l#2L^2=n$^z#oNFL;_dJ5&aiU z8lzFGN=>v4`ptVPr_zYg)_j3_kazBQ z^C#n_b40<}=N^OzF?k;iPY=5_wDAxhK9&W3lOyx(>-#DhN8xCMJe0Kidw;ufEvZU0 z?97EhLa|4(s^qPV{ee}TO55z~UCQ-M-RgBGwluqN_MMI8f3N>?U+SjIF}E_Ol8>os zLcgs#mn~-Ok1X#*VyqSCnvvU=gI zvt|l{)6Kq3^>0s^eZX}czn+=qDvV_yi~Krlm3hv%cBa8N!;(y)RJExtC4?y)9xcbZ zIQ`H`{pccoyXx9s81egtIWyi#X7(|bU1Y@3@#;EycbmXV^HeKE8=cw>=eU)Oy2_HM z;r<~1m5*7sf(GPyZqa{Kzuf&FB_jPjwZ=vp76SZxLN%QpWf*y;>h{fRtFmKSq+a)GJiSmW!R>##h>`b`gOit z+f*QIv!8Z7CY-ya_p?$!>N{VN#YE9xeg=U+r+u8E+uz3Ywx*00mzQPbwv6fP?1Dcp%VT-&m0UemJL zCz`XKeia|^(BE6$BT7TNI771;-2=_1nLxgKmrN+kAM~*}xIaW5oH7x@rlkvCwp#xP zT(h%RzA_6(tL^U1H>0L*ZW4?g!wqn9>&UMJn6? zXyFNtQ|qfHv(95My;#<&B!AZZ2QJ&HY<0n*%^8LI0pKMR|3SUY9sr$yN6f44l_&-! z0b_wX>KRP!gY?+EmM3NRSjjO3{=y+PB@?CdNjl3@2QeIA_KCl>DF&G!flBzh}+KC=ql5FkBRnRPNo^5!<)X^nxfwd=$2R9O|z2_ z+zT&stNnN14IuE&AG}ZTk(F~tKSsGABeW2O=8ITWG>*d)yFITqNXTsu#cWAOOcQj~ zFcIi8dgaV`H1baS_g$dw=t?NR-(6+@D=-6=F5!vGZ>B{L%8h1$H2&-WFCaN0W^~$k zI~ngGzkKEw0*|^xeMxlHcf5sAKqZxt9EUW Sx&-uR9WpbvLe&_0JpLEUu=AP# diff --git a/tests/_images/QCSharpness_plot_qc_sharpness.png b/tests/_images/QCSharpness_plot_qc_sharpness.png deleted file mode 100644 index aa3edb35db560cffcf3653f9583b23beb359a999..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30929 zcmagGby!th^es#aNDD}bbR#JxNT+mncS|GEjUe4E-Q5k+NJ@j0NDD~!UHiTFyZ86s zcb*6I0nR!5tTor1V~#QA4p)?yL`Nk;g@J)VmzEM!hJk@y2mjAOMgre|-gU`=fB0O) zHC0oSdy3>^a`BvA*GEBC~XHadhTmVX^zafAPk_ z$$~|;hEf&03W}qYmNN_t5-s!vE2p|J2m|9GAT1`W>XC7j>6w8$+tQV-@`0tU)?s85 zU))RyOEsk$m2LnXCvaI@H;+xD<23DcECF{UP1*{9TqN6O0QZ1RB$2oyETS+rtS~k@ zML;e3sI5SU>MU=UpuDRb<_vrvU*O!?O}&dob}65lRM$o4j(1m;q71#r|9)W5NjNKz zQ9+O2%TktpqW}Fk&3_UmGzfZd!so(bpg<2RBoTNL9OyBP7KFs}-&67lk z5v|9>;E!eSI`bS>_YtreBlLx0?#yZ!nC(p$@ZbE7_Z$>F{r8JHBZI%-ej~OH9M?zMrE;3QJGHEqFT$J3ntNq49<|3OsQ z_4sN0{he)o!uj4z=#2ckD9rcQez~HdPFJh$9A+c9x0gpB?Ch9d@wpiO9*Ttl*Yz%v zkOOJ7*5hx>PV?R0HbPz}s=#NM=9}NcS3Gwz`EPggY!_>TTby^;jd~IO{74^UZd&Pm zJFnX}_}W)?@awD8_uhOnMG8BK8v40~g-A1;o~QjUu|+t~GEq@@d5{IbY6XUkXTGbm znxTPFuhbnzq0Q(%T&%k}a_}8ps4>}_uX^qLXKW)uMZU*X?@jA21?i{V+)smH5_#>n z`($yXW5<;&WmQrr)Swu8KJLzcorHRH$-G-epfX8`sVxe$p3VI21DYuVATF^ zgwbXHXE+|C`eF8SOq3fW9B97+!_bMjCQDRkVdVKw#bjh;nooM4d*1F95l6n2i+}HZ z%enBY&D}vLG^q%Bx4#0p9%nuVv;)INchv(&HL{7dY z0)>iGjG~%af4$WVE84>ZSN!nF7lZqNy_ur3Zg@DqVRnDe>+MeB~hVvw5C@$Q)a{q=gFMD&g)X`W>C zpN4t;tqkY!4Y1E!SzdpQ?ypblt!GjF;uYSfO1}0^NO$PuU2s_M_BUlG?7Uk&t_7@BByBzU_!hyUG5?jDnyX_&_s- za$B*Iq`6*KfB7G;w-y@Zw6q96BrzFnk0g?S{rn8pS~iUnrH^%YtxK@$;mAR&&K$ic z!wD5!2Ip$4t4)19EjVkY4p77dhKPT`H`2m)-WfML-Ru`;$LaRIS{)>IXzu}GHcT1K zAF##4fALdgES)>rAO6|D-sdbZCx>+t>^}F`hFxw)OKjj0|BPpjCNb+=gP+3?ymMm$ zQHB!bASE^AL&@1q{tW%Ny|T^ZxOUKHK2&U$U^kpGS&J`8to&&GoZz+AJwfqE&~TrSLJ7cNv-9xdIo(~uE zn7b{B%0J#HS9_cqCyl>L-|~{U-i=2)fdjiCQ#W2x0wN@$w3Gp+w5*JGGmMhRwyLBg zx4yo3wNw>Gf%}FNsH63nrDBnc3&Zn;m~xBu%iLjdss4;e}$@Y}5X{Z=PP= zT6X*E`*gPk15zvR?#&$G`YTTm-kyI_Em-B^2T8rq{64UBB&b(tpMvyx3ic2>p_T3b zWYnwl<=Whih}2-T!NGf}??L|Rom+lkVF)-e8S@6dnHmP(i6bL2kLN{#Y*y1$(|eqQ zuiQy61@Bpx8|_R$l#AslB`EuAPd)zLH9?a)BV=SmK07;`uS{<~k@dq57M^FnI8U?DmQbd|Q0IMuG6>u7Wrr?-Yhh>|ii?p8 zaKGR)Zw?AReq?=rWkh||anK5*Ip_aGdtvv^-C5T{YUpu&{TA`pU_x^8*_@8U>E*J+ z*`z4uY5LkdlAkI2u;Ak@cewv($`Mb12O+a z``!osNaudGv-h*)1&8@qj?H{!`~AOBDDJ@v?PU9ogXnr$*>R~2LgHh$HJu*y$As@{ z|NJr>3_eav)5gjdfjG@m$bN6TP)%-$C=8E2c%SsXSegANGwewR&!R%+T zlJKGeD2+p%zjYqyT0UJ^|MGo$Fw|?7j3$L4abNj}OwRK=Mih#Vw{PFxgRH-Gvy)Am zD8u$~Q_Q$V6CN?=tI$sgl>5mpeE42_S3#$r)BdMzLa8vxfh?U=+$`IhnIAx%iQ! z^YQioY2^Ld&A59rC@MEo(LT75>F>Qi1%4FxbZ!Vv<@w*$cZ#?Ht*?txIV_wu!)cw) zesDWaiNDNE*sSiufKn!j&-DhAR&7v?$>86sgK;uxdg+kBQF!~|v1?08S{IxCT)T)g zSJLGsha!`~ug}I{Eb_SSIGTiIhC!tzHmwc~B01#LLB0hUIIp}svP7kbYaSQmdU$BV zK-vP;v3x3RqHf}SIymr10$PIiCw=W(KDRU3|MFfxggH+LB+pgom@K<>96!S#8CrY1 z(2e_cR}8K*5&ENfH@x6JT^C78r zV{fIB6$S(s=C7xJ$z2F8A|q|bonLl56A}}rI=#JUGISpR2QpRq9SeIY(!|6Yi8 zU!QK>bUod5b?`|@`N(|uaO3-Q{B#QHEK~&lOk^v73`EDmvI8v?ba~t>j-srOwWDuL z`a;m5q(GbfIqu?MekM;6^ZmoQ3Mk2u32^}d0qx6jGx?2;Ng#(pa0Y-sd5Nzi!yBQm zNi(<;Wt3D@UQnHz(Sw%;Z)Og15_H-??XPy(Q)W+{r|Hk4MfQKI{BZ1B z)ZU(fO({_gYFoQW(i^=I#;SIar^km9kdOvmJCCugd>niwo62t1^`yr+n&)H^fcoqm zqef*u2pgzEzJ5A=%{=#Fm@Y7ZeVD{lYmzy&*iJG1)J>oo@f5s%w&48+CfJ$S;O8&@ z)nz|KhQFY#HSR~%o!v$Tu&5o;IdEj%M zO$cUZKi%&@AmN9TLMOVay1LVkxAT(~I^1!?RG_kzYu3OmH{R?i__oeGtGW8+75(+q zJ7q1caIAn)madF{|NcodRT*?;*;I9K4dKx-GH!(m-WzqC+5c)!J*evH6vo*Hsnmqe zb^kj&CVzLaQXbDn(2HGg?mE1ko7|6oWdclrGx@5Ofss+VKsoAb>RTIp?q431c@j~j z+@w5Y125Bt;q7G=H`2+R9071ZI)2HdIT^`eB}EaN)7XgrEFO&p%M>oubz!eFjdbuGuOy7;!NCtEB^Mk;; zhJcNp?(+48N~n-mH%t2ooKBPd_AjsF?Sf=|V*vQ?2Yv4)JLo3NlJy42LrHpDcCtpn z>Q8tbcMNVt3ud`LsMGhObFXwml?5cnT~Ky3^jz_v76+UrKE`?ukMNtKD(;N@Sc z#kZ8Aeyrrgb`8uyc7e)%Ff%Z?9zG&*R(`6x2u8t90w+{<7*nM(%bhEQgN#oNTQ{}K z7>3bb=0X$TsvizUGQTcoEsbD4-q{+5qLP@czr_i74-%~>2={O?3aCZN67X^Y{mI8s z<|YIaQqSMm&>l!c5%q&3{9|`24=l9T_3E*YWbzxmX3*AnLhaVW<+3M0Pm9$?@X&(# zUiZDeTn%m_+r*BKN-X)4CDFZF^;v|1mP((X=Gxcdyqwz{1l9fZpx~cj+H6y>pSB>r z`@g(A0}vMuog=-?^kncS%_;hcmwg}2L3gw1;9(W!*AE#t_aa9}uCuNac0K?--xFL6kXfL-oIAFBtbzbW9* zc3t$L*l0Cat4+eZzoSf-qJk=Z0zt<$y)m)+9$i+O{I1bP)ml4k&POGIrh*;H+>Jw0+mEeq6y(q=fqfne8l zomCU=xF}UC(`<2~23Y>$tM`&s(Z^NS`5L=rE}w@31Nf7*r-wBtOET*;{?42Ldo60; zvW*X*lJ^`)Tvy!W`HP@uR5(e;nvOAN>!!_g!2zcmD*97-lE+;LOyu2I$fIzhVgw$zVKLtg)lG& zG2_Kt;JVd!iTGLVZ(S=qUhHIe6D)i=_!a)_F9W<}YV(Mph0g%Up99#EBt7drfj8iA zmQ+?kod(+L_Za~8uS;^bMaJmu__y9peqQbj|85)jTeu#H-(`1FFT~SbmVfLMmsWrC z3kCjIP!>5%hp?6!?M6Ws38V6Pw9a485Ue`NW~j23Aj&}gcrRbY5X z3I~T+9;+=ha@mIX$0-xbPl$duAw#pn^4<#l8S;Ys+$R;Sm$Rq#bKhR*PYUd#2MIen zTRjb8-2yV4se#eP?vG?ge&=L`a*0Z+DVeW&v9M|ys_atf6v$;?RMiuRe!Kb+(p0x| z$^W!;9~x+>n*HH=xfT;?F|u1XZ#(oh;Y8prGBB(kt3v01x0Vz;dhZ$9glGW=U6GNM zti)ozYEYyTPm4xXyY{4k_XQs4?YLPwuZ<^}i{;X}NL+s^K{N@-QlM+1fAa>TKFL`$ zY4Pf&(fb7(x<74~o~s@J5Ph&J4$o0;Xr=>#c@i89(H}ys_6_Z60{=y&u>RXhpu`O+9Du9wzHer$GYGk`cDUs9h5oWn^c^+%&OQY;b zepW4c_XQczRF;Pwjx0QOIK{Wl&?=!8dw7QhH6+3{_;E?TOo#=#2hCo7aWNfheX4AT zTABJ3KvPI)1Q3No_|_U1bWXUqxQ^ekB_VpF)nyNdY6=e*7c`~gA?U;bhkXtdAy^){ z%%~_Ri|E?buoA@BdsfjHidmsfdO>zJqR$P}mgs2X`F=7{#7K#-7ewWd*UzV-9}z7T zqmb7djepAQyeh91Fv2U=L?A~y(vz?aN+2MN77}707483qJcu|inxh!9vExN=$663% z`JMY4s?mZ^nC5E{`hqTJ-J{q$BYO&;Zz)D(`9Gb15Sh%wEBH!&!&f7gRGTL|MDG2E z-e$GO>P|IHHg;sZN%y=dmT9*r+b0bWNRG2=8nB-Pj{9F4Ah=vla>`XUZ=%x3rgT^N zTrm2aeldV?gaH3Ab@Dooe2nc|K(CGS?vU6nKT2>naU%>lq&@>gVy%0fh*OAlO|O%en);g zS-hB!0jxC9*HTXWUrm-K>Amr7hRZCiXj_x|b zU8ubOQD!d`e)(UA-cyQlc3lqAn@^yuOjj@ff6gEH*NPQa;#_aG8Pz zjfjAKBGrK@lUnCt8b!0ayjwGZ&M*0+X}JE9x*DAb3qDdETv|JO-_Aehqs~RuFm?PX z$ECm#_Q0V=^EQ|+A@huUH6mW?G4frBCX4Un!KAAsC4|S4f8EyYAH+&Rz@3rtouS!F zK#{sa%TLV^PfkZ0&>CqxXjR`ViQ-R=rb^*2Lvzp=sL%3-UZRmjypdVrlNH)pUDE6< z-zmcyTbb*0GSixJGHc>RhH$9Y0~KZPN#+{Kv6`oA!$6y{h1S?(YZEJ4!T~u2*M3a% zkj{tHOpi~x2{*!L`i5UVB$4M$9fs<}{sFXm6 zJp-^QpU{cFgy&C+$>r_sB{lOh%v}MWe-C^n$!wSriXy6( zj?S9RxR06EP#+&T1@>6}myQym(ie*kDE0I~2A#h>>F66M%T0foz?)i@ zkxf5XAM@Ctn&P--QwFo{f0YQW(rNlCpUL+T9P-)2bGmtPQ3+0GAZ}%CYA`dly`3e5|=SHEg@Gaj0%7VKJ;|w4; z+2dawuobKxhc$zs)r$CpbPbW{fb^t_AO2*r)a0Px>3QSd2HPe)>gHcNAdjqf8Ccai zli-A3aK2P-MjjEt`p&J}avAA3rLwpVm zeWQdbvcl`p`RwqODXF6g(Pp?<0Dw}Pr#zq=u-NDg{(QM;0c3_}&Pn(uHk{tOHC!jRVS2qN-q^9##Az*0`NI9vFf1ioHx zwO15L!qbdZcG?Jj`ES(Vk=-GBI|j>7;r&ezpnV}g@hrkhdF0clsTS=Ylbo;qIFIud z^)#lX5kW{>-|ML1lL?fcA#yzH^S@zlC%L=0q$9YaVy?uu?OF59w5UtYe^*EsTM6*k z*j`|;8|O8u89G$LSrAjjwbqwZ!z^|ts9q^6+u60JWW(C|=`qF@1c=cJi_cU1LOrgAGT(JzuQDGj#*;axE`Aol%grWU3{$Gdk2{mryIa72ZFd_2q>ueK@u5Bi7 zUgPLmZAv}YAl0?$jyI)qTzE?()b5w5-GNw3G$;07<13 zN`toI8Z_8HdV`<0{EqtoF|=d9hvWBFS~wc*j;|WODbKCy%)8c$y+W6{&l90Wy~dh%dZbksK*}t_*r@Zrk7xq7Cqaj^;C9dow=mmpnUv&l#Nb;9Fyb zB%xvE6(FNUt2{-J^=z4eE#mnps!4H5H)A-tF^vQT`W|Nj{w|iC6?y77H4inuzATxY z9ce+-rCXiy6gIqYg6cd^Lug>6jAAe{CIuc+zm1C6QwJf}JXVP#YpHTnvDS^?*Op%5 z_Y^o&COGzrb(lJHXlnNZ5u&`(#604tdQ8YO~d z0N^E}0fD*yX)8fF$~uud#0vShZ~|gx+51Gb<`FmgfLl3n7@xc4H7=e36$Jr2)Q>@& zIe_Q~%UC6CQg_hVQfhO90~h`7Z}TVb&ytY@*zHfh;}u|_euPr;>)1P}eUr<0@GmeU zHl#nWK(&ZcYs|2ur5i+C7F)!(ACp*u(^B<2)Rk(yl^}#O3m|^k%;wMD?I@hqnl{8N z6s=}R-Q!qaULQ$!f*^u&NaP>c-3ZH7TF&7XmGip~;m^nqF)lALE*emE0#W`JQ_KB| z%ca?1v?z&G(Ku$BtWEL!x_7HJ`B&K7k9Y=)g^4M@wKYwZp&!C>tcEa<$^aA-QF=Vv zQAwbBrEu91fsKAuXEE_7;Q1RNC{7g(INr@auVemTQ^1*LVXmcK_qJ+nZHE($vbibu zia72=V=pRWEw!WAgTg+Nt=X{%6sgOH1(E`{tk zr)6;psPF$Hf;MRkwgM`QP|$}Tq-%8cRM0m&tCwpEySlyw>d(f*)!I(|jC|S7X(uSm zW7&e)K*OD?)N3Pq*V^9xIr)xMO1lwud_?xvci+@_HeUj9#MQ;ycCLA8>ALlih$WyK z>w^xlj#)(amRgd??HVI|2HZKj&9uR5d7%xBI2U4gGk;D529ubQi2XiEYP?XbpDwnN z<-R(H==3sz(%3qUq={1Gm$T6g%6wETLSo;@f+}Mh%OylFzP=7{!(hiGAhHg#c)>jo zNjO7h;y#@(4G7JWUyYwpL{$o9tzxcgY%2cU)+b=qE2GkHbIZ85l^iUQGY3Mw9U!6E z0htAotTbp{6{1N6AR!#m1df&(OfL3kh4IhTH1ra=&v-r}5eM2oq<3 zt?=p5b6@QTc;x2SRcGiH4}ftM{>lcNMB9&d$2$|gkIpf#ybmgx*iDCG^!|F0Et|!OT)mV!ZmMEg;F$70Qo_s?xm#8{LC0=Q+BG?N}4bF>VKuV#1$Sg&) zy7}d-%BYkkU0v40jKByhA>ogZiy$A%HPZWnfRGHk=Zl{bp13wDC38$+&WLsB$GGl$ zd6eL?7q~vPc%|04T>YepjP$`HTpo(rkJEH?#Ww3Pp@EC=_9dKytv2t!P0?k(bANBR zGipu~dQeN=0%=LW?GRa8%MQY10#f}!f~BLQBg>RmQB+hsUX$${+ex=?4d-?KW2mLx zA^Qgub5v9p%yWoU)?^w0wTzI{QqeI3>c@b9KGWuY{AqFO-EtEzmi6oJZV;&sajic8 z;(W;IiMi#-difE!p#)14`@##hRgn&f^2vjtY1NcUAs$2XdRX@@95auE@kJq1 z(QVSTr>~Or@O`Fd^RumG@v6w8yRPe|>{6FY84KN4k6u(y7WZE+JNT44Kfh>tb@fD4 zPr1U@3PniJhc_)j4L0*m{dgLzA2+|#NOA+>QM9t<4-o+03jC)CAeXIf9oW)?;tW%q z9n(<3H*EKG8}yXwxCxuB;7ru>hsVA1?lq1RYwi4#^`-dCrNl6Uf>}C-P)>0B2Nq3a zRn7n$xQ#1SR(8}XAp~+WGaS?RdG`}O(+%wB&s`RH+w>1_A8=|pk~?!RjZw*kTi`bQ zQ}K~sqHTN4gM>~O=BJbYJPLhKXbFkI`p^^IvBRt2T&2JaK{>UzUeW%+v4a*}5Fu00 zw%Nob&-?B&1AsIBhx3_8qPG!19;hCkiUR6tCLo-%fKuQHsBHwjxXy4+Ul=~B{2NFt zhNMHlEgGM!_n@27_+FG5%&O##QftUN0p?sd?k9+oK&o=U!XTok0~Xx8+M!a+j0y)O zt^%;dqX4m>?~09==bL>ntU^2Kv|h@hz7xBfp0A46*2_&N70c5$;Y~+jre;W7YN)A| zO`!W;Hj;zpxJ8qS^P-4V=TngU_lR2~OO51LD9DjD_2;~%vKvytjvH%)dateX5zV!U z-K+v-8QLij@fT*{SStG#r*YQUPLgb*fes|x0EZ%##eO~ zi!9t$snR13CnWtoFHzKi)eA|8jY=kW-J7P~siqlCX4#4R@J1L}7_buFwf%xnU8be5T0JRWr zNcN3(eOOvr8kuz*%P9l55ktDpIdBcledryLs~(}&@L^}9ngN2#T&3P9(ANMS5E76c zP3k7KkpFG7gz?*MDNCk+h|_MZ*6hP%y?2o*2U(rBlXG0H+gBkEoDKS*qP|{zgrK5o z8{Ali;6#`*^<+F0dKWDviLOJUFjbVX`fkU1|0K3xjl+$8D*N2;^hzFVg2Ie>2Z3fa zU*?$ucT;FqB`EBIYjH=WQ)gxTVgg)QEp3{ep9C{G1FoF z>qm=N$$64*c=K;tSjs`Nb}H(#wUS(v{h|7IO5pO!*#QbYTPC0DCQvzxX*pUv&&&b) zcuSTiV6drHnPJK$y$`yON?O`fxoYh?bGg6=XKIF*Ec&w9@4XiQB@Y_oDy24T)+b-XtlG%i0Xs4+Ep3j3rm-hR4+C46$I7w9s)O>Nf9)wJ5-_w&^@fre zix3tm3Ug3;xQ#K$fN$aoLS zTS*c``l2dP)f?R9)v`yh76o*avF*dcmFxi;CZ>7BKx{M6S1Yk~p>)^j+#MLFAYJ2B z-GvJcIswWiFy7W?5fi!GhKmZvm-gAHvwD#d9*eF zbMa?K@QD|23m0GG6N~vl+rLk(qm|Te*R(1DT9X55?M8%49)ynnij217*-D+1CM3Nzb@}NDm3JJv-%Nzr$(g6cop3 zd0X-_Oy5KeOPfb?7=I7@6hOqi>heMep4m@Vdh}1B**t~^DS<>L%19-mMfxw$Q{*l+ zh@>t~R(8vNXb+oYk7s9mSJI(ZB(#oebyId&Tb!x*eH2fGp1mJQy{0={N>w(1y5}uc zA{RSN4aJ+9W>G#f${-5$gl|+y6kFP=)W$AWdeZ6g4GBf4H$dh{fGJz*{=z_!sO{TY z=<6lZ^(&xe_1GYTxn&7Y#)^_L2>&&9BZed~U!X3b1pEc70-?vWWj(R7y$k&M>EP=f zkP<-3&DsDuHziKEBUE>Q@6QJ$T|ju=!%a>_fs%wU_%Xn=j9#VC5fGk+%WgRmc>ftD zr_ovcEfZe~gYAsok?Toem;(=2Do0!|)6qKBc z_RWFXOy>e>^giT)-jcGs8fnGdhhbN^%#@xl`57(wnt{c3; z@GUV*|KMUjjR{l%!Izdb=tY_v1GwS#ipyHhoxXFUZW?H(;L%8oi4&0Jq0(BZSn|}f z6z7tqk*9{Qe?lrGm-eFBek<;QY>Y^Vx-@4E|94b>Q`qx5t(*h znW~m_t-Lq&^yH1FRrmw6;Zkmz^rL$~NwK;eYJTPYLog6UJO}_3Bvsyk*Eoo>8vc(* z9XY!<;CeWqk$o{%$on^hvIYl3LV`j!nA3HESNX_@S|FdARhOB~su0&)8@mLlO`=R6 zMPnX|QM3xSXh}LQvw`0{f2F&eE0ztVkxGx)=tsz^E;$QlHr8V6SNaU2Q<@7THRI_C-5^#?kso$~nCY21{VbY9zjA@Y0Y&TEwz@Gu*K9M#l2{3yoD0E5*YW zeBNpp-&+~5fFGucTu$V_L2S~LnBUm;omqY1fVmv?g)&WV91s1Mh$C@2`d#(*%&#@p zLPn@1@s((HrwJBhR0M{UbXF`8home!HO}CpDyHk%`zHNBu#w))b|x^Ok{PS#yxq0RLQ$ zd-Xf`T|eY91Zd&gw*ZcDp1fbzP!!h{IuF*HUWVgtcbfJDAj)=JH|49(Q{$lY6@M6T zS}nZz5haEt>DFhb5yXz3Z$_3cUmDj)reYdL&tA=LSlAGHB#tnm(LRQI&=@>2d4z;{ zpbL;rIqtGB9h0cBI*}%^Z3d3xl7~-tEyXVe@f|KaTum!}*+!b@VLBLvx!^GCG$yUv zN1CV!yR@l~J4L|2M`%!6U5q2C%868MOHj~F%yjsy-lNmCUhKma4$m9RTF=x9mP~OUOq3J4 z?&neeKJ}nz{_*#&Mk$~Z6~U0#ZhW3_6!Kf}tyQY{a|%yubNBC8k7$y{JUAR%Mm#wD z!CLV>U-pBT%G8aisnsi}l$pa-J;fLWjPY>Z?Ikl5JV>CuMKVt;sVlY%Rw$% z-YcYunm6$MrqnNOiQs4pC8C^@ez0&Uyr!bj@1I>i53=dcJ{isJfopiTwYDD0#m20XnN6*tQ6G9LXTtNII9DF)-eaWxjhkTB})a ziET0vX*8a}JC-Go4j4jy#ec1bwWBE<7V_|0Z5#+jnu1ios$74_WI0n_70VRF#pRqGwMg*_bsU%2&@vZ;qoUdx?<*tgQUEb5 zCB33%aei(vFTY-^si|La=+v)%IW5~Y-QIz32&*3&r#noE53IFSus%vHa_yBO7t&(+ z%sivq`e2ND!lXzj`N0wYyQ{KVCD|}Wv8)8GZYqstBUh)7uVJFhP6fQW3|WED+c?Bk zWlvxaAb9_o2SzjmZ6gr#{dy$FINAR+00R)1`F{<-hQJXFIs3nFeS`?^?vsr^Er5!E zOYPY@V0wxGRU{mC2+o_~T)CF8Ni5T12)TgQC0e^6%)}Y1qAAIspUps@URK8 zdMgowzB~5Nb4n%est20Of7j90Zk?93mS)`WS3y=!6z0RBUTQ(A``M=wFfqCu+e2pE z+MFfa5V=W3Nq`bRqz=!Bl7odx@s_Xmu-`SaE<%$o_nxYrMz>D2RVjExu6w7PSFecHKbzKN2iuE=86ntw zliNcnH^y#nj*3C0%wwg1V?>l__%~Y-q<{4mG3$uiD$J~J{8Pn3lvQ4ttetH|eo{Sd zmU?Z3I$Z`1^NG={kexV*(jnFt9xBGIguw_pdMWZmvdl;_M%yv%ReF@Y>`|rG#9XYD z>5#ep^=4OWn7y`w++*DuIRIz$5xZdm*#0Sg+_)c=5?MH zKlboQhnz!yIgHYPMFe8}|FzBq075zt@G!s-|NQqvz_>ZPg&H2{bjGeauLK(HSBXr; zcFZNYljt*NeaDmd%%bD(9)WZ7511S<>W{#?J99fsNa{e!_yKnrU(Jf1u*Eri4M#^E zs|3Hzw{Xyi_2BZNrm9q!zCG+REK@|dRV>DuviOKhstG6m!-ZQ@H}~|%Za>RJibgpj z>K>WfgGTx;mTLGKW;o}v;3XpRniAwxE@}1t;*#)K>1B@we2Ly}nQNORnb5A_vKSJOXkIg)9$Cz!U>g`b4Rn;`zb78u$T97^x9sPQG~Ck=PtAS{hi#Qb6n|Xy1IiGrWx6-<0eq? z?8qX@qAtg`#fYsYuQm4eN;`}A;`NU#xmzIModR28>*emJ(^RJOThi|H^%(2WZ>7 zt8Lt1a)}HEniSasZY^jA0LB+`Qq}kYLuVE+r~gbBHPM>v`gEb<<2F!JPj4#{xBi}L zEC2(H9W9pmz6HJ7Hssrdc>;#jCa}=N)XSe?0~E`#zJ*7Kb|&vhw|^thPlrpOc8rj% zK(X6fS|{t%-oMbN%v*lW(8X5G-r&wi;`jyOtzJ*1s%8TmH^#{1UAmQcX(kK`ZtGAO z#+k8@6_vP=Rh%47oIc53ss~$FB~N8klZAf-N>G_p6FWI}4^x%>CQ-FR20HvkvJLR} z5QSg9Zz>f*F;B1tUC(D^qo{wQn5(5UP4dFS2A1acGjqKzRCB>;>nHJjgp%l($D#G z<7*fYVi^k1xe!*9%jLztw>I?M+6qu?8#quFNWH>zc-||9i%-Gye7kiCtL+Vg7QsrH zCIzE4h2r$kdo#{=(C`Kz0ewzx04oEvpt)cJfAEy84r0L((ijDU8NFBmu5Lgw{{e)% zIC2kgRq}%1xoWoZ%9Fy}_6u9sy^w=fTxvf2Mx7hBfE7iS(A9{VLRj*mm{k#93Ss)? zGAn-N_0vZf zTUm1`MU*Ycs6%!I~X8iBFx@xa~H`n*TflkaAP zlPps>6;76TBlPHCZ9YST6pSGZK$ca&W6Q|N$pvnQfM|ys{CN3kcmp(Fn?R>B1#?mm z9P`7lQr`{>C6Pr&K7fW4)5d@vV#!I=&H|>5z=TE=mmSNSH@~cAE}0hJW+r#~Wd7Vo z?Le~CGSm{5q@oVepIHfp<$6~yNo}_hM=-(_gDuK!5k_Ddfws1bl7S;+1z(KT;lpUB zC%@UU>uUJn8&h~pKw>Q%IkowSP>F33b+{D|u9$L)6i)h9^K`-Yk^-9zw=p>Bazn~+ zS@Pv_H(Qy680keGd4w179*OqSy93@+(YW771&q_D#WZY%e&*kQaaegSLGby)6~{fxz$%IuDE4wLl%Dd^5q$DQl19Bz9(BqYFmM6wY6|aCV|G!rojvzu+fi! z;`3D*b-D4(T?q&B7#)fC`4lwf zR-90}%}}q8%y$e*cs|Vat+* zCq2j%SnIfRaiu-9e~T=AT74;c{ovD3^T-J&=%015OJFJOV%{_?>?Rl^hr?-$$0bO_*N4y6DkUiDkn~6o ze0+{Vij)BtoQT^TOr!t38|zw*J^}xBVh;^Zx9D|Ta=3P+rtB4h+k<1h6<50kYK(Dm zLBE!pt~~*Vxvaj@msLHdA@fPDqPUq(D%oDhC-~}e0VW#A14?-kv&)S||7C7Wo;HlM z2iUd5XNOlqiAvs{Bh$vjy-#-M6n@1o7KE;c6_CsG?FDxG@p@m7Ax>X@_`<*>rfNf9 z#IT8#=)qRdVMZTQ$dwgrAX*yut(r<)m`PNeVSH(SuHGs=$GJTsp}xe)`1?1EJxg3O zLb`iXKr0oj{}LqsV?n*!G=Lw+o3 z42jr4B@=Q+%6#iQ59Idx@JRYq87v`@o=@VoUnl|wm5#WbEz^(c>B|eernvaTN_iU% z1^U$^nm1y`HGSd;d}b4-JHloR9pqxec|4SYoibWKjMI+fe%@vj)X}A}R?2#_IO0`x zkHsm+B)asmB^Qhz^!)4X)O2bFk~gd##m^cO$v41dLi3}H8Yd)2wDSm>Jy`*>M*QcK zqF&zR?~dC!Je~k;1b;IA?)U^ulh{^8ui{uPrr9sPm8ISnnOt<;&^jpZ~3S{bIl>uSp$Y${&)BJVGLp z**kZR4SY>fx0Ju>4l1mT$L@2Ek)f3R{qwt>$OyRUp-HV+N=YY3>;L{j0bvv!sy4t7 z{>$P61~B?7*7AY zV;ejH2x$_?!oX0D8+ilb0P^H0%V4}aWQFW^(*+-_R$JeKUXuwTmnUmB>l zB{L5{nD9m^;&!JF*M7Q}O-=y)19{LlJ1g|WSwvHII4dj6cb#h1cDwQsd6=u+_>}Uo ziG|gyqB7bUy7#=!O|Qk5>WXFil?dTe>sk5zU(VJhuv1_qqLwiPT6m-s1cz0q`K0$q z_^bpArQrA0pSL>jSE}_*?}ZG!6hs;IeLDcqnf~#a0uW_CWmCouz1MMqNW}k4NkHos zW=Q-72CLt_T603F%MlexP=;a#GMIw_iLZ|jcPd(1D7)0<08@vq0L^Q~V=D@bxcJ;} zy?$mAgr)0qv%TUvuLoJ~!Hg~|)-o^PFNiY^z%>X9Ufy0W3%NJVtj3L0bh!@Fc zg26OmK)~Anr9Z-^I1ItX3BU?~rlySgLX_0i2QH76!SG=x{B9vNOi`vA5g2=NP}GZk zMJYBOM_FyZ%7+hHxc@-_=FI*BV`~NuYe&mXD$2^eFAYA!{+>?*gtaXu!M>MwNNxdL zFw0~+N4|(u!RS{sB@0_zVVmCap7=%#frHBcIKpIP9ix{#U?~pLmdGUe9r0*TMWsv! zzB4DJ^kbZ+1lB)SLXLR%GY%I&SUoLeuU0@$$c$#(crkHWDk5xyBB=KSS3OFI2X4da zcxh<&!m4P2BI&emqD}iMDa+nyx7p`=ch}?q$%-EGM-od&#D)faf!?AhLmsqV<@;dc ztYFi`Cbq?h02(cP;GIlb_pa=HB?LSsFwnpj^hI>|4h$T88XU5&g}>lLu%d6$oGqvc|Mn4;VpJhiR?BwFHyZOiQO^r+vPHO!yH@nrID7@rM#GeNV##~~4 zI-)pqgZP)_PjY;BVSha5aE4H+p9Nemdfo~zqAJrgu!T=)Vb^gK&%H5FZ=i{9q-T(# z#6!lNWtWgpt!XTTPE-sVYZ-b6%@J&+{C({URE~|DIC?ve^yw!a;?iegOr;&0vV6yi zx%XHu&tt#cgH%Ek)Q%|;^k4RXMhL)o3IO}npfg14vs7e@9|hBnqrmhF&8rWa#@(Fl z%#>-cK$g??K}czXQZVE?N-{&dqrpMJXVMe`mcjIjuH(PcwW5<*8av9@8>&XBQCw+N zl2kZBrVVgOYj^WPqnxO47xZQUsdHvx`l|klDN2||GH9Z?FcWOC%7&?=h*N0#OCoAZ z$ST+liB$L>PnrCeWoq9N7O~?ER^uu4ZNKZ^P6;vg{+6K49e{&Xqbnxi^~YSoLPW>s zAD$mWWbUTY(JU8r`JN}4fSA~*Lcp|B#?9Zv*8%qQq>jK=?g4IT037A+kWJ`pXTkvr zdF=>xFo(Zp57cH!yNXBhc;#B)hZZ`dUC!oC*3kD12FBZ1SsGrzyaD%(O5k7ZmO-#u z64h@q+KvMdpYwnoAN)??a%=WT2eMOra@8Wo=)zF{&DcNOU#plDUMt%U_k!?(nDyGo zBG*N)>Lo1UREXx%I^GSnxUykRnB=R3AX7fL!d{_R8L`FMlAzxMA_+KQ+VQGtXe?&r zlE7BWRLxW@Br)W$jMckaHJgwaSewL_!Iqog+1b7#nAK-Qx9N-Y-+z)>wfd&$C zgad(BPdmPf@*Fe`>k*8V>yVBI^qhQP3jp~O+;;=zw?brTNy!E@%?WM`K}i$^**@!l zH$Wo#28{DU6bMKtx()_@z*=kJYw+uRK|isf(G98YTs2+8s07+IinNv)<2JWxwK6J4 zMg1=)oI|4OvW9&;)W~|F{`$dmc`+$l`W$Ofl#(=L4>($Ee`f6OO9XQ?FWRQNi}5^W z@yEG{%Oqmtmv|Df^KHly{Z#{ggj3Af+m0qx>C{*|dyQ}>=fu&ND_fyu3u3yZo%!&tMa7bQKZW-Mi zAtY{xR>KbB@|7@bHBoVP(<4|h3=t4i=FW!+Wady?Gx6Jw<@>9d8S(%+Y`p=_)?h|U zN=(dpQEHxSE5){)P8d1(AXSQ*6}_NiuldXJl8DEx$Ml?ns8yF>^$CLbvPu}U+C|?< z)EunWI!~A-LaGe8EWFXO_Bcm;3Abvo?f>fRETF1tyLOGDG!hEZ(vnJ}AWAABozfr; zn+65xRyrjF0cq(}QUOWnZloIo0R@FK*Z2DW=R5xyCmqA_4&_Dm+WT40n$JD&`X_!DHOIU;6-rrOO-IW^lZ# zNDIhWb;G(w-YzmfnB2p1c`3^&Y_(T>Hwc?Z$Djm-X>?jY;76Kp zxdFl*2H3@(2yik-eY@{e+;y4Wy+7$chh88$7FfV547N)%1j<@Ln^q+p~i#{o6;* z^0#pU-V-vXS1N1dlmx`b3u(AyJdwaQQ4>_A-7(6-4|u;!*${FTP4eoKe}b&fg|ZMi zO9xKnsV{s?GGlF`bVSZ<1v#TtGNv{>*p%xy0ZiPw3@njdz1?hz#5$_L8>p|qB*<3E z=j-BfKEN45Q(VC}z+E2`t06?6F4hy)cVB1XNy3A69I_L+wQuJ=@Pw=w-?+Z2s)VJb zC0r>OnaU8;HFCkJOqzmVFXolMDnW+TVk59yc(Bgnwa-E5-EXb2?6aI!6}$9D+aWSeYV`~ z1h$g(FlW_CD+y`CXmPclyWr!+(RoB)dGM=CvAK(a!E*hvg(-Vn2`(oUI&Uifq&BPE z7p8(L+#o;gtIO*VQbC;G<9v;z@iMsZWu{$OUgoldGh33#8&kwsOlM>{@ka}+r{4{@ z?(d9=T|`Z9pl;;dKpDOsKisAHXeaW3=1jLkgi58S=rerC7-6mw9Z~^p(GBNosw|Hs z25zOLYknW2QrA`3RWKO(h`JmoW%*V7I=N~sV}Vizq2vPvrQfqBGpa6J@?i_cl480y zOC|)my5EM3sEi`gDfzXH8z8|>yKfdDonR1LZtwm?o+!>jh!Gst1r+1>Nb?U3If@`r z?+1U@6_Im@Bs~%G?1iXo5)?J&sV<~oHp>K-0H7)WyRr_PBdBzc<{*-R0J|ew|Hm}= zKM`~XIMD&RQGYN&Bt`~;wF6Z!R6Y*S)Y^4@$oB~zUa)Txfah@mIuKUg1Y-ZI%&ucgX`diG$s^}LEl<}cU`^5ZT#dmC1lHRanT4}vw9av z=+}s*Eu7r@i+)Ykh*#edf8tIz7MeUcRdoqu8&1Lx4oyrMx6fI?f>(8&L)Uj-qQBW+ z!uYbGOxC9F;`?WbI1J1zY(-yG{nU6eFALM(%E@2}XHpg&wr8#6aV8}fdYCR20Q9gY%QI`X7UG zpWWI2)q?=f3I)FEKWrHDw~okd4$-s?PeIGUEDZ7yFtOzy6Fe}`>G6PdPsbsFgFV2e zQKl6J)QfV!%>X( zjky$2t5Qok&i4s5jfUIpmnT2!lHf%tZi$t(UPJdIJnGxC<=6?YfV_0om~v;vltf7> zM*61~i`Hy<<&2`OB9A3vddCwJ9&s!Xhu*o9<|}E4BlS=_u{cv>l1#izL|wE_EBw3x zZ`6yS3P&@mbTn=ebLtsE-%z#22|oL8#%k(@dA)c#lK(EoqU9N!@p}D!jwIiq7(&W! zgph`We4+bdRJz1vHOzv*<)EQM&2iUxeJ~InNwsNj-VkP{eZNfMaX*nO;1fRJyX@}5 zR;}33R~JhJ542V=q+D9_2%UZty7iO#MuAiNl9|}5nQV>PQ5PDR@scG7MB=0wiOD7( zGq-?6+yW3zr3nI7!+98gjOyUWq(87Zdf52ma1#&!mon6%IUm5v6iyh~3JH;-Gn%iw zJ=(M5dB{%Ay+?ns4@2-)M;w-DI!B-mn(fzJS4NlW3Q4hHe{Dh;^lxOpg`b4K`ia89 zzfSBY&Krnwlcr-KBwx6rm5{}DmBIEd8H=_Kw!lly=UCIv@l)cBiz@T>UrC!AlSPP1 zGNM*c%-_F_)m`^Gs-yF)s;02fd2NC1E1MnNh>5F|QN1D9Mesn-nB8Wo`1(<&=2>is z+A0(@iv{nCyXuzE__Q36bRZ>`6$=E*^8LfPE>S}~Sa>V`LT}L93u&H56QXxcPc}>1 z`Fccl2q>da;%S2#H21Z@b{j=XHEQ(|_P=>0ehZ)biQPHGQI@MsHV z%PK00i@K~Z~Qs{ZL7!T^Hc1*~!Q3_3PTg}s?7_AJ6_%K!am>;a; z58DE&l8hhIvGNUO@scVu52A?TGY{+Hi^l!?VkILj ze94#_52SXa8~5LOWD9}Lxd*k{vWS6Pul6+gB)aW|BorN8k94u^0h0dyy7c^BV>icI z8_yffBJFrV1|h*kp?KNLe-%D%(@`!=rqgtjiO^lyGl(ty2t2%a5LuaM<9VqEi zc5>Ou>r5)-UrqQdWLPSA5>$S6Y6-o6GP#CTb4kL_{8xr~`)fyMys%2!XeRu?FgXAM z+{+|-ZNlbv6QB4c3r{kg@kcUoB0=gC(&DETI*&D9P&OFrxYey-5$gwh_*QyXS^V(m zBB9~O7>!UXmZVeuOrei2j+sNboIlLEe0DkXmt_zMc&3TXZtzR7Q6&|Tsb3j;sB(oV zVY4%4f$ts^I`m6Pod9A<2QVZ2slxE{MWn&I^IKf^#0RfUBa*&VuAMB4;H^?09HXaO%9`$;DMMl)h&-CQg+mIh?~~&8wwu zsfWw)N850@X8nL5g`}~=ReI2yEo5!4BHD?qL`j@ISlW`J`^aAR>P-|KH9G6%F9Uo< z?_|Jym5V#RLAtKOnDMij)wr%Q-+949c_!g@b#?ohkhpTuqbYvuSF3iSYFqT|9=XRj z0qdc5cOs5n=?U&OboKb{jJzLHXfWt7K-TlkI?i}mel17X#pL{%g6$|S`mP3&-dUQG zw_V{KHf0uM!mTaCTW#aXbmA?}xaBYO?22%2F4F*rGI1{7#tUhKEmulVJz}kygk>($ z8m!w1CYwH&SuzNVaTuA3;;wWGF%g%vj%>b5P^eYGG06|r*%FgHz9b+oW5f}jGJ4^X zkqn;dH)AEMn|B!-Q$H7J4h$4Ca-#0bxU9@1GmI}>V^FFrvzkzP@@gxwZF+?$rRu^{ zCn>$u1DaX(&TN!YlTl zi#N_Yz~=K2Tv8VrmiCY+20*sz^xyUa&j9Tvo>;I)cjpMIcf{QG)h%a%mXDBZ5DPkh z5Vi8I=T2kIv?H!8E9os=t=q_Z@Nrj%LOee~U9b#RNRIw*9hLX-w~Ag$Zcy-~w{%1B zU|lGGAcw!)`qA{L1D@sr=_HFp4U_XC#vti)qCI2rvAMQStQT{ug4z1DdtD!9R7=^* zF+G$CVvy3on_Apldze{`i$$D|mA}nhNFSWpd=2EB)J1hF6(yO-aZ@e~N)6_(}dSjFT7jc1xba}KsX!;^k-9~2z}w6P?g2(9DLMFknug;u-itKHPi zx1rTXi%5v$i@j(n&2=cS;FE1Hf-WI_3Fo$@FEPv5r;ys`UGtlhDsg_>Nf)Us2Zgjl zbMXpzxp~NO&GoKr&Rjz2PhJ}7d?@GZnqqS?0mV?N6!Fg5=U^rifa{yl9l&3;o_yZh#enIp=|Mt|!(o(MRGz5NjU?pZZTYtBs@ICU%?Sj5tz;xK9Nx$`~fE@CTVN6nr6t`R1apPF~7Q zY0CN!32E18=CKQLV#gm!m=mX*=$f^nUsh;qjm5AX6d%@#638olrJ53^0#A)iNiFq( zw5s@`CkA@W*VR#4O=bK$TV!7%HHS_<0{!qX+o@)CFp<=`*wuOGG(ogm;M0VV zVasc;%I#W{!nVD8!$-0Kia%W;}WF!pv-{Wc< z=AqsHF=+&D>WiGhmpHR;Wpm4?W-ZlD=3$koHYux*&|2|^B!(v?j0Ztg9VJF-sL5{R z>vbbPz{Y0sE7|z6jO^@;XyFTXHlCu6I0~|e@G!RN$JZ_=run`UFz({rD?sVCi&6+J zp$cCPov4O*Jds;^S3<;}uO|>a{`5D$Np_Y#uB%=utL+o4c1!z`fc-Ha9gVXYu}Bhk zc%}H|(a#>?3TaOL0fZZK(Az+8=p6f;A*6W57Fg5T!mM*7jTg_r%vcL3O$qb3|u! zZ&mpAoxF0HXEr#|roi=^cAY|1+Rh{+vB1GWl^pq$Hppm*AMeb#K}sDO;)s*(TRtYu zb04>ZmG47u&#tv_kqO#TogW&Ue}Pl~2RJZn9#;2emvO8;(?Bi%sUn&7I{J>eV)irO z3AFVvZ*tC}w;j@MC=T%)nzvZ!hPBvr=l&ivU|2Si!jCiB$iWK|O*S-h7N|60DU8ds zH@!Ssiz=-m{$}ZqR4gNutY?nAMFK9t0!(T-DvVdczg0&pTuX`b_y%3qr#ZUDn+53t zFJ-f6lcwo(rXr05{Yp6+%<6LcBxJ`f;MSoTv8V|++a=@6RtKoLEHGjc{Yn-$d26y; zw|gHEk3e?&0$1GZeYWbo52QNf3kgjgm%rO(c5quHVy##8o9DfZpXfC4Ha+{lRtfBtgPWvQ{C~>`0+=ZFWWP-A}kdb_QYSk z)-dv@Qh$+_A12`H7TfA-yX6P!N7<=%|Acj4g5|G)i$-K29^Xz1@s`ER*!TGZ?|_95 zjsYMsUX5%g2W=3cO2hg@;H6k}h**d;S)p)@YNGn9941>AV@KE}o6)}h1^rqs>roB_ z%QGC?I7Oa7XJ7BXl@k=O_0*9!X%0{(GhKdOkcIMx@}xXJ656V?EfeJ&0KWMQ6lx^y zX8#6=e4&3^E+Aq0AB%^~CxWol`Tn*it)&X_D4b8%Ksu3HF_L-Qs#&7BQYuSDlFj&iIA1uadiH&i zt2^YSrpe+L`RNZYbxi2xzrFA@6mnr^9qFPSm|&dg`V@NN-ZeO6s_;aDMLo)sGt18; zc|69Y@=v=t+1rv}HJtdok<%>>i8|J;{FoRnT8azZlBQGvbV>@$cB?F@my52{a;$ma z)4ERU|06EWmW6KNbX5xvok>btK7j9|6tk;TfQhHo( zJo>|)wH%YuSzq_0|08*CxT)%@*`21poNW`HiTOKlv~4agbl-J~OcL%}$&|skGj-z# z8Z#Ih!50ymDcqsY5z8-}D3b*3Xy9{gcX*TjU<=_ngFcT*_#^laM9Mm0kv1M&J*8m* zY6CfSW)(*&ekvx>OVL$VliKPw&hj$07%c-EdX+Ru>6fa8^Q~3Kn^Knt)rv1RXxMz9X%$qGaPHwov-CnQ@-h{s$ondp8>6$COl*gA9p&rZpsEGuDppyEk_t4PddW91` z!XF-amk>+h(Z_i4aZ;`7w3_o_Eds@nhAZ?4itGGr_k6!$*E6za&MW1{?|Do#NS~|d z>B+OjBNiz7sl5p>8ipS1MeQ%Ur?BA7;)CW?&gjbTAk3{8aVGfegD8lpnmKmN@c6rua?lcdPg&zH$(5{d24_#O9%sw zO%_|wmCG(X2|=P{ICgS@G}cqvGvzwm^b0SBKem%T6d8yF3Ca3*5E*kvOwLUGAJKM( zNna!5Yu7ij*xN6+h-UNVsflsjO32&fU@A8{^bbL_Jh)nd?|@IP(g_Y#%~I^5|EBW8N~UEYd6@?aunb80{@%x zNA)cAVDY2j$p%jVfU~}F>|*`wzaeuowG~t-zullK)Lj5fzCb}{;IkMZ9D#5EetG~y z_kwoF)AmJ^PQ9icngif!SU7u4JYDIn_&CmhUf*=mA|NVrEhqv5w<$P%`R1eL1HX95 zkS%IzO6>)Edz;rEVX zQ;rI8%`d|qR|OHrn*=t{xuDz1xX^nY=P@HO;dJK|MGu-Mu;8&_K zA9ExeDrRZko%<;$GD$L+t$yYjg}m%iN|m^t!5bRs4RT{6%KQ9vbvpPoG&MVPp;CTS&hHXkMV=Vp^XG7w;0>xPNc= z#0%cyl+woYo~56+vhnWKKEu2Gke=x5sHSOTYV_a9 zY)IN>NP&$>wo8a1-a5TJoP6Ic{Nq%GzSYyJmY@td*Q`PQinQD@Z+}lmZMyOn?tp%p z+Bl&Hk4IBA$%1Yrbp=g?ZzY%8{VwNB!h`-^ZG8H2s2R%-msS1H)sf>56;l<@(-ba+ zN^Z0X{Tk~E%!MFdTXZD!nh(I)O&?$g&>+N=_@bfl9F^&0`6jZlcqvRjHRS~xykVS) z{`9FzKc7UhSu5dDCzIx6yJUzMFidK?s<5EQDuOx)8@cyi5G@EgbK#*_;#&zXW{HzO z?4_u%o5%BI;Sf*6czEhMiIto)niC=AvL0hOOs@y_qUxq^kACCfYThH>1Vx zTkFS*%Mc{VZXD-VUTG1!RiMXSKl){oFeTxNdEKpMzNo4yA%||4s_{pe4=L~DE9Emz zq6>WLv}n17muk9o2dhQY_I>|mVMJ(wJbjJ2#p3pdybTJ}+90`!X_bHnoU`TW@4XCp zNSoxhafuAWakILXUx&5o!=u*0pu72Xl-Mo91Xoq)ad0=RqD5XRYvnbpZcwByQ};Dt zu-*%_H+@HAFCTs@Uq!YIGG5H`$*piER0erw4)Mc|9@p4R3>tc#PX!SAWhQ<}E(+(k zfQ&k^=OUY@Qk2z75NOQF7g01CE_!Ene(j;&nwG3RwWJij8R{XWMmC&H8!YJv2e6vU ztD>)GDuhU|#x0(Z57TtMW!imY>7lq~&I6^9Mvk6tuIW2JM+$`MuvI_fr@75w zkO4h-M6|@^Ev(g+^?~B3n)~dlJ&FRn3*!j`Cmf7Rq6jsRw(J0C`m3S08`B}$SEyH) z0QW0HHr~dLujmm!P2>kZd2AiEqO3kC7rAqdn=413lpRAE7f9>5_NDAPM__ zICMD301(5k2w!})Hx*Dehi#R1b7{cXMz?>qF;p&M*f&HRm(K^V!)%qw_s)7I^7w;& zNAaDE<<%NZDy*De5@qSEiQVTW8QGLK&9TNZiK{xgtkg0|?EphRH;U?*%sSIm4=gAa zW41cvCdSUo59FMRj#ocBqNwN8Ib1EUYEYEF_(b_-VWHo4e209Ba{NdK=adqOdI$Uw zH5IKslDAEA;%Uk>yW&{C&v|lCz3qN$itR^PwT%?2L^R$`LS18hx8Ob0O_SXUzdVCd z@w!|Nr`g^sb(*|Oo@kE6>cg$QAGmcx;eH|h`R_gJn53CJR%BR=;gkSSPX7%$#}F_N zK2`+24m5Y@XwJcCj?S`nn$X2Nzn5-4_y+p^JMe9)oya-B-tv1d_(9<_DvRW><-4nI zh76}72ID8VKdQXOj6v+1<*{i3wOtMyJZYZcYc@UBdtIZSPBydHVh7q-V-LEx2i+S+ z2fWRAygrQGc>OqI(6T|{jm##Oqfn7VW4^P!$MQ#a?*}R5WB8-Krs^aG)ud1Ks6zr+ z`lOPA&ME}R!%763CbiXE!u2(a3q}bjNBHxS1H71sPd=Qvj0lGafpv2hyXsZ(`XaNxJM@TO%%fZU}4cZc5Wr;(E zc8s|^0sB2%t<79x>aNGCFq$up-j0K)oWhQDao932xj(|$2%$luJ%=0z8gj0?xZX9* z{g)zfcfLytbd#AallV$k<2;X+l6&Yzo2`YSER5r~t$!aCeeO>AHoa!3zT41o|5L!0 zX2iB`v)m2a`O2rlgSDkbPs2QA3vjA`Qwn(F#T)p)zAzM|#VRTH`#~t3#>D{@mUX;n zJG*k-jx)NH^Y4hMFn7+pQ$4j?;CJUBp!$8>jZBaaD1?Z?@A%1|b+GA<#uk62j%9ao zOUJ_BEAHUTRc6b*XA!(t6xZR*%vE2Yk6&CM7!f9E%GMc5C`89$O?w<1?c7kS+2Qqt z2QOk%rTM(EDZx63=A+$}4Kvy@@&ZAz5%<;=k`T`G(K(XJz=ep(4-}-**WX+lwhb&e zoX`0r@BcbTEn7j`X71{hZA|LOV84}3ysO0JO+O*CG%>B(As5Q~34n{+`9)beACXlAg>g!wP{KH?i2j)L#6&j2Jbx<=}KvGQazxR3jN7 z-kejSZZ643Uk3Dcqoi2#W-z8ST2>SdyUmp-H1NN_Pck2j%IW2dmR`#l-Jovry7R+_;F362#FkcT z3cH}gnaqsq{SU5`g`S#CY-7G8{?A#jx%ylY$vDl&@9rMf=Ws|Qt=uRuKuddvzh5wB z^!;irNAY%a{|c7H5V~xg)jKMjumHYha1{Ln@4|=W1RVtXbbROi$Fj#OcEp^$Xf?2X zftgnDMM_?qMgDJVtE}*n{6@x=`T>M=hiPgjs z)MxObJkK=F{QXi!zwO9(8}&@Nr%uR8IGC?_T&xtPn54O&G2okOE*+X(ULFJZ{GkeS zMH-JiG3ZFo(Y|-v7|w?gOi7?{T?{wpe+y1M7>%&t)AL7kXaUb&j^CcsMZXl&sk@W( zK_ZgdPB7(uwxin?d7}>w;cMf{W8<@b!{5lziAOYU0q4K_&X@9Y(~M{9-F`!OcNIwR z=}@~Om<0G%2%TY&a%x}dpHBqR9jgCNXZ3bNavh3-Y9J#7207gw^QysI8SR^YFIDR5 zs~?OS!M}0?V}R7GqcHEtU9N5vr7Bx;L$g?_(BM@b=h#a<>OZ9}GS^uwFkK6Otxylz zAe33~LZm-C1_~_#g7Zl==ig1#itka`nW$LxZSX;thQ&pPiMIUOCf~ms&0Ltv_llDz z!j}C1aGOc8j?5<_aQN4{jxr59im;gWnSW-swTQ6cA~Cw;U_)dP|JIr z{aZfk3;BGTco^7ZMgzQ{1&-L9kbxM!0#2&p*>%Z_K!3_+;&LDWIp;rIW5wvIt}m)} zRF##9;^X5Ft4-TirgGw}?7w;}mLs%I1eylxXPPP__Gf#a{le>Ax;;~+3#O7$(e(G~ zu*g|kx1ZlBYf&%?r8GYy4c@1Csgs1NX7V%U&%ah2p#3N&ezRBLxE$Y?j+P-_L-b4E z*uiPoQt?Gfag)na8`Y+BOSdRPr}2d|U4@w0t5_xJ`ZGRzX|PEcnUXj>k)NItf9xfr zJ4&ot)!J~0^h%CnS=^$cT!!Nkc7V5(XYtRg2}Ru>bjQAwYfmd`N_W@}WuD!heG>9r zv_B@f|8Gp}@?_4h9r;G3_2QMEz#_g;Pfw_yx^pNd4pR1pi>c7Rg{5IY6?n-8;LHTJ zay`2gm4eUQ2itY*l5;nRcTZcG$%7<(8|$3D)zO~P_ESB~zuQMm>{vRK8CSX=NJhZO znUCFongY|PW^B^#>{ch0YJQw8#Wyz>kzfMia_U61S+7$RtWnJW?Z7C&W+L{h3x23y z5lJ%%Q-4^yS##frgU_LqgWZFFk)3fVMKqM>lQ1qMTg&Cw%t6ZqqQaYJw|XW58uRH! zn0p3ZAGJ<}x-Xl}!%hlA!&Egi9{$6ml1QI%|340JLdM@gTLt+pp;QE8i61cmi5+lm zY+=m2g~O)!5kKdKa(NlyqHlV(U`F3FyZu1f6rOfAJ7HN_D>ViyUCuTO0gf&^>#|1cD6*che^`o*FIQ|k!JK$a!s(aCi`%=x549=;}w`egznl1 zb-GmOz7HiXHw~7y`_+@2AlYa9?O^HtzE(eX@W5$ze!#716~vZlJEyZ8N2omoxX|7L zR5D%gMf$Jm)KieH=l&G_h>A)Gu`<$i>fyHn8qY?A@Xnp|)zrIAIU8doZ-qco96_ko z@d~*kPzaYG=AA<@!m(w?O*VSgQSh3D2`$H|4udNyPDR!eKrmKdRowv5Tph-Y%;q{# za2p3$4dsL*|J82vaPxmLM7%jr?35 zaC@V{WTtk2I*{>M5W=X9Z@2nHK*(3P|H}lqiXBJ-Gzj$}qhP?^O(tMN1{BJvo5#$+ zw~=sa{{%1+SHyAW0wdknv*RcPZQo3S?xcT%H?}hm5aR_fEr`!O0(1|JKReHwR8?g8 zq0t@RX)=t|M2BE$R2>-|Edcs}JZttaIKtlpK&3#yZnh9ITlIQrr{Gi9Sa;TjFM^6S zf=D;*t4O4N7_MX(9+)HWJ#}NpOt7FOkv_r})2W20nKyPBpA<(9M3Kd|D=d>a3>rNm z`(pnYxq-OpVC2RNPXo})WRSszCb#brUgSAd3(09el3|yTL@JIRYeVei&JS{H}rq zi^%;xXrqk4gRuaZADJ+RtlQJo4qi8QfH>7ZE1^de@$UU!&kXfP=5X>juPGs86Ol=3 zCFdt4D`_g9Afzq_v&YU0U6dzj2^e{pa1kPX56(RmtF!dznkb7X-!)Td{weJStNB4r?9N}S2 zd~jfRuUTRSlLiml0%7IRWWcNk$0&z?Cdko335nQ?pD#r}Lfa@BV$1#sL9Tzb43NJC zN#Y~mbM_-P;gtb!x#i&AeHfZKUuSa=@OPBlJ|GmhpYIvQsUTBLIrM6;`G8erYgY~q zY7dE#HZLYQGQkG6L_BhyOD$X<)-pq37_Fqg1EO?3I2njUM$^i&!py1J`u0;W5_BP- zG>jIrfVg2bYV6bt#(pAMI%Edy=W4M0zJVa|0fP2hqekXdya>>SUg(@7@_^=msR2+E zLOV6E2pv9av~Nx2QQ-6YF9XIhzT;!x6%C;I&sn+mH-Zq!>Hl@SF6_BcX0%A#9r@Ms b=^T^pg8QqTBvE8|F`A5&f@HC{q2GT4>ebbw diff --git a/tests/experimental/test_qc_image.py b/tests/experimental/test_qc_image.py new file mode 100644 index 000000000..1fd62cfe2 --- /dev/null +++ b/tests/experimental/test_qc_image.py @@ -0,0 +1,216 @@ +from __future__ import annotations + +import copy + +import numpy as np +import pytest +import spatialdata_plot as sdp + +import squidpy as sq +from tests.conftest import PlotTester, PlotTesterMeta + +_ = sdp + +# Large tile size to keep tests fast (fewer tiles = less computation). +_FAST_TILE = (1000, 1000) + + +@pytest.fixture(scope="session") +def _sdata_hne_with_tissue(): + """Load the Visium H&E dataset once and pre-compute the tissue mask. + + The tissue mask is the most expensive setup step (~16 s). Pre-computing + it here and deepcopying per test avoids paying that cost repeatedly. + """ + sdata = sq.datasets.visium_hne_sdata() + sq.experimental.im.detect_tissue(sdata, image_key="hne", scale="scale0", inplace=True, new_labels_key="hne_tissue") + return sdata + + +@pytest.fixture() +def sdata_hne(_sdata_hne_with_tissue): + """Per-test deep copy so each test gets a fresh SpatialData with tissue mask.""" + return copy.deepcopy(_sdata_hne_with_tissue) + + +class TestQCImage(PlotTester, metaclass=PlotTesterMeta): + def test_plot_calc_qc_image_hne(self, sdata_hne): + """Test QC image overlay with a single sharpness metric.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=[sq.experimental.im.QCMetric.TENENGRAD], + progress=False, + ) + + ( + sdata_hne.pl.render_images() + .pl.render_shapes("qc_img_hne_grid", color="qc_outlier", groups="True", palette="red", fill_alpha=1.0) + .pl.show() + ) + + def test_plot_calc_qc_image_not_hne(self, sdata_hne): + """Test QC image overlay with is_hne=False default metrics.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + is_hne=False, + metrics=None, + progress=False, + ) + + ( + sdata_hne.pl.render_images() + .pl.render_shapes("qc_img_hne_grid", color="qc_outlier", groups="True", palette="red", fill_alpha=1.0) + .pl.show() + ) + + def test_plot_plot_qc_image(self, sdata_hne): + """Test QC image plotting function.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=[sq.experimental.im.QCMetric.TENENGRAD], + progress=False, + ) + + sq.experimental.pl.qc_image( + sdata_hne, + image_key="hne", + ) + + +def test_qc_image_hne_metric_without_hne_flag(sdata_hne): + """Test that H&E metrics raise ValueError when is_hne=False.""" + with pytest.raises(ValueError, match="H&E-specific metrics"): + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + is_hne=False, + metrics=[sq.experimental.im.QCMetric.HEMATOXYLIN_MEAN], + ) + + +def test_qc_image_default_metrics_hne(sdata_hne): + """Test that default H&E metrics produce expected var_names.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=None, + is_hne=True, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + expected = { + "qc_tenengrad", + "qc_var_of_laplacian", + "qc_entropy", + "qc_brightness_mean", + "qc_hematoxylin_mean", + "qc_eosin_mean", + } + assert set(adata.var_names) == expected + + +def test_qc_image_default_metrics_generic(sdata_hne): + """Test that default generic metrics produce expected var_names.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=None, + is_hne=False, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + expected = { + "qc_tenengrad", + "qc_var_of_laplacian", + "qc_entropy", + "qc_brightness_mean", + } + assert set(adata.var_names) == expected + + +def test_qc_image_rgb_metric(sdata_hne): + """Test running a single RGB metric.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + is_hne=True, + metrics=[sq.experimental.im.QCMetric.HEMATOXYLIN_MEAN], + detect_tissue=False, + detect_outliers=False, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + assert "qc_hematoxylin_mean" in adata.var_names + + +def test_qc_image_outlier_detection_with_tissue(sdata_hne): + """Test that outlier detection with tissue classification populates expected columns.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=[sq.experimental.im.QCMetric.TENENGRAD], + detect_outliers=True, + detect_tissue=True, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + assert "qc_outlier" in adata.obs.columns + assert "is_tissue" in adata.obs.columns + assert "is_background" in adata.obs.columns + assert "unfocus_score" in adata.obs.columns + assert set(adata.obs["qc_outlier"].cat.categories) == {"False", "True"} + # At least some tiles should be classified as tissue + assert (adata.obs["is_tissue"] == "True").any() + + +def test_qc_image_outlier_detection_without_tissue(sdata_hne): + """Test outlier detection without tissue classification scores all tiles.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=[sq.experimental.im.QCMetric.TENENGRAD], + detect_outliers=True, + detect_tissue=False, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + assert "qc_outlier" in adata.obs.columns + assert "unfocus_score" in adata.obs.columns + # Without tissue detection, these columns should not exist + assert "is_tissue" not in adata.obs.columns + assert "is_background" not in adata.obs.columns + # All tiles should have a valid unfocus score (no NaNs) + assert not np.any(np.isnan(adata.obs["unfocus_score"].values)) + + +def test_qc_image_compute_only(sdata_hne): + """Test compute-only mode without outlier detection.""" + sq.experimental.im.qc_image( + sdata_hne, + image_key="hne", + tile_size=_FAST_TILE, + metrics=[sq.experimental.im.QCMetric.TENENGRAD, sq.experimental.im.QCMetric.BRIGHTNESS_MEAN], + detect_outliers=False, + detect_tissue=False, + progress=False, + ) + adata = sdata_hne.tables["qc_img_hne"] + assert set(adata.var_names) == {"qc_tenengrad", "qc_brightness_mean"} + # No outlier-related columns + assert "qc_outlier" not in adata.obs.columns + assert "unfocus_score" not in adata.obs.columns + # Spatial coordinates should still be present + assert "centroid_y" in adata.obs.columns + assert "spatial" in adata.obsm From 1cb6fde0e4719d7e9b29fb53abeb930512a953c4 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 09:04:03 +0200 Subject: [PATCH 048/131] remove coord_type abstraction --- docs/extensibility.md | 6 ------ src/squidpy/gr/_build.py | 5 ++--- src/squidpy/gr/neighbors.py | 25 ++----------------------- 3 files changed, 4 insertions(+), 32 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index 798d8c714..5ad3c0b98 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -15,7 +15,6 @@ The `squidpy.gr.neighbors` module exposes two builder base classes: | Base class | Method / property | Required | Purpose | |---|---|---|---| -| {class}`~squidpy.gr.neighbors.GraphBuilder` | {attr}`~squidpy.gr.neighbors.GraphBuilder.coord_type` | yes | Return the {class}`~squidpy._constants._constants.CoordType` this builder supports. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` using the coordinate and matrix types of your custom backend. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_percentile` | no | Override percentile handling when the backend needs custom behavior. | @@ -60,7 +59,6 @@ import numpy as np from scipy.sparse import csr_matrix from snnpy import build_snn_model -from squidpy._constants._constants import CoordType from squidpy.gr.neighbors import GraphBuilderCSR @@ -71,10 +69,6 @@ class SNNRadiusBuilder(GraphBuilderCSR): super().__init__(**kwargs) self.radius = radius - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - def build_graph(self, coords): N = coords.shape[0] model = build_snn_model(coords, verbose=0) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 25dfd0826..5532157d6 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -87,7 +87,7 @@ def _resolve_graph_builder( if coord_type is None: if radius is not None: logg.warning( - f"Graph creation with `radius` is only available when `coord_type = {CoordType.GENERIC!r}` specified. " + "Graph creation with `radius` is only available for generic coordinates. " f"Ignoring parameter `radius = {radius}`." ) coord_type = CoordType.GRID if has_spatial_uns else CoordType.GENERIC @@ -779,7 +779,7 @@ def _run_spatial_neighbors( libs = [None] start = logg.info( - f"Creating graph using `{builder.coord_type}` coordinates and `{builder.transform}` transform and `{len(libs)}` libraries." + f"Creating graph using `{builder.transform}` transform and `{len(libs)}` libraries." ) if library_key is not None: mats: list[tuple[Any, Any]] = [] @@ -800,7 +800,6 @@ def _run_spatial_neighbors( "distances_key": dists_key, "params": { "n_neighbors": getattr(builder, "n_neighs", 6), - "coord_type": builder.coord_type.v, "radius": getattr(builder, "radius", None), "transform": builder.transform.v, }, diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index b5f2aad16..0d880192c 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -26,7 +26,7 @@ from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances from sklearn.neighbors import NearestNeighbors -from squidpy._constants._constants import CoordType, Transform +from squidpy._constants._constants import Transform from squidpy._utils import NDArrayA from squidpy._validators import assert_positive @@ -55,11 +55,6 @@ def __init__( self.set_diag = set_diag self.percentile = percentile - @property - @abstractmethod - def coord_type(self) -> CoordType: - """Coordinate system supported by this builder.""" - def build(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: adj, dst = self.build_graph(coords) adj, dst = self.apply_filters(adj, dst) @@ -127,7 +122,7 @@ def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, c return adj, dst def apply_percentile(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - if self.percentile is not None and self.coord_type == CoordType.GENERIC: + if self.percentile is not None: threshold = np.percentile(dst.data, self.percentile) adj[dst > threshold] = 0.0 dst[dst > threshold] = 0.0 @@ -176,10 +171,6 @@ def __init__( super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.n_neighs = n_neighs - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") @@ -219,10 +210,6 @@ def __init__( super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.radius = radius - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] r = self.radius if isinstance(self.radius, int | float) else max(self.radius) @@ -272,10 +259,6 @@ def __init__( super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) self.radius = radius - @property - def coord_type(self) -> CoordType: - return CoordType.GENERIC - def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tri = Delaunay(coords) @@ -328,10 +311,6 @@ def __init__( self.n_rings = n_rings self.delaunay = delaunay - @property - def coord_type(self) -> CoordType: - return CoordType.GRID - def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: if self.n_rings > 1: adj = self._base_adjacency(coords, set_diag=True) From 8afcd030b4e559d5ae760c67377da712fb8093a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 07:04:32 +0000 Subject: [PATCH 049/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/_build.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 5532157d6..58617d0fb 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -778,9 +778,7 @@ def _run_spatial_neighbors( else: libs = [None] - start = logg.info( - f"Creating graph using `{builder.transform}` transform and `{len(libs)}` libraries." - ) + start = logg.info(f"Creating graph using `{builder.transform}` transform and `{len(libs)}` libraries.") if library_key is not None: mats: list[tuple[Any, Any]] = [] ixs: list[int] = [] From 21f02c376059b06c7579365fb1b0a79b6246dce3 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 13 Apr 2026 23:27:43 +0200 Subject: [PATCH 050/131] make builder positional consistently --- src/squidpy/gr/_build.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 58617d0fb..bac3aa6fe 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -376,7 +376,7 @@ def spatial_neighbors( ) return _run_spatial_neighbors( - adata, builder=builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy + adata, builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy ) @@ -441,7 +441,7 @@ def spatial_neighbors_from_builder( ) return _run_spatial_neighbors( adata, - builder=builder, + builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, @@ -527,7 +527,7 @@ def spatial_neighbors_knn( ) return _run_spatial_neighbors( adata, - builder=builder, + builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, @@ -596,7 +596,7 @@ def spatial_neighbors_radius( ) return _run_spatial_neighbors( adata, - builder=builder, + builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, @@ -665,7 +665,7 @@ def spatial_neighbors_delaunay( ) return _run_spatial_neighbors( adata, - builder=builder, + builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, @@ -753,7 +753,7 @@ def spatial_neighbors_grid( ) return _run_spatial_neighbors( adata, - builder=builder, + builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, @@ -763,8 +763,8 @@ def spatial_neighbors_grid( def _run_spatial_neighbors( adata: AnnData, - *, builder: GraphBuilder[Any, Any], + *, spatial_key: str = Key.obsm.spatial, library_key: str | None = None, key_added: str = "spatial", From 744c757e2825ddc525420e4d00588b4f584b05a2 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Tue, 14 Apr 2026 00:10:38 +0200 Subject: [PATCH 051/131] make more abstractions --- docs/api.md | 4 + docs/extensibility.md | 20 +++-- src/squidpy/gr/_build.py | 4 + src/squidpy/gr/neighbors.py | 168 ++++++++++++++++++++++-------------- 4 files changed, 125 insertions(+), 71 deletions(-) diff --git a/docs/api.md b/docs/api.md index dd19bb18e..4d23a6ed5 100644 --- a/docs/api.md +++ b/docs/api.md @@ -128,6 +128,10 @@ See the {doc}`extensibility guide ` for how to implement a custo gr.neighbors.GraphBuilder gr.neighbors.GraphBuilderCSR + gr.neighbors.GraphPostprocessor + gr.neighbors.DistanceIntervalPostprocessor + gr.neighbors.PercentilePostprocessor + gr.neighbors.TransformPostprocessor gr.neighbors.KNNBuilder gr.neighbors.RadiusBuilder gr.neighbors.DelaunayBuilder diff --git a/docs/extensibility.md b/docs/extensibility.md index 5ad3c0b98..befd61b23 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -8,24 +8,28 @@ The `squidpy.gr.neighbors` module exposes two builder base classes: Use it when you want to plug in a custom coordinate type or sparse-matrix backend. - {class}`~squidpy.gr.neighbors.GraphBuilderCSR` is the CSR-specialized builder used by the built-in graph construction strategies. Use it when your builder returns - {class}`~scipy.sparse.csr_matrix` objects and should reuse the default CSR-specific - percentile filtering, transform handling, and sparse warning suppression. + {class}`~scipy.sparse.csr_matrix` objects and should reuse Squidpy's CSR-specific + postprocessors, sparse warning suppression, and multi-library combination. +- Reusable postprocessors such as + {class}`~squidpy.gr.neighbors.DistanceIntervalPostprocessor`, + {class}`~squidpy.gr.neighbors.PercentilePostprocessor`, and + {class}`~squidpy.gr.neighbors.TransformPostprocessor` are also exposed for + custom builder composition. ### What to override | Base class | Method / property | Required | Purpose | |---|---|---|---| | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.build_graph` | yes | Construct and return ``(adj, dst)`` using the coordinate and matrix types of your custom backend. | -| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_filters` | no | Post-process the raw ``adj``/``dst`` before percentile filtering and transform. | -| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_percentile` | no | Override percentile handling when the backend needs custom behavior. | -| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.apply_transform` | no | Override transform handling when the backend needs custom behavior. | +| {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.postprocessors` | no | Return post-build processing steps for ``(adj, dst)``. You can either override this or pass ``postprocessors=...`` to ``super().__init__()``. | | {class}`~squidpy.gr.neighbors.GraphBuilder` | {meth}`~squidpy.gr.neighbors.GraphBuilder.combine` | no | Combine per-library results when using ``library_key``. If you do not need ``library_key`` support, leaving this unimplemented is fine. | The generic builder only defines the pipeline. The CSR-specialized builder adds -the built-in CSR behavior for percentile filtering, adjacency transforms, -multi-library ``library_key`` combination, and -{class}`~scipy.sparse.SparseEfficiencyWarning` suppression. +multi-library ``library_key`` combination and +{class}`~scipy.sparse.SparseEfficiencyWarning` suppression, while built-in and +custom CSR builders can compose the public reusable postprocessors for +distance-interval pruning, percentile filtering, and adjacency transforms. Here ``adj`` and ``dst`` are square sparse matrices of shape ``(n_obs, n_obs)`` with matching sparsity structure: diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index bac3aa6fe..e2778fa62 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -410,6 +410,10 @@ def spatial_neighbors_from_builder( {{class}}`~squidpy.gr.neighbors.GraphBuilderCSR`, while custom backends can implement the more generic {{class}}`~squidpy.gr.neighbors.GraphBuilder` interface directly. + Reusable post-build operations are also exposed via + :class:`~squidpy.gr.neighbors.DistanceIntervalPostprocessor`, + :class:`~squidpy.gr.neighbors.PercentilePostprocessor`, and + :class:`~squidpy.gr.neighbors.TransformPostprocessor`. Custom builders only need to implement multi-library support when using ``library_key``; otherwise leaving :meth:`~squidpy.gr.neighbors.GraphBuilder.combine` unimplemented is fine. diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 0d880192c..9a3078e64 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -7,7 +7,8 @@ import warnings from abc import ABC, abstractmethod -from collections.abc import Iterable, Sequence +from collections.abc import Callable, Sequence +from dataclasses import dataclass from itertools import chain from typing import Generic, TypeVar, cast @@ -30,19 +31,32 @@ from squidpy._utils import NDArrayA from squidpy._validators import assert_positive -__all__ = ["GraphBuilder", "GraphBuilderCSR", "KNNBuilder", "RadiusBuilder", "DelaunayBuilder", "GridBuilder"] +__all__ = [ + "GraphBuilder", + "GraphBuilderCSR", + "GraphPostprocessor", + "DistanceIntervalPostprocessor", + "PercentilePostprocessor", + "TransformPostprocessor", + "KNNBuilder", + "RadiusBuilder", + "DelaunayBuilder", + "GridBuilder", +] CoordT = TypeVar("CoordT") GraphMatrixT = TypeVar("GraphMatrixT") +GraphPostprocessor = Callable[[GraphMatrixT, GraphMatrixT], tuple[GraphMatrixT, GraphMatrixT]] class GraphBuilder(ABC, Generic[CoordT, GraphMatrixT]): """Base class for spatial graph construction strategies. Custom builders must implement :meth:`build_graph`. Overriding - :meth:`combine` is optional and only needed to support multi-library graph - construction via ``library_key``. + :meth:`postprocessors` and :meth:`combine` is optional. Postprocessors can + be provided directly via ``__init__`` or by overriding + :meth:`postprocessors`. """ def __init__( @@ -50,31 +64,26 @@ def __init__( transform: str | Transform | None = None, set_diag: bool = False, percentile: float | None = None, + postprocessors: Sequence[GraphPostprocessor[GraphMatrixT]] = (), ) -> None: self.transform = Transform.NONE if transform is None else Transform(transform) self.set_diag = set_diag self.percentile = percentile + self._postprocessors: list[GraphPostprocessor[GraphMatrixT]] = list(postprocessors) def build(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: adj, dst = self.build_graph(coords) - adj, dst = self.apply_filters(adj, dst) - adj, dst = self.apply_percentile(adj, dst) - adj, dst = self.apply_transform(adj, dst) + for postprocessor in self.postprocessors(): + adj, dst = postprocessor(adj, dst) return adj, dst @abstractmethod def build_graph(self, coords: CoordT) -> tuple[GraphMatrixT, GraphMatrixT]: """Construct raw adjacency and distance matrices.""" - def apply_filters(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: - """Apply builder-specific post-processing filters.""" - return adj, dst - - def apply_percentile(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: - return adj, dst - - def apply_transform(self, adj: GraphMatrixT, dst: GraphMatrixT) -> tuple[GraphMatrixT, GraphMatrixT]: - return adj, dst + def postprocessors(self) -> Sequence[GraphPostprocessor[GraphMatrixT]]: + """Return post-build processing steps for ``(adj, dst)``.""" + return self._postprocessors def combine( self, @@ -92,12 +101,12 @@ def combine( class GraphBuilderCSR(GraphBuilder[NDArrayA, csr_matrix], ABC): """CSR-based graph construction strategy. - Specializes :class:`GraphBuilder` for sparse CSR matrix output. Adds built-in handling - for percentile-based edge pruning, adjacency transforms (spectral/cosine), - SparseEfficiencyWarning suppression, and multi-library ``library_key`` - combination. All built-in concrete builders + Specializes :class:`GraphBuilder` for sparse CSR matrix output. Adds + SparseEfficiencyWarning suppression and multi-library ``library_key`` + combination. Built-in concrete builders (:class:`KNNBuilder`, :class:`RadiusBuilder`, :class:`DelaunayBuilder`, :class:`GridBuilder`) - inherit from this class. + inherit from this class and declare their postprocessors explicitly in + ``__init__`` using the reusable public postprocessor classes. Subclass this (not the generic :class:`GraphBuilder`) when implementing a builder that returns CSR matrices. @@ -117,30 +126,6 @@ def build(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: """Construct raw adjacency and distance matrices.""" - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - """Apply builder-specific post-processing filters.""" - return adj, dst - - def apply_percentile(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - if self.percentile is not None: - threshold = np.percentile(dst.data, self.percentile) - adj[dst > threshold] = 0.0 - dst[dst > threshold] = 0.0 - return adj, dst - - def apply_transform(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - adj.eliminate_zeros() - dst.eliminate_zeros() - - if self.transform == Transform.SPECTRAL: - return cast(csr_matrix, _transform_a_spectral(adj)), dst - if self.transform == Transform.COSINE: - return cast(csr_matrix, _transform_a_cosine(adj)), dst - if self.transform == Transform.NONE: - return adj, dst - - raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") - def combine( self, mats: Sequence[tuple[csr_matrix, csr_matrix]], @@ -168,7 +153,16 @@ def __init__( percentile: float | None = None, ) -> None: assert_positive(n_neighs, name="n_neighs") - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + postprocessors: list[GraphPostprocessor[csr_matrix]] = [] + if percentile is not None: + postprocessors.append(PercentilePostprocessor(percentile)) + postprocessors.append(TransformPostprocessor(Transform.NONE if transform is None else Transform(transform))) + super().__init__( + transform=transform, + set_diag=set_diag, + percentile=percentile, + postprocessors=postprocessors, + ) self.n_neighs = n_neighs def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: @@ -207,7 +201,18 @@ def __init__( set_diag: bool = False, percentile: float | None = None, ) -> None: - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + postprocessors: list[GraphPostprocessor[csr_matrix]] = [] + if isinstance(radius, tuple): + postprocessors.append(DistanceIntervalPostprocessor(tuple(sorted(radius)))) + if percentile is not None: + postprocessors.append(PercentilePostprocessor(percentile)) + postprocessors.append(TransformPostprocessor(Transform.NONE if transform is None else Transform(transform))) + super().__init__( + transform=transform, + set_diag=set_diag, + percentile=percentile, + postprocessors=postprocessors, + ) self.radius = radius def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: @@ -231,12 +236,6 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(adj, dst, self.radius) - return adj, dst - - class DelaunayBuilder(GraphBuilderCSR): """Build a generic point-cloud graph from a Delaunay triangulation. @@ -256,7 +255,18 @@ def __init__( set_diag: bool = False, percentile: float | None = None, ) -> None: - super().__init__(transform=transform, set_diag=set_diag, percentile=percentile) + postprocessors: list[GraphPostprocessor[csr_matrix]] = [] + if isinstance(radius, tuple): + postprocessors.append(DistanceIntervalPostprocessor(tuple(sorted(radius)))) + if percentile is not None: + postprocessors.append(PercentilePostprocessor(percentile)) + postprocessors.append(TransformPostprocessor(Transform.NONE if transform is None else Transform(transform))) + super().__init__( + transform=transform, + set_diag=set_diag, + percentile=percentile, + postprocessors=postprocessors, + ) self.radius = radius def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: @@ -278,12 +288,6 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst - def apply_filters(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: - if isinstance(self.radius, Iterable): - _filter_by_radius_interval(adj, dst, self.radius) - return adj, dst - - class GridBuilder(GraphBuilderCSR): """Build a grid-based spatial graph. @@ -306,7 +310,8 @@ def __init__( ) -> None: assert_positive(n_neighs, name="n_neighs") assert_positive(n_rings, name="n_rings") - super().__init__(transform=transform, set_diag=set_diag, percentile=None) + postprocessors = [TransformPostprocessor(Transform.NONE if transform is None else Transform(transform))] + super().__init__(transform=transform, set_diag=set_diag, percentile=None, postprocessors=postprocessors) self.n_neighs = n_neighs self.n_rings = n_rings self.delaunay = delaunay @@ -369,9 +374,9 @@ def _base_adjacency(self, coords: NDArrayA, *, set_diag: bool) -> csr_matrix: def _filter_by_radius_interval( adj: csr_matrix, dst: csr_matrix, - radius: Iterable[float], + radius: tuple[float, float], ) -> None: - minn, maxx = sorted(radius)[:2] + minn, maxx = radius mask = (dst.data < minn) | (dst.data > maxx) a_diag = adj.diagonal() @@ -379,6 +384,43 @@ def _filter_by_radius_interval( adj.data[mask] = 0.0 adj.setdiag(a_diag) +@dataclass(frozen=True) +class DistanceIntervalPostprocessor: + interval: tuple[float, float] + + def __call__(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: + _filter_by_radius_interval(adj, dst, self.interval) + return adj, dst + + +@dataclass(frozen=True) +class PercentilePostprocessor: + percentile: float + + def __call__(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: + threshold = np.percentile(dst.data, self.percentile) + adj[dst > threshold] = 0.0 + dst[dst > threshold] = 0.0 + return adj, dst + + +@dataclass(frozen=True) +class TransformPostprocessor: + transform: Transform + + def __call__(self, adj: csr_matrix, dst: csr_matrix) -> tuple[csr_matrix, csr_matrix]: + adj.eliminate_zeros() + dst.eliminate_zeros() + + if self.transform == Transform.SPECTRAL: + return cast(csr_matrix, _transform_a_spectral(adj)), dst + if self.transform == Transform.COSINE: + return cast(csr_matrix, _transform_a_cosine(adj)), dst + if self.transform == Transform.NONE: + return adj, dst + + raise NotImplementedError(f"Transform `{self.transform}` is not yet implemented.") + @njit def _csr_bilateral_diag_scale_helper( From dbc7f0d806ac1e8c60798384e7ba1b8c326b32a3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:10:38 +0000 Subject: [PATCH 052/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/gr/neighbors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 9a3078e64..d99646734 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -236,6 +236,7 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst + class DelaunayBuilder(GraphBuilderCSR): """Build a generic point-cloud graph from a Delaunay triangulation. @@ -288,6 +289,7 @@ def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: dst.setdiag(0.0) return adj, dst + class GridBuilder(GraphBuilderCSR): """Build a grid-based spatial graph. @@ -384,6 +386,7 @@ def _filter_by_radius_interval( adj.data[mask] = 0.0 adj.setdiag(a_diag) + @dataclass(frozen=True) class DistanceIntervalPostprocessor: interval: tuple[float, float] From d7e5e5f0c58d4043414bb3eb33b896c4844189a8 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Tue, 14 Apr 2026 00:20:16 +0200 Subject: [PATCH 053/131] graphmatrixT exposure --- docs/api.md | 2 ++ pyproject.toml | 2 +- src/squidpy/gr/__init__.py | 2 ++ src/squidpy/gr/_build.py | 7 +++---- src/squidpy/gr/neighbors.py | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/api.md b/docs/api.md index 4d23a6ed5..e975967b4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -23,6 +23,7 @@ import squidpy as sq gr.spatial_neighbors_radius gr.spatial_neighbors_delaunay gr.spatial_neighbors_grid + gr.GraphMatrixT gr.SpatialNeighborsResult gr.mask_graph gr.nhood_enrichment @@ -128,6 +129,7 @@ See the {doc}`extensibility guide ` for how to implement a custo gr.neighbors.GraphBuilder gr.neighbors.GraphBuilderCSR + gr.neighbors.GraphMatrixT gr.neighbors.GraphPostprocessor gr.neighbors.DistanceIntervalPostprocessor gr.neighbors.PercentilePostprocessor diff --git a/pyproject.toml b/pyproject.toml index 06e9dfc5a..5dd9499c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -261,7 +261,7 @@ addopts = [ markers = [ "internet: tests that require internet", "gpu: tests that require GPU", -] + ] [tool.coverage] run.branch = true diff --git a/src/squidpy/gr/__init__.py b/src/squidpy/gr/__init__.py index fce615b66..5a9a489fc 100644 --- a/src/squidpy/gr/__init__.py +++ b/src/squidpy/gr/__init__.py @@ -4,6 +4,7 @@ from squidpy.gr import neighbors from squidpy.gr._build import ( + GraphMatrixT, SpatialNeighborsResult, mask_graph, spatial_neighbors, @@ -26,6 +27,7 @@ from squidpy.gr._sepal import sepal __all__ = [ + "GraphMatrixT", "SpatialNeighborsResult", "NhoodEnrichmentResult", "neighbors", diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index e2778fa62..2d9d1b3bd 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,7 +3,7 @@ from __future__ import annotations import warnings -from typing import Any, Generic, NamedTuple, TypeVar +from typing import Any, Generic, NamedTuple import geopandas as gpd import numpy as np @@ -36,12 +36,14 @@ from squidpy.gr.neighbors import ( DelaunayBuilder, GraphBuilder, + GraphMatrixT, GridBuilder, KNNBuilder, RadiusBuilder, ) __all__ = [ + "GraphMatrixT", "SpatialNeighborsResult", "spatial_neighbors", "spatial_neighbors_from_builder", @@ -52,9 +54,6 @@ ] -GraphMatrixT = TypeVar("GraphMatrixT") - - class SpatialNeighborsResult(NamedTuple, Generic[GraphMatrixT]): """Result of spatial_neighbors function.""" diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index d99646734..422fbf77a 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -32,6 +32,7 @@ from squidpy._validators import assert_positive __all__ = [ + "GraphMatrixT", "GraphBuilder", "GraphBuilderCSR", "GraphPostprocessor", From 13768e2c72b5307ff67706c810b76d9c3800fafe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:20:18 +0000 Subject: [PATCH 054/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5dd9499c8..06e9dfc5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -261,7 +261,7 @@ addopts = [ markers = [ "internet: tests that require internet", "gpu: tests that require GPU", - ] +] [tool.coverage] run.branch = true From 21f248e8423ecc1a24c2079bfd8be4aba7966a06 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Tue, 12 May 2026 13:58:47 +0200 Subject: [PATCH 055/131] add radius: float | int | tuple t --- src/squidpy/gr/_build.py | 22 ++++++++++++++++------ src/squidpy/gr/neighbors.py | 9 ++++++++- tests/graph/test_spatial_neighbors.py | 15 ++++++++++++++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 2d9d1b3bd..8e3e3c342 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -109,7 +109,13 @@ def _resolve_graph_builder( FutureWarning, stacklevel=3, ) - return DelaunayBuilder(**common, radius=radius, percentile=percentile) + # Preserve the documented legacy contract: under the deprecated + # `spatial_neighbors`, a scalar `radius` with `delaunay=True` is silently + # ignored. The new `DelaunayBuilder(radius=r)` interprets a scalar as + # the interval `(0.0, r)`, so we strip scalars here before delegating + # and only forward tuple intervals to keep the legacy behavior intact. + legacy_radius = radius if isinstance(radius, tuple) else None + return DelaunayBuilder(**common, radius=legacy_radius, percentile=percentile) if radius is not None: # TODO: below check should be removed once legacy mode spatial_neighbors is deprecated if n_neighs_was_set: @@ -615,7 +621,7 @@ def spatial_neighbors_delaunay( elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, library_key: str | None = None, - radius: tuple[float, float] | None = None, + radius: float | tuple[float, float] | None = None, percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool = False, @@ -636,10 +642,14 @@ def spatial_neighbors_delaunay( %(sdata_params)s %(library_key)s radius - If a :class:`tuple`, used as a post-construction pruning interval - ``[min(radius), max(radius)]``. This does not change the triangulation - itself; it only removes Delaunay edges whose Euclidean lengths fall - outside the interval. + Post-construction edge pruning of the Delaunay graph. The triangulation + itself is never changed; only the resulting edges are filtered. + + - :class:`tuple` ``(min, max)``: keep edges with Euclidean length + in ``[min(radius), max(radius)]``. + - :class:`float` / :class:`int` ``r``: shorthand for ``(0.0, r)``, + i.e. keep edges with length at most ``r``. + - ``None``: keep every Delaunay edge. %(graph_common_params)s %(copy)s diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 422fbf77a..12165455c 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -246,6 +246,11 @@ class DelaunayBuilder(GraphBuilderCSR): ``GridBuilder(delaunay=True)``, this builder uses geometry-based connectivity and stores real Euclidean edge lengths. + ``radius`` only controls post-construction edge pruning; the triangulation + itself is unchanged. A tuple ``(min, max)`` keeps edges with Euclidean + length in that interval. A scalar ``r`` is shorthand for ``(0.0, r)``, + i.e. keep edges with length at most ``r``. ``None`` keeps every edge. + See :func:`~squidpy.gr.spatial_neighbors_delaunay` for the user-facing API or :func:`~squidpy.gr.spatial_neighbors_from_builder` for direct builder usage. """ @@ -257,8 +262,10 @@ def __init__( set_diag: bool = False, percentile: float | None = None, ) -> None: + if isinstance(radius, int | float): + radius = (0.0, float(radius)) postprocessors: list[GraphPostprocessor[csr_matrix]] = [] - if isinstance(radius, tuple): + if radius is not None: postprocessors.append(DistanceIntervalPostprocessor(tuple(sorted(radius)))) if percentile is not None: postprocessors.append(PercentilePostprocessor(percentile)) diff --git a/tests/graph/test_spatial_neighbors.py b/tests/graph/test_spatial_neighbors.py index 150d211bb..63b441c84 100644 --- a/tests/graph/test_spatial_neighbors.py +++ b/tests/graph/test_spatial_neighbors.py @@ -209,8 +209,12 @@ def test_builder_module_export(self): ({"n_neighs": 3, "coord_type": "generic"}, KNNBuilder(n_neighs=3)), ({"radius": 5.0, "coord_type": "generic"}, RadiusBuilder(radius=5.0)), ({"delaunay": True, "coord_type": "generic"}, DelaunayBuilder()), + ( + {"delaunay": True, "radius": (2.0, 4.0), "coord_type": "generic"}, + DelaunayBuilder(radius=(2.0, 4.0)), + ), ], - ids=["knn", "radius", "delaunay"], + ids=["knn", "radius", "delaunay", "delaunay_interval"], ) def test_generic_builder_matches_legacy(self, non_visium_adata: AnnData, legacy_kwargs: dict, builder: object): legacy = spatial_neighbors(non_visium_adata, **legacy_kwargs, copy=True) @@ -264,6 +268,15 @@ def test_delaunay_mode_ignores_scalar_radius(self, non_visium_adata: AnnData): np.testing.assert_array_equal(default.connectivities.toarray(), ignored.connectivities.toarray()) np.testing.assert_allclose(default.distances.toarray(), ignored.distances.toarray()) + def test_delaunay_builder_scalar_radius_equals_zero_max_tuple(self, non_visium_adata: AnnData): + scalar = spatial_neighbors_from_builder(non_visium_adata, builder=DelaunayBuilder(radius=5.0), copy=True) + interval = spatial_neighbors_from_builder( + non_visium_adata, builder=DelaunayBuilder(radius=(0.0, 5.0)), copy=True + ) + + np.testing.assert_array_equal(scalar.connectivities.toarray(), interval.connectivities.toarray()) + np.testing.assert_allclose(scalar.distances.toarray(), interval.distances.toarray()) + def test_delaunay_mode_warns_on_n_neighs(self, non_visium_adata: AnnData): with pytest.warns(FutureWarning, match=r"Parameter `n_neighs` is ignored when `delaunay=True`"): spatial_neighbors(non_visium_adata, coord_type="generic", delaunay=True, n_neighs=3, copy=True) From 3bbc535342b07887dfb3d4b789619aa14b24085f Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Tue, 12 May 2026 14:08:45 +0200 Subject: [PATCH 056/131] 4 or 6 --- src/squidpy/gr/_build.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 8e3e3c342..37bab7544 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -3,7 +3,7 @@ from __future__ import annotations import warnings -from typing import Any, Generic, NamedTuple +from typing import Any, Generic, Literal, NamedTuple import geopandas as gpd import numpy as np @@ -694,7 +694,7 @@ def spatial_neighbors_grid( elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, library_key: str | None = None, - n_neighs: int = 6, + n_neighs: Literal[4, 6] = 6, n_rings: int = 1, delaunay: bool = False, transform: str | Transform | None = None, @@ -718,9 +718,10 @@ def spatial_neighbors_grid( %(library_key)s n_neighs Number of neighboring tiles used to form the base grid connectivity. - Defaults to ``6``. On a Visium-like hexagonal grid, ``6`` corresponds to - the immediate surrounding spots, while smaller values such as ``3`` make - the first-ring graph deliberately sparser. + Defaults to ``6``. Use ``4`` for a square grid or ``6`` for a hexagonal + grid such as Visium; other values are accepted at runtime but their + geometric interpretation on a regular grid is not part of the public + API. n_rings Number of rings of neighbors. Defaults to ``1``. ``n_rings=1`` keeps only immediate neighbors; larger values add progressively more distant From 77627dcd1b45e6fba18d33c380d888555460f7a5 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 10:27:31 +0200 Subject: [PATCH 057/131] mark snn as ilustrative --- docs/extensibility.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/extensibility.md b/docs/extensibility.md index befd61b23..c137ffa1c 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -59,6 +59,7 @@ example below swaps the backend while keeping full compatibility with the rest of the Squidpy graph pipeline: ```python +# Following code is only illustrative and probably not runnable. import numpy as np from scipy.sparse import csr_matrix from snnpy import build_snn_model From 0afbe3c9ef665fc99b6d0ef3b7dcad9e5b97d1ff Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 10:56:02 +0200 Subject: [PATCH 058/131] add uns params --- src/squidpy/gr/_build.py | 15 ++++++++------- src/squidpy/gr/neighbors.py | 38 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 37bab7544..4ef266f8d 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -381,7 +381,12 @@ def spatial_neighbors( ) return _run_spatial_neighbors( - adata, builder, spatial_key=spatial_key, library_key=library_key, key_added=key_added, copy=copy + adata, + builder, + spatial_key=spatial_key, + library_key=library_key, + key_added=key_added, + copy=copy, ) @@ -549,10 +554,10 @@ def spatial_neighbors_radius( data: AnnData | SpatialData, *, spatial_key: str = Key.obsm.spatial, + radius: float | tuple[float, float], elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, library_key: str | None = None, - radius: float | tuple[float, float] = 1.0, percentile: float | None = None, transform: str | Transform | None = None, set_diag: bool = False, @@ -810,11 +815,7 @@ def _run_spatial_neighbors( neighbors_dict = { "connectivities_key": conns_key, "distances_key": dists_key, - "params": { - "n_neighbors": getattr(builder, "n_neighs", 6), - "radius": getattr(builder, "radius", None), - "transform": builder.transform.v, - }, + "params": builder.uns_params(), } if copy: diff --git a/src/squidpy/gr/neighbors.py b/src/squidpy/gr/neighbors.py index 12165455c..c09b9bf9b 100644 --- a/src/squidpy/gr/neighbors.py +++ b/src/squidpy/gr/neighbors.py @@ -10,7 +10,7 @@ from collections.abc import Callable, Sequence from dataclasses import dataclass from itertools import chain -from typing import Generic, TypeVar, cast +from typing import Any, Generic, TypeVar, cast import numpy as np from fast_array_utils import stats as fau_stats @@ -27,7 +27,7 @@ from sklearn.metrics.pairwise import cosine_similarity, euclidean_distances from sklearn.neighbors import NearestNeighbors -from squidpy._constants._constants import Transform +from squidpy._constants._constants import CoordType, Transform from squidpy._utils import NDArrayA from squidpy._validators import assert_positive @@ -86,6 +86,10 @@ def postprocessors(self) -> Sequence[GraphPostprocessor[GraphMatrixT]]: """Return post-build processing steps for ``(adj, dst)``.""" return self._postprocessors + @abstractmethod + def uns_params(self) -> dict[str, Any]: + """Parameters stored in :attr:`anndata.AnnData.uns` after graph construction.""" + def combine( self, mats: Sequence[tuple[GraphMatrixT, GraphMatrixT]], @@ -166,6 +170,13 @@ def __init__( ) self.n_neighs = n_neighs + def uns_params(self) -> dict[str, Any]: + return { + "coord_type": CoordType.GENERIC.v, + "n_neighbors": self.n_neighs, + "transform": self.transform.v, + } + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tree = NearestNeighbors(n_neighbors=self.n_neighs, radius=1, metric="euclidean") @@ -216,6 +227,13 @@ def __init__( ) self.radius = radius + def uns_params(self) -> dict[str, Any]: + return { + "coord_type": CoordType.GENERIC.v, + "radius": self.radius, + "transform": self.transform.v, + } + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] r = self.radius if isinstance(self.radius, int | float) else max(self.radius) @@ -278,6 +296,13 @@ def __init__( ) self.radius = radius + def uns_params(self) -> dict[str, Any]: + return { + "coord_type": CoordType.GENERIC.v, + "radius": self.radius, + "transform": self.transform.v, + } + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: N = coords.shape[0] tri = Delaunay(coords) @@ -326,6 +351,15 @@ def __init__( self.n_rings = n_rings self.delaunay = delaunay + def uns_params(self) -> dict[str, Any]: + return { + "coord_type": CoordType.GRID.v, + "n_neighbors": self.n_neighs, + "n_rings": self.n_rings, + "delaunay": self.delaunay, + "transform": self.transform.v, + } + def build_graph(self, coords: NDArrayA) -> tuple[csr_matrix, csr_matrix]: if self.n_rings > 1: adj = self._base_adjacency(coords, set_diag=True) From 1711a62326bc9b94056f2bac66701b013b152a77 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 11:09:08 +0200 Subject: [PATCH 059/131] deprecation warnings alignment --- src/squidpy/gr/_build.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 4ef266f8d..5b4adc5c1 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -102,10 +102,10 @@ def _resolve_graph_builder( ) return GridBuilder(n_neighs=n_neighs, **common, n_rings=n_rings, delaunay=delaunay) if delaunay: - # TODO: below check should be removed once legacy mode spatial_neighbors is deprecated + # TODO: below check should be removed when spatial_neighbors is deprecated if n_neighs_was_set: warnings.warn( - "Parameter `n_neighs` is ignored when `delaunay=True` and will be removed in squidpy v2.0.0.", + "Parameter `n_neighs` is ignored when `delaunay=True` use `spatial_neighbors_delaunay` instead.", FutureWarning, stacklevel=3, ) @@ -117,10 +117,10 @@ def _resolve_graph_builder( legacy_radius = radius if isinstance(radius, tuple) else None return DelaunayBuilder(**common, radius=legacy_radius, percentile=percentile) if radius is not None: - # TODO: below check should be removed once legacy mode spatial_neighbors is deprecated + # TODO: below check should be removed when spatial_neighbors is deprecated if n_neighs_was_set: warnings.warn( - "Parameter `n_neighs` is ignored when `radius` is set and will be removed in squidpy v2.0.0.", + "Parameter `n_neighs` is ignored when `radius` is set use `spatial_neighbors_radius` instead.", FutureWarning, stacklevel=3, ) @@ -213,9 +213,9 @@ def spatial_neighbors( """ Create a graph from spatial coordinates. - .. deprecated:: 1.6.0 + .. deprecated:: 1.7.0 ``spatial_neighbors`` is deprecated and will be removed in squidpy - v1.7.0. Use one of the mode-specific functions instead: + v1.9.0. Use one of the mode-specific functions instead: - :func:`spatial_neighbors_knn` - :func:`spatial_neighbors_radius` @@ -294,14 +294,14 @@ def spatial_neighbors( - Generic radius mode: ``coord_type='generic'``, ``delaunay=False``, ``radius`` set. Uses ``radius`` and builds a radius-based neighbor graph. - ``n_neighs`` is ignored and passing it is deprecated. + ``n_neighs`` is ignored and will throw a warning if passed. If ``radius`` is a tuple, the graph is built with the maximum radius and then pruned to the interval ``[min(radius), max(radius)]``. - Generic Delaunay mode: ``coord_type='generic'``, ``delaunay=True``. Builds a Delaunay triangulation graph. ``n_neighs`` is - ignored by the triangulation and passing it is deprecated. + ignored by the triangulation and will throw a warning if passed. If ``radius`` is a tuple, it is used only as a post-construction pruning interval. @@ -321,11 +321,11 @@ def spatial_neighbors( - If ``coord_type`` resolves to ``'grid'``, grid mode is used. In that case ``radius`` is ignored. - Otherwise, if ``delaunay=True``, Delaunay mode is used. - ``n_neighs`` is ignored (deprecated). + ``n_neighs`` is ignored (deprecated, removed in v1.9.0). A tuple ``radius`` is only used afterward as a pruning interval. A scalar ``radius`` is ignored. - Otherwise, if ``radius`` is set, radius mode is used. - In this mode ``n_neighs`` is ignored (deprecated). + In this mode ``n_neighs`` is ignored (deprecated, removed in v1.9.0). - Otherwise, k-nearest-neighbor mode is used. Grid-specific behavior @@ -355,7 +355,7 @@ def spatial_neighbors( """ warnings.warn( "Calling `spatial_neighbors` is deprecated and will be removed in squidpy " - "v1.7.0. Use `spatial_neighbors_knn`, `spatial_neighbors_radius`, " + "v1.9.0. Use `spatial_neighbors_knn`, `spatial_neighbors_radius`, " "`spatial_neighbors_delaunay`, `spatial_neighbors_grid`, or " "`spatial_neighbors_from_builder` instead.", FutureWarning, From 4632b74e92bb05ff327b5a039887c06be10c96c6 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 11:16:50 +0200 Subject: [PATCH 060/131] set diag isnt defaulted to none --- src/squidpy/gr/_build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 5b4adc5c1..8ceeb248b 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -206,7 +206,7 @@ def spatial_neighbors( n_rings: int | None = None, percentile: float | None = None, transform: str | Transform | None = None, - set_diag: bool | None = None, + set_diag: bool = False, key_added: str = "spatial", copy: bool = False, ) -> SpatialNeighborsResult | None: @@ -259,7 +259,6 @@ def spatial_neighbors( - :class:`tuple` - prune the final graph to only contain edges in interval `[min(radius), max(radius)]`. delaunay Whether to compute the graph from Delaunay triangulation. Only used when ``coord_type = {c.GENERIC.s!r}``. - Defaults to ``False``. n_rings Number of rings of neighbors for grid data. Only used when ``coord_type = {c.GRID.s!r}``. Defaults to ``1``. From f4805c19beb0c3388f5536d57b11e02a2ccc1fb9 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 11:17:55 +0200 Subject: [PATCH 061/131] fix --- src/squidpy/gr/_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 8ceeb248b..abc389dc6 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -259,6 +259,7 @@ def spatial_neighbors( - :class:`tuple` - prune the final graph to only contain edges in interval `[min(radius), max(radius)]`. delaunay Whether to compute the graph from Delaunay triangulation. Only used when ``coord_type = {c.GENERIC.s!r}``. + Defaults to ``False``. n_rings Number of rings of neighbors for grid data. Only used when ``coord_type = {c.GRID.s!r}``. Defaults to ``1``. @@ -273,7 +274,6 @@ def spatial_neighbors( - `{t.NONE.v}` - no transformation of the adjacency matrix. set_diag Whether to set the diagonal of the spatial connectivities to `1.0`. - Defaults to ``False``. key_added Key which controls where the results are saved if ``copy = False``. %(copy)s From f2b0fa055f7d82502d461649672c78134611df90 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 11:20:12 +0200 Subject: [PATCH 062/131] radius is also positional --- src/squidpy/gr/_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index abc389dc6..f64a1f115 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -551,9 +551,9 @@ def spatial_neighbors_knn( @d.dedent def spatial_neighbors_radius( data: AnnData | SpatialData, + radius: float | tuple[float, float], *, spatial_key: str = Key.obsm.spatial, - radius: float | tuple[float, float], elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, library_key: str | None = None, From 0ed1604669a863ead9e3c400eec961c40af8cc54 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 21 May 2026 11:25:44 +0200 Subject: [PATCH 063/131] n_neighs on grid mode --- src/squidpy/gr/_build.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index f64a1f115..92a374c2e 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -698,7 +698,7 @@ def spatial_neighbors_grid( elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, library_key: str | None = None, - n_neighs: Literal[4, 6] = 6, + n_neighs: int = 6, n_rings: int = 1, delaunay: bool = False, transform: str | Transform | None = None, @@ -722,10 +722,7 @@ def spatial_neighbors_grid( %(library_key)s n_neighs Number of neighboring tiles used to form the base grid connectivity. - Defaults to ``6``. Use ``4`` for a square grid or ``6`` for a hexagonal - grid such as Visium; other values are accepted at runtime but their - geometric interpretation on a regular grid is not part of the public - API. + Defaults to ``6`` for Visium-like grid coordinates. n_rings Number of rings of neighbors. Defaults to ``1``. ``n_rings=1`` keeps only immediate neighbors; larger values add progressively more distant From c85ba43914623854ca5349a87f9d4c82574f82e4 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Tue, 26 May 2026 15:15:30 +0200 Subject: [PATCH 064/131] make radius kw only --- src/squidpy/gr/_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 92a374c2e..54c8c3a0c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -551,8 +551,8 @@ def spatial_neighbors_knn( @d.dedent def spatial_neighbors_radius( data: AnnData | SpatialData, - radius: float | tuple[float, float], *, + radius: float | tuple[float, float], spatial_key: str = Key.obsm.spatial, elements_to_coordinate_systems: dict[str, str] | None = None, table_key: str | None = None, From d9264a8503d4253bf61bc66bf5ebee31cfbb8a6c Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Wed, 27 May 2026 11:17:13 +0200 Subject: [PATCH 065/131] put a working example --- docs/extensibility.md | 73 +++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/docs/extensibility.md b/docs/extensibility.md index c137ffa1c..5b06b3d38 100644 --- a/docs/extensibility.md +++ b/docs/extensibility.md @@ -50,50 +50,61 @@ with matching sparsity structure: - By convention, ``dst`` should have a zero diagonal, and ``adj`` should only have a non-zero diagonal when ``set_diag=True``. -### Example: fast radius search with SNN +### Example: approximate kNN search with pynndescent -The built-in {class}`~squidpy.gr.neighbors.RadiusBuilder` uses scikit-learn's -``NearestNeighbors``. The [snnpy](https://github.com/nla-group/snn) library -provides a faster exact fixed-radius search based on PCA-based pruning. The -example below swaps the backend while keeping full compatibility with the rest -of the Squidpy graph pipeline: +The built-in {class}`~squidpy.gr.neighbors.KNNBuilder` uses scikit-learn's +``NearestNeighbors``. The [pynndescent](https://github.com/lmcinnes/pynndescent) +library provides an approximate nearest-neighbor search backend that is often +faster on larger datasets. The example below swaps the backend while keeping +the Squidpy graph pipeline contract intact. + +To run the example, install the optional backend into the same environment as +Squidpy: + +```bash +python -m pip install pynndescent +``` ```python -# Following code is only illustrative and probably not runnable. +# Following code is illustrative and requires ``pynndescent`` to be installed. import numpy as np from scipy.sparse import csr_matrix -from snnpy import build_snn_model +from pynndescent import NNDescent from squidpy.gr.neighbors import GraphBuilderCSR -class SNNRadiusBuilder(GraphBuilderCSR): - """Radius graph using the SNN fixed-radius search backend.""" +class PynndescentKNNBuilder(GraphBuilderCSR): + """KNN graph using the pynndescent approximate nearest-neighbor backend.""" + + def __init__(self, n_neighs: int = 6, **kwargs): + super().__init__(**kwargs) + self.n_neighs = n_neighs - def __init__(self, radius: float, **kwargs): - super().__init__(**kwargs) - self.radius = radius + def uns_params(self): + return { + "n_neighbors": self.n_neighs, + "set_diag": self.set_diag, + } - def build_graph(self, coords): - N = coords.shape[0] - model = build_snn_model(coords, verbose=0) - indices, dists = model.batch_query_radius( - coords, self.radius, return_distance=True, - ) + def build_graph(self, coords): + n_obs = coords.shape[0] + model = NNDescent(coords, metric="euclidean") + indices, dists = model.query(coords, k=self.n_neighs) - row = np.repeat(np.arange(N), [len(idx) for idx in indices]) - col = np.concatenate(indices) - d = np.concatenate(dists).astype(np.float64) + row_indices = np.repeat(np.arange(n_obs), self.n_neighs) + col_indices = indices.reshape(-1) + dists = dists.reshape(-1).astype(np.float64) - adj = csr_matrix( - (np.ones(len(row), dtype=np.float32), (row, col)), - shape=(N, N), - ) - dst = csr_matrix((d, (row, col)), shape=(N, N)) + adj = csr_matrix( + (np.ones_like(row_indices, dtype=np.float32), (row_indices, col_indices)), + shape=(n_obs, n_obs), + ) + dst = csr_matrix((dists, (row_indices, col_indices)), shape=(n_obs, n_obs)) - adj.setdiag(1.0 if self.set_diag else adj.diagonal()) - dst.setdiag(0.0) - return adj, dst + adj.setdiag(1.0 if self.set_diag else adj.diagonal()) + dst.setdiag(0.0) + return adj, dst ``` Use it like any other builder: @@ -101,5 +112,5 @@ Use it like any other builder: ```python import squidpy as sq -sq.gr.spatial_neighbors_from_builder(adata, SNNRadiusBuilder(radius=100.0)) +sq.gr.spatial_neighbors_from_builder(adata, PynndescentKNNBuilder(n_neighs=6)) ``` From 52b4e79d0e0faf39e07998b09abdd7d8f434b282 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Wed, 27 May 2026 11:25:20 +0200 Subject: [PATCH 066/131] fix api.md --- docs/api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index e30c23d89..af9a90e7b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -148,4 +148,5 @@ See the {doc}`extensibility guide ` for how to implement a custo :toctree: api experimental.tl.calculate_tiling_qc - experimental.pl.tiling_qc \ No newline at end of file + experimental.pl.tiling_qc +``` \ No newline at end of file From bfbb3e8a263751bac4c69c52873417c8f253f90e Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 28 May 2026 14:45:53 +0200 Subject: [PATCH 067/131] be more compatible with main --- src/squidpy/gr/_build.py | 108 +++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 54c8c3a0c..819d9a09b 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -128,67 +128,63 @@ def _resolve_graph_builder( return KNNBuilder(n_neighs=n_neighs, **common, percentile=percentile) -def _resolve_spatial_data( + + + +def _resolve_data( data: AnnData | SpatialData, - *, - spatial_key: str, elements_to_coordinate_systems: dict[str, str] | None, - table_key: str | None, - library_key: str | None, + table_key: str | None = None, + spatial_key: str = Key.obsm.spatial, + library_key: str | None = None, ) -> tuple[AnnData, str | None]: - """Resolve SpatialData to AnnData, returning (adata, library_key).""" - if isinstance(data, SpatialData): - sdata = data - assert elements_to_coordinate_systems is not None, ( - "Since `data` is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." - ) - assert table_key is not None, ( - "Since `data` is a :class:`spatialdata.SpatialData`, `table_key` must not be `None`." - ) - elements, table = match_element_to_table(sdata, list(elements_to_coordinate_systems), table_key) - assert table.obs_names.equals(sdata.tables[table_key].obs_names), ( - "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." + if not isinstance(data, SpatialData): + return data, library_key + + assert elements_to_coordinate_systems is not None, ( + "Since input is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." + ) + table = extract_adata_if_sdata(data, table_key=table_key) + elements, matched_table = match_element_to_table(data, list(elements_to_coordinate_systems), table_key) + assert matched_table.obs_names.equals(table.obs_names), ( + "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." + ) + regions, region_key, instance_key = get_table_keys(table) + regions = [regions] if isinstance(regions, str) else regions + ordered_regions_in_table = table.obs[region_key].unique() + + # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 + remove_centroids = {} + elem_instances = [] + for e in regions: + schema = get_model(elements[e]) + element_instances = get_element_instances(elements[e]).to_series() + if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): + element_instances = element_instances.drop(index=0) + remove_centroids[e] = True + else: + remove_centroids[e] = False + elem_instances.append(element_instances) + + element_instances = pd.concat(elem_instances) + if (not np.all(element_instances.values == table.obs[instance_key].values)) or ( + not np.all(ordered_regions_in_table == regions) + ): + raise ValueError( + "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." ) - regions, region_key, instance_key = get_table_keys(sdata.tables[table_key]) - regions = [regions] if isinstance(regions, str) else regions - ordered_regions_in_table = sdata.tables[table_key].obs[region_key].unique() + centroids = [] + for region_ in ordered_regions_in_table: + cs = elements_to_coordinate_systems[region_] + centroid = get_centroids(data[region_], coordinate_system=cs)[["x", "y"]].compute() # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - remove_centroids = {} - elem_instances = [] - for e in regions: - schema = get_model(elements[e]) - element_instances = get_element_instances(elements[e]).to_series() - if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): - element_instances = element_instances.drop(index=0) - remove_centroids[e] = True - else: - remove_centroids[e] = False - elem_instances.append(element_instances) - - element_instances = pd.concat(elem_instances) - if (not np.all(element_instances.values == sdata.tables[table_key].obs[instance_key].values)) or ( - not np.all(ordered_regions_in_table == regions) - ): - raise ValueError( - "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." - ) - centroids = [] - for region_ in ordered_regions_in_table: - cs = elements_to_coordinate_systems[region_] - centroid = get_centroids(sdata[region_], coordinate_system=cs)[["x", "y"]].compute() - - # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - if remove_centroids[region_]: - centroid = centroid[1:].copy() - centroids.append(centroid) - - sdata.tables[table_key].obsm[spatial_key] = np.concatenate(centroids) - adata = sdata.tables[table_key] - library_key = region_key - else: - adata = data - return adata, library_key + if remove_centroids[region_]: + centroid = centroid[1:].copy() + centroids.append(centroid) + + table.obsm[spatial_key] = np.concatenate(centroids) + return table, region_key @d.dedent @@ -471,7 +467,7 @@ def _prepare_spatial_neighbors_input( library_key: str | None, ) -> tuple[AnnData, str | None]: """Resolve input data and validate the requested spatial basis.""" - adata, library_key = _resolve_spatial_data( + adata, library_key = _resolve_data( data, spatial_key=spatial_key, elements_to_coordinate_systems=elements_to_coordinate_systems, From 9dd330eed4e386c29a88e401890841376dedda67 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 28 May 2026 14:49:09 +0200 Subject: [PATCH 068/131] align more to main --- src/squidpy/gr/_build.py | 113 +++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/src/squidpy/gr/_build.py b/src/squidpy/gr/_build.py index 726be35eb..b3b82f18c 100644 --- a/src/squidpy/gr/_build.py +++ b/src/squidpy/gr/_build.py @@ -131,63 +131,6 @@ def _resolve_graph_builder( - -def _resolve_data( - data: AnnData | SpatialData, - elements_to_coordinate_systems: dict[str, str] | None, - table_key: str | None = None, - spatial_key: str = Key.obsm.spatial, - library_key: str | None = None, -) -> tuple[AnnData, str | None]: - if not isinstance(data, SpatialData): - return data, library_key - - assert elements_to_coordinate_systems is not None, ( - "Since input is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." - ) - table = extract_adata_if_sdata(data, table_key=table_key) - elements, matched_table = match_element_to_table(data, list(elements_to_coordinate_systems), table_key) - assert matched_table.obs_names.equals(table.obs_names), ( - "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." - ) - regions, region_key, instance_key = get_table_keys(table) - regions = [regions] if isinstance(regions, str) else regions - ordered_regions_in_table = table.obs[region_key].unique() - - # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - remove_centroids = {} - elem_instances = [] - for e in regions: - schema = get_model(elements[e]) - element_instances = get_element_instances(elements[e]).to_series() - if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): - element_instances = element_instances.drop(index=0) - remove_centroids[e] = True - else: - remove_centroids[e] = False - elem_instances.append(element_instances) - - element_instances = pd.concat(elem_instances) - if (not np.all(element_instances.values == table.obs[instance_key].values)) or ( - not np.all(ordered_regions_in_table == regions) - ): - raise ValueError( - "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." - ) - centroids = [] - for region_ in ordered_regions_in_table: - cs = elements_to_coordinate_systems[region_] - centroid = get_centroids(data[region_], coordinate_system=cs)[["x", "y"]].compute() - - # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 - if remove_centroids[region_]: - centroid = centroid[1:].copy() - centroids.append(centroid) - - table.obsm[spatial_key] = np.concatenate(centroids) - return table, region_key - - @d.dedent @inject_docs(t=Transform, c=CoordType) def spatial_neighbors( @@ -386,6 +329,62 @@ def spatial_neighbors( ) +def _resolve_data( + data: AnnData | SpatialData, + elements_to_coordinate_systems: dict[str, str] | None, + table_key: str | None = None, + spatial_key: str = Key.obsm.spatial, + library_key: str | None = None, +) -> tuple[AnnData, str | None]: + if not isinstance(data, SpatialData): + return data, library_key + + assert elements_to_coordinate_systems is not None, ( + "Since input is a :class:`spatialdata.SpatialData`, `elements_to_coordinate_systems` must not be `None`." + ) + table = extract_adata_if_sdata(data, table_key=table_key) + elements, matched_table = match_element_to_table(data, list(elements_to_coordinate_systems), table_key) + assert matched_table.obs_names.equals(table.obs_names), ( + "The spatialdata table must annotate all elements keys. Some elements are missing, please check the `elements_to_coordinate_systems` dictionary." + ) + regions, region_key, instance_key = get_table_keys(table) + regions = [regions] if isinstance(regions, str) else regions + ordered_regions_in_table = table.obs[region_key].unique() + + # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 + remove_centroids = {} + elem_instances = [] + for e in regions: + schema = get_model(elements[e]) + element_instances = get_element_instances(elements[e]).to_series() + if np.isin(0, element_instances.values) and (schema in (Labels2DModel, Labels3DModel)): + element_instances = element_instances.drop(index=0) + remove_centroids[e] = True + else: + remove_centroids[e] = False + elem_instances.append(element_instances) + + element_instances = pd.concat(elem_instances) + if (not np.all(element_instances.values == table.obs[instance_key].values)) or ( + not np.all(ordered_regions_in_table == regions) + ): + raise ValueError( + "The spatialdata table must annotate all elements keys. Some elements are missing or not ordered correctly, please check the `elements_to_coordinate_systems` dictionary." + ) + centroids = [] + for region_ in ordered_regions_in_table: + cs = elements_to_coordinate_systems[region_] + centroid = get_centroids(data[region_], coordinate_system=cs)[["x", "y"]].compute() + + # TODO: remove this after https://github.com/scverse/spatialdata/issues/614 + if remove_centroids[region_]: + centroid = centroid[1:].copy() + centroids.append(centroid) + + table.obsm[spatial_key] = np.concatenate(centroids) + return table, region_key + + @d.dedent def spatial_neighbors_from_builder( data: AnnData | SpatialData, From 6c61ddce64de4b971d3673c90c27a83b2e44bdff Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 09:21:34 +0200 Subject: [PATCH 069/131] save state --- hatch.toml | 4 + pyproject.toml | 3 + src/squidpy/experimental/__init__.py | 4 +- src/squidpy/experimental/_fit/__init__.py | 46 +++ src/squidpy/experimental/_fit/_estimator.py | 58 +++ .../experimental/_fit/_methods/__init__.py | 12 + .../_fit/_methods/_stalign/_core.py | 371 ++++++++++++++++++ .../_fit/_methods/_stalign/_estimator.py | 130 ++++++ .../_fit/_methods/_stalign/_helpers.py | 160 ++++++++ .../_fit/_methods/_stalign/_tools.py | 231 +++++++++++ src/squidpy/experimental/_fit/_registry.py | 46 +++ src/squidpy/experimental/_fit/_result.py | 32 ++ tests/experimental/fit/__init__.py | 0 tests/experimental/fit/test_core.py | 95 +++++ tests/experimental/fit/test_stalign.py | 95 +++++ 15 files changed, 1285 insertions(+), 2 deletions(-) create mode 100644 src/squidpy/experimental/_fit/__init__.py create mode 100644 src/squidpy/experimental/_fit/_estimator.py create mode 100644 src/squidpy/experimental/_fit/_methods/__init__.py create mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/_core.py create mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py create mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py create mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/_tools.py create mode 100644 src/squidpy/experimental/_fit/_registry.py create mode 100644 src/squidpy/experimental/_fit/_result.py create mode 100644 tests/experimental/fit/__init__.py create mode 100644 tests/experimental/fit/test_core.py create mode 100644 tests/experimental/fit/test_stalign.py diff --git a/hatch.toml b/hatch.toml index 065fe2dfb..f2672cd35 100644 --- a/hatch.toml +++ b/hatch.toml @@ -17,10 +17,14 @@ extra-dependencies = ["diff-cover"] matrix = [ { deps = ["stable"], python = ["3.11", "3.12", "3.13"] }, { deps = ["pre"], python = ["3.13"] }, + { deps = ["stable"], python = ["3.13"], extras = ["jax"] }, ] overrides.matrix.deps.env-vars = [ { key = "UV_PRERELEASE", value = "allow", if = ["pre"] }, ] +overrides.matrix.extras.features = [ + { value = "jax", if = ["jax"] }, +] # default commands (only `cov-report` is overridden) scripts.run = "pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}" scripts.run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}" diff --git a/pyproject.toml b/pyproject.toml index 06ffac1bf..208f6ee10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,9 @@ dependencies = [ "xarray>=2024.10", "zarr>=3", ] +optional-dependencies.jax = [ + "jax", +] optional-dependencies.leiden = [ "leidenalg", "spatialleiden>=0.4", diff --git a/src/squidpy/experimental/__init__.py b/src/squidpy/experimental/__init__.py index 3c5b1f537..a33215238 100644 --- a/src/squidpy/experimental/__init__.py +++ b/src/squidpy/experimental/__init__.py @@ -6,6 +6,6 @@ from __future__ import annotations -from . import im, pl, tl, utils +from . import fit, im, pl, tl, utils -__all__ = ["im", "pl", "tl", "utils"] +__all__ = ["fit", "im", "pl", "tl", "utils"] diff --git a/src/squidpy/experimental/_fit/__init__.py b/src/squidpy/experimental/_fit/__init__.py new file mode 100644 index 000000000..b56d9df80 --- /dev/null +++ b/src/squidpy/experimental/_fit/__init__.py @@ -0,0 +1,46 @@ +"""In-memory model-fitting core for experimental squidpy tools. + +This subpackage factors the *fit* half of fitting-style tools (alignment, +imputation, ...) out of any particular container or write-back path: + +- :class:`Estimator` -- a model whose :meth:`~Estimator.fit` takes in-memory + data and returns a :class:`FitResult` (scikit-learn-style; training is + assumed to fit in memory). +- :class:`FitResult` -- the fitted parameters plus an optional, pure in-memory + :meth:`~FitResult.transform`. Container write-back lives in the calling + function, not here. +- :class:`Registry` -- a flat ``name -> Estimator`` map, one per family. + +Concrete estimators live in :mod:`._methods` and pull their optional +dependencies (e.g. JAX) in lazily, so importing this package stays cheap. + +Example +------- +>>> import numpy as np +>>> from squidpy.experimental.fit import Estimator, FitResult, Registry +>>> demo = Registry("demo") +>>> class Shift(FitResult): +... def __init__(self, delta): +... self.metadata, self.delta = {}, delta +... +... def transform(self, x): +... return np.asarray(x) + self.delta +>>> @demo.register("mean_shift") +... class MeanShift(Estimator): +... name = "mean_shift" +... +... def fit(self, ref, query): +... return Shift(ref.mean(0) - query.mean(0)) +>>> est = demo.get("mean_shift")() +>>> result = est.fit(np.ones((3, 2)), np.zeros((3, 2))) +>>> result.transform(np.zeros((3, 2))).tolist() +[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]] +""" + +from __future__ import annotations + +from squidpy.experimental.fit._estimator import Estimator +from squidpy.experimental.fit._registry import Registry +from squidpy.experimental.fit._result import FitResult + +__all__ = ["Estimator", "FitResult", "Registry"] diff --git a/src/squidpy/experimental/_fit/_estimator.py b/src/squidpy/experimental/_fit/_estimator.py new file mode 100644 index 000000000..c73a3173f --- /dev/null +++ b/src/squidpy/experimental/_fit/_estimator.py @@ -0,0 +1,58 @@ +"""Estimator contract: a pure, in-memory fit.""" + +from __future__ import annotations + +import importlib.util +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, ClassVar + +if TYPE_CHECKING: + from squidpy.experimental.fit._result import FitResult + + +class Estimator(ABC): + """Base class for an in-memory model fit. + + Subclasses implement :meth:`fit`, which takes already-extracted in-memory + data (NumPy arrays, sparse matrices, or an :class:`~anndata.AnnData`) and + returns a :class:`FitResult`. Following scikit-learn, ``fit`` assumes the + training data fits in memory -- there is no streaming / out-of-core + contract (scikit-learn's ``partial_fit`` is the analogous opt-in exception + and is intentionally not modelled here). + + Reading data out of a container and writing results back are *not* part of + this contract; both are handled by the calling function. + + Attributes + ---------- + name + Stable identifier used as the registry key. + requires + Optional third-party packages the estimator needs at fit time (e.g. + ``("jax",)``). Advertised here so :meth:`check_requirements` can fail + early with an actionable message before any heavy import fires. + """ + + name: ClassVar[str] + requires: ClassVar[tuple[str, ...]] = () + + @abstractmethod + def fit(self, *args: Any, **kwargs: Any) -> FitResult: + """Fit the model on in-memory data and return a :class:`FitResult`.""" + + def check_requirements(self) -> None: + """Raise :class:`ImportError` if any package in :attr:`requires` is missing. + + Estimators should call this at the top of :meth:`fit`, before importing + their optional backend, so callers get a clean message instead of a + deep ``ModuleNotFoundError``. + """ + missing = [pkg for pkg in self.requires if importlib.util.find_spec(pkg) is None] + if missing: + verb = "is" if len(missing) == 1 else "are" + names = ", ".join(repr(p) for p in missing) + extras = ",".join(missing) + raise ImportError( + f"Estimator {self.name!r} requires {names}, which {verb} not installed. " + f'Install with `pip install "squidpy[{extras}]"`.' + ) diff --git a/src/squidpy/experimental/_fit/_methods/__init__.py b/src/squidpy/experimental/_fit/_methods/__init__.py new file mode 100644 index 000000000..8b15faa6b --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/__init__.py @@ -0,0 +1,12 @@ +"""Concrete estimators for :mod:`squidpy.experimental.fit`. + +Importing this package registers the bundled estimators (e.g. STalign in the +``align`` family) but stays cheap: optional dependencies such as JAX are pulled +in lazily, only when an estimator's ``fit`` is actually called. +""" + +from __future__ import annotations + +from squidpy.experimental.fit._methods._stalign import ALIGN, StalignEstimator, StalignFitResult + +__all__ = ["ALIGN", "StalignEstimator", "StalignFitResult"] diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_core.py b/src/squidpy/experimental/_fit/_methods/_stalign/_core.py new file mode 100644 index 000000000..8ac139b6c --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_core.py @@ -0,0 +1,371 @@ +"""Core JAX implementation for experimental STalign point registration. + +Lifted byte-for-byte from scverse/squidpy#1150 (Selman Özleyen). +""" + +from __future__ import annotations + +from typing import Any, Literal + +import jax +import jax.numpy as jnp +import jax.scipy as jsp +import numpy as np + +__all__ = ["jax_dtype", "lddmm", "transform_points_row_col"] + + +def jax_dtype() -> jnp.dtype: + """Resolve the active JAX float dtype at call time, not import time.""" + return jnp.float64 if jax.config.x64_enabled else jnp.float32 + + +def _to_affine(linear: Any, translation: Any) -> Any: + return jnp.array( + [ + [linear[0, 0], linear[0, 1], translation[0]], + [linear[1, 0], linear[1, 1], translation[1]], + [0.0, 0.0, 1.0], + ], + dtype=linear.dtype, + ) + + +def _grid_points(x: tuple[Any, Any]) -> Any: + yy, xx = jnp.meshgrid(x[0], x[1], indexing="ij") + return jnp.stack((yy, xx)) + + +def _interp( + x: tuple[Any, Any], + image: Any, + phii: Any, + *, + mode: str = "nearest", +) -> Any: + """Interpolate a channels-first image on physical row-column coordinates.""" + arr = jnp.asarray(image) + coords = jnp.asarray(phii) + if coords.shape[0] != 2: + raise ValueError(f"Expected interpolation coordinates to have leading axis of size 2, found `{coords.shape}`.") + if arr.ndim == 2: + arr = arr[None, ...] + + row_step = x[0][1] - x[0][0] + col_step = x[1][1] - x[1][0] + row_idx = (coords[0] - x[0][0]) / row_step + col_idx = (coords[1] - x[1][0]) / col_step + idx = jnp.stack((row_idx.reshape(-1), col_idx.reshape(-1))) + + def _sample(channel: Any) -> Any: + values = jsp.ndimage.map_coordinates(channel, idx, order=1, mode=mode) + return values.reshape(coords.shape[1:]) + + return jax.vmap(_sample)(arr) + + +def transform_points_row_col( + xv: tuple[Any, Any], + velocity: Any, + affine: Any, + points: Any, + *, + direction: Literal["forward", "backward"] = "forward", +) -> Any: + pts = jnp.asarray(points) + n_steps = velocity.shape[0] + time_steps = range(n_steps) + flow_sign = 1.0 + if direction == "backward": + affine = jnp.linalg.inv(affine) + pts = pts @ affine[:2, :2].T + affine[:2, -1] + flow_sign = -1.0 + time_steps = reversed(time_steps) + + for t in time_steps: + disp = _interp( + xv, + jnp.moveaxis(flow_sign * velocity[t], -1, 0), + pts.T[:, :, None], + mode="nearest", + )[:, :, 0].T + pts = pts + disp / n_steps + + if direction == "forward": + pts = pts @ affine[:2, :2].T + affine[:2, -1] + + return pts + + +def _transform_grid_backward( + x_target: tuple[Any, Any], + xv: tuple[Any, Any], + velocity: Any, + affine: Any, +) -> Any: + target_grid = _grid_points(x_target) + affine_inv = jnp.linalg.inv(affine) + source_grid = jnp.einsum("ij,jhw->ihw", affine_inv[:2, :2], target_grid) + affine_inv[:2, -1][:, None, None] + + for t in range(velocity.shape[0] - 1, -1, -1): + disp = _interp(xv, jnp.moveaxis(-velocity[t], -1, 0), source_grid, mode="nearest") + source_grid = source_grid + disp / velocity.shape[0] + + return source_grid + + +def _contrast_transform(source_image: Any, target_image: Any, weights: Any) -> Any: + flat_source = source_image.reshape(source_image.shape[0], -1) + flat_target = target_image.reshape(target_image.shape[0], -1) + flat_weights = weights.reshape(-1) + + design = jnp.concatenate((jnp.ones((1, flat_source.shape[1]), dtype=source_image.dtype), flat_source), axis=0) + weighted_design = design * flat_weights[None, :] + design_cov = weighted_design @ design.T + target_cov = weighted_design @ flat_target.T + regularized = design_cov + 0.1 * jnp.eye(design_cov.shape[0], dtype=design_cov.dtype) + coefficients = jnp.linalg.solve(regularized, target_cov) + return (coefficients.T @ design).reshape(target_image.shape) + + +def _build_velocity_grid(x_source: tuple[Any, Any], *, a: float, expand: float) -> tuple[Any, Any]: + minimum = jnp.array([x_source[0][0], x_source[1][0]]) + maximum = jnp.array([x_source[0][-1], x_source[1][-1]]) + center = (minimum + maximum) / 2.0 + half_width = (maximum - minimum) * expand / 2.0 + step = a * 0.5 + return ( + jnp.arange(center[0] - half_width[0], center[0] + half_width[0] + step, step), + jnp.arange(center[1] - half_width[1], center[1] + half_width[1] + step, step), + ) + + +def _build_regularizer( + xv: tuple[Any, Any], + *, + a: float, + p: float, +) -> tuple[Any, Any, Any]: + dv = jnp.array([xv[0][1] - xv[0][0], xv[1][1] - xv[1][0]]) + shape = (xv[0].shape[0], xv[1].shape[0]) + fy = jnp.arange(shape[0], dtype=xv[0].dtype) / (shape[0] * dv[0]) + fx = jnp.arange(shape[1], dtype=xv[1].dtype) / (shape[1] * dv[1]) + frequency_grid = jnp.stack(jnp.meshgrid(fy, fx, indexing="ij"), axis=-1) + ll = (1.0 + 2.0 * a**2 * jnp.sum((1.0 - jnp.cos(2.0 * np.pi * frequency_grid * dv)) / (dv**2), axis=-1)) ** ( + 2.0 * p + ) + kernel = 1.0 / ll + dv_prod = jnp.prod(dv) + return kernel, ll, dv_prod + + +def _update_mixture_weights( + transformed_source: Any, + target_image: Any, + match_weights: Any, + artifact_weights: Any, + background_weights: Any, + *, + sigmaM: float, + sigmaA: float, + sigmaB: float, + estimate_muA: bool, + estimate_muB: bool, + muA: Any, + muB: Any, + iteration: int, +) -> tuple[Any, Any, Any, Any, Any]: + if estimate_muA: + muA = jnp.sum(artifact_weights * target_image, axis=(-1, -2)) / jnp.maximum(jnp.sum(artifact_weights), 1e-12) + if estimate_muB: + muB = jnp.sum(background_weights * target_image, axis=(-1, -2)) / jnp.maximum( + jnp.sum(background_weights), 1e-12 + ) + + if iteration < 50: + return match_weights, artifact_weights, background_weights, muA, muB + + weights = jnp.stack((match_weights, artifact_weights, background_weights)) + mixing = jnp.sum(weights, axis=(1, 2)) + mixing = mixing + jnp.max(mixing) * 1e-6 + mixing = mixing / jnp.sum(mixing) + + n_channels = target_image.shape[0] + norm_match = (2.0 * np.pi * sigmaM**2) ** (n_channels / 2.0) + norm_artifact = (2.0 * np.pi * sigmaA**2) ** (n_channels / 2.0) + norm_background = (2.0 * np.pi * sigmaB**2) ** (n_channels / 2.0) + + match_weights = mixing[0] * jnp.exp(-jnp.sum((transformed_source - target_image) ** 2, axis=0) / (2.0 * sigmaM**2)) + match_weights = match_weights / norm_match + artifact_weights = mixing[1] * jnp.exp( + -jnp.sum((muA[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaA**2) + ) + artifact_weights = artifact_weights / norm_artifact + background_weights = mixing[2] * jnp.exp( + -jnp.sum((muB[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaB**2) + ) + background_weights = background_weights / norm_background + + total = match_weights + artifact_weights + background_weights + total = total + jnp.max(total) * 1e-6 + return match_weights / total, artifact_weights / total, background_weights / total, muA, muB + + +def _lddmm_loss( + linear: Any, + translation: Any, + velocity: Any, + *, + x_source: tuple[Any, Any], + source_image: Any, + x_target: tuple[Any, Any], + target_image: Any, + xv: tuple[Any, Any], + match_weights: Any, + ll: Any, + dv_prod: Any, + points_source: Any, + points_target: Any, + sigmaM: float, + sigmaR: float, + sigmaP: float, +) -> tuple[Any, tuple[Any, Any, Any, Any, Any]]: + affine = _to_affine(linear, translation) + source_grid = _transform_grid_backward(x_target, xv, velocity, affine) + warped_source = _interp(x_source, source_image, source_grid, mode="nearest") + contrast_source = _contrast_transform(warped_source, target_image, match_weights) + + match_energy = jnp.sum((contrast_source - target_image) ** 2 * match_weights) / (2.0 * sigmaM**2) + fft_velocity = jnp.fft.fftn(velocity, axes=(1, 2)) + reg_energy = ( + jnp.sum(jnp.sum(jnp.abs(fft_velocity) ** 2, axis=(0, 3)) * ll) + * dv_prod + / 2.0 + / velocity.shape[1] + / velocity.shape[2] + / sigmaR**2 + ) + + transformed_points = transform_points_row_col(xv, velocity, affine, points_source, direction="forward") + if points_source.shape[0] == 0: + point_energy = jnp.array(0.0, dtype=source_image.dtype) + else: + point_energy = jnp.sum((transformed_points - points_target) ** 2) / (2.0 * sigmaP**2) + + total = match_energy + reg_energy + point_energy + return total, (contrast_source, transformed_points, match_energy, reg_energy, point_energy) + + +def lddmm( + xI: tuple[Any, Any], + I: Any, + xJ: tuple[Any, Any], + J: Any, + *, + L: Any, + T: Any, + points_source: Any | None = None, + points_target: Any | None = None, + a: float = 500.0, + p: float = 2.0, + expand: float = 2.0, + nt: int = 3, + niter: int = 5000, + diffeo_start: int = 0, + epL: float = 2e-8, + epT: float = 2e-1, + epV: float = 2e3, + sigmaM: float = 1.0, + sigmaB: float = 2.0, + sigmaA: float = 5.0, + sigmaR: float = 5e5, + sigmaP: float = 2e1, +) -> dict[str, Any]: + x_source = (jnp.asarray(xI[0]), jnp.asarray(xI[1])) + x_target = (jnp.asarray(xJ[0]), jnp.asarray(xJ[1])) + source_image = jnp.asarray(I, dtype=jax_dtype()) + target_image = jnp.asarray(J, dtype=jax_dtype()) + linear = jnp.asarray(L, dtype=jax_dtype()) + translation = jnp.asarray(T, dtype=jax_dtype()) + + if points_source is None: + source_landmarks = jnp.zeros((0, 2), dtype=jax_dtype()) + target_landmarks = jnp.zeros((0, 2), dtype=jax_dtype()) + else: + source_landmarks = jnp.asarray(points_source, dtype=jax_dtype()) + target_landmarks = jnp.asarray(points_target, dtype=jax_dtype()) + + xv = _build_velocity_grid(x_source, a=a, expand=expand) + velocity = jnp.zeros((nt, xv[0].shape[0], xv[1].shape[0], 2), dtype=jax_dtype()) + kernel, ll, dv_prod = _build_regularizer(xv, a=a, p=p) + + match_weights = jnp.full(target_image.shape[1:], 0.5, dtype=target_image.dtype) + background_weights = jnp.full(target_image.shape[1:], 0.4, dtype=target_image.dtype) + artifact_weights = jnp.full(target_image.shape[1:], 0.1, dtype=target_image.dtype) + muA = jnp.mean(target_image, axis=(1, 2)) + muB = jnp.zeros_like(muA) + estimate_muA = True + estimate_muB = True + + loss_and_grad = jax.jit(jax.value_and_grad(_lddmm_loss, argnums=(0, 1, 2), has_aux=True)) + + for iteration in range(niter): + (energy, aux), (grad_linear, grad_translation, grad_velocity) = loss_and_grad( + linear, + translation, + velocity, + x_source=x_source, + source_image=source_image, + x_target=x_target, + target_image=target_image, + xv=xv, + match_weights=match_weights, + ll=ll, + dv_prod=dv_prod, + points_source=source_landmarks, + points_target=target_landmarks, + sigmaM=sigmaM, + sigmaR=sigmaR, + sigmaP=sigmaP, + ) + contrast_source, transformed_points, _, _, _ = aux + + affine_scale = 1.0 + 9.0 * float(iteration >= diffeo_start) + linear = linear - (epL / affine_scale) * grad_linear + translation = translation - (epT / affine_scale) * grad_translation + + grad_velocity = jnp.fft.ifftn( + jnp.fft.fftn(grad_velocity, axes=(1, 2)) * kernel[None, ..., None], + axes=(1, 2), + ).real + if iteration >= diffeo_start: + velocity = velocity - epV * grad_velocity + + if iteration % 5 == 0: + match_weights, artifact_weights, background_weights, muA, muB = _update_mixture_weights( + contrast_source, + target_image, + match_weights, + artifact_weights, + background_weights, + sigmaM=sigmaM, + sigmaA=sigmaA, + sigmaB=sigmaB, + estimate_muA=estimate_muA, + estimate_muB=estimate_muB, + muA=muA, + muB=muB, + iteration=iteration, + ) + + affine = _to_affine(linear, translation) + return { + "A": affine, + "v": velocity, + "xv": xv, + "WM": match_weights, + "WB": background_weights, + "WA": artifact_weights, + "E": energy, + "points": transformed_points, + } diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py b/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py new file mode 100644 index 000000000..e9cec4735 --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py @@ -0,0 +1,130 @@ +"""STalign estimator: JAX LDDMM point-cloud registration. + +The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see +:mod:`._stalign_core`, :mod:`._stalign_helpers` and :mod:`._stalign_tools`. +This module only adds the thin :class:`Estimator` / :class:`FitResult` adapter +onto the :mod:`squidpy.experimental.fit` core. JAX is imported lazily inside +:meth:`StalignEstimator.fit`, so importing this module is cheap and does not +require JAX. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +import numpy as np + +from squidpy.experimental.fit._estimator import Estimator +from squidpy.experimental.fit._registry import Registry +from squidpy.experimental.fit._result import FitResult + +if TYPE_CHECKING: + from ._stalign_tools import STalignConfig + +#: Registry for the ``align`` family of estimators. +ALIGN = Registry("align") + + +@dataclass +class StalignFitResult(FitResult): + """Fitted STalign deformation, in ``(x, y)`` convention. + + ``deltas`` is the per-point displacement that maps the *query* points into + the *reference* frame; :meth:`transform` bakes it in. STalign produces a + diffeomorphic (non-affine) map, so ``affine`` is ``None`` here -- the full + map (velocity field, velocity grid, affine initialisation) is kept under + ``metadata['stalign_result']`` for power users who need to transform + arbitrary points via ``stalign_result.transform_points``. + """ + + deltas: np.ndarray + affine: np.ndarray | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def transform(self, x: np.ndarray) -> np.ndarray: + """Apply the fitted displacement to the ``(N, 2)`` ``(x, y)`` points it was fit on. + + ``deltas`` is per-point, so ``x`` must have the same shape as the query + cloud passed to :meth:`StalignEstimator.fit`. To map *arbitrary* points, + use ``metadata['stalign_result'].transform_points``. + """ + coords = np.asarray(x, dtype=float) + if coords.shape != self.deltas.shape: + raise ValueError( + f"`transform` expects coordinates of shape {self.deltas.shape} (the fitted query cloud), " + f"found {coords.shape}. Use `metadata['stalign_result'].transform_points` for arbitrary points." + ) + return coords + self.deltas + + +@ALIGN.register("stalign") +class StalignEstimator(Estimator): + """Align a query point cloud onto a reference with a JAX LDDMM solver.""" + + name = "stalign" + requires = ("jax",) + + def fit( + self, + ref: np.ndarray, + query: np.ndarray, + *, + config: STalignConfig | None = None, + landmarks_source: np.ndarray | None = None, + landmarks_target: np.ndarray | None = None, + ) -> StalignFitResult: + """Fit a deformation mapping ``query`` onto ``ref``. + + Parameters + ---------- + ref + ``(N, 2)`` reference point cloud in ``(x, y)`` order. + query + ``(M, 2)`` query point cloud in ``(x, y)`` order, to be aligned to + ``ref``. Both are plain in-memory arrays; extracting them from an + ``AnnData`` / ``SpatialData`` is the caller's responsibility. + config + Optional :class:`STalignConfig` of solver hyperparameters. + landmarks_source, landmarks_target + Optional corresponding ``(x, y)`` landmark arrays used to + initialise the affine. Must be provided together. + + Returns + ------- + A :class:`StalignFitResult` whose ``deltas`` move ``query`` into the + reference frame. + """ + self.check_requirements() + # Import the JAX-backed solver only after requirements pass, so callers + # without JAX get the clean ImportError from check_requirements rather + # than a confusing failure from a module-level `import jax`. + from ._stalign_tools import stalign_points + + ref_xy = np.asarray(ref, dtype=float) + query_xy = np.asarray(query, dtype=float) + for arr_name, arr in (("ref", ref_xy), ("query", query_xy)): + if arr.ndim != 2 or arr.shape[1] != 2: + raise ValueError(f"Expected `{arr_name}` to be an (N, 2) array, found shape {arr.shape}.") + + # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. + src_rc = query_xy[:, [1, 0]] + tgt_rc = ref_xy[:, [1, 0]] + lm_src_rc = None if landmarks_source is None else np.asarray(landmarks_source, dtype=float)[:, [1, 0]] + lm_tgt_rc = None if landmarks_target is None else np.asarray(landmarks_target, dtype=float)[:, [1, 0]] + + result = stalign_points( + source_points=src_rc, + target_points=tgt_rc, + config=config, + landmarks_source=lm_src_rc, + landmarks_target=lm_tgt_rc, + ) + + aligned_xy = np.asarray(result.aligned_points)[:, [1, 0]] + deltas_xy = aligned_xy - query_xy + return StalignFitResult( + deltas=deltas_xy, + affine=None, + metadata={"method": "stalign", "stalign_result": result}, + ) diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py b/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py new file mode 100644 index 000000000..6d3304188 --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py @@ -0,0 +1,160 @@ +"""Numeric helpers for STalign point-cloud registration. + +Lifted from scverse/squidpy#1150 (Selman Özleyen). Container readers +(``extract_points`` / ``extract_landmarks``) were dropped on the move into +:mod:`squidpy.experimental.fit`: reading coordinates out of an ``AnnData`` / +``SpatialData`` is the caller's concern, so everything here operates purely on +in-memory NumPy arrays. +""" + +from __future__ import annotations + +from typing import Literal + +import numpy as np + +PointOrder = Literal["row_col", "xy"] + +__all__ = [ + "PointOrder", + "affine_from_points", + "from_row_col", + "rasterize", + "to_row_col", +] + + +def _validate_points(points: np.ndarray, *, name: str) -> np.ndarray: + arr = np.asarray(points, dtype=float) + if arr.ndim != 2 or arr.shape[1] != 2: + raise ValueError(f"Expected `{name}` to have shape `(n, 2)`, found `{arr.shape}`.") + if not np.all(np.isfinite(arr)): + raise ValueError(f"Expected `{name}` to contain only finite values.") + return arr + + +def to_row_col(points: np.ndarray, *, point_order: PointOrder) -> np.ndarray: + """Convert coordinates to row-column order.""" + arr = _validate_points(points, name="points") + if point_order == "row_col": + return arr + if point_order == "xy": + return arr[:, [1, 0]] + raise ValueError(f"Unknown `point_order`: `{point_order}`.") + + +def from_row_col(points: np.ndarray, *, point_order: PointOrder) -> np.ndarray: + """Convert row-column coordinates to the requested order.""" + arr = _validate_points(points, name="points") + if point_order == "row_col": + return arr + if point_order == "xy": + return arr[:, [1, 0]] + raise ValueError(f"Unknown `point_order`: `{point_order}`.") + + +def _normalize(values: np.ndarray) -> np.ndarray: + values = np.asarray(values, dtype=float) + vmin = np.min(values) + vmax = np.max(values) + if np.isclose(vmin, vmax): + return np.ones_like(values, dtype=float) + return (values - vmin) / (vmax - vmin) + + +def rasterize( + x: np.ndarray, + y: np.ndarray, + *, + g: np.ndarray | None = None, + dx: float = 30.0, + blur: float | list[float] = 1.0, + expand: float = 1.1, +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + """Rasterize a point cloud into a multi-scale density image.""" + x = np.asarray(x, dtype=float) + y = np.asarray(y, dtype=float) + if x.ndim != 1 or y.ndim != 1 or x.shape != y.shape: + raise ValueError("Expected `x` and `y` to be 1D arrays with the same length.") + if x.size == 0: + raise ValueError("Expected at least one point to rasterize.") + if dx <= 0: + raise ValueError("Expected `dx` to be positive.") + if expand <= 0: + raise ValueError("Expected `expand` to be positive.") + + blur_values = np.atleast_1d(np.asarray(blur, dtype=float)) + if blur_values.ndim != 1 or np.any(blur_values <= 0): + raise ValueError("Expected `blur` to be a positive scalar or a 1D sequence of positive values.") + + if g is None: + weights = np.ones_like(x, dtype=float) + else: + weights = np.asarray(g, dtype=float) + if weights.shape != x.shape: + raise ValueError("Expected `g` to have the same shape as `x` and `y`.") + if not np.allclose(weights, 1.0): + weights = _normalize(weights) + + min_x = float(np.min(x)) + max_x = float(np.max(x)) + min_y = float(np.min(y)) + max_y = float(np.max(y)) + + center_x = (min_x + max_x) / 2.0 + center_y = (min_y + max_y) / 2.0 + half_x = (max_x - min_x) * expand / 2.0 + half_y = (max_y - min_y) * expand / 2.0 + + grid_x = np.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=float) + grid_y = np.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=float) + if grid_x.size < 2 or grid_y.size < 2: + raise ValueError("Rasterized grid is too small. Increase the point spread or lower `dx`.") + + mesh_x, mesh_y = np.meshgrid(grid_x, grid_y) + out = np.zeros((len(blur_values), grid_y.size, grid_x.size), dtype=float) + radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) + + for x_i, y_i, w_i in zip(x, y, weights, strict=False): + col = int(np.rint((x_i - grid_x[0]) / dx)) + row = int(np.rint((y_i - grid_y[0]) / dx)) + + row0 = max(row - radius, 0) + row1 = min(row + radius, out.shape[1] - 1) + col0 = max(col - radius, 0) + col1 = min(col + radius, out.shape[2] - 1) + + patch_x = mesh_x[row0 : row1 + 1, col0 : col1 + 1] + patch_y = mesh_y[row0 : row1 + 1, col0 : col1 + 1] + denom = 2.0 * (dx * blur_values * 2.0) ** 2 + + kernels = np.exp(-((patch_x[..., None] - x_i) ** 2 + (patch_y[..., None] - y_i) ** 2) / denom) + kernels_sum = kernels.sum(axis=(0, 1), keepdims=True) + kernels /= np.where(kernels_sum == 0.0, 1.0, kernels_sum) + out[:, row0 : row1 + 1, col0 : col1 + 1] += np.moveaxis(kernels * w_i, -1, 0) + + return grid_x, grid_y, out + + +def affine_from_points( + points_source: np.ndarray, + points_target: np.ndarray, +) -> tuple[np.ndarray, np.ndarray]: + """Compute an affine initialization from corresponding landmarks.""" + source = _validate_points(points_source, name="points_source") + target = _validate_points(points_target, name="points_target") + if source.shape != target.shape: + raise ValueError( + f"Expected `points_source` and `points_target` to have the same shape, found " + f"`{source.shape}` and `{target.shape}`." + ) + + if source.shape[0] < 3: + linear = np.eye(2, dtype=float) + translation = np.mean(target, axis=0) - np.mean(source, axis=0) + return linear, translation + + source_h = np.concatenate((source, np.ones((source.shape[0], 1), dtype=float)), axis=1) + target_h = np.concatenate((target, np.ones((target.shape[0], 1), dtype=float)), axis=1) + affine = np.linalg.lstsq(source_h, target_h, rcond=None)[0].T + return affine[:2, :2], affine[:2, -1] diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py b/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py new file mode 100644 index 000000000..be73201fe --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py @@ -0,0 +1,231 @@ +"""Low-level point-cloud tools for experimental STalign. + +Lifted from scverse/squidpy#1150 (Selman Özleyen) with import paths adjusted +and minor cleanups (config unpacking, lazy dtype resolution). The container +write-back path (``STalignResult.transform_adata``) was dropped on the move +into :mod:`squidpy.experimental.fit`: results stay in memory and the caller +owns write-back. +""" + +from __future__ import annotations + +from dataclasses import asdict, dataclass, field +from typing import TYPE_CHECKING, Any, Literal, TypeAlias + +import jax.numpy as jnp +import numpy as np + +from ._stalign_core import jax_dtype, lddmm, transform_points_row_col +from ._stalign_helpers import ( + PointOrder, + affine_from_points, + from_row_col, + rasterize, + to_row_col, +) + +if TYPE_CHECKING: + import jax + + JaxArray = jax.Array +else: # pragma: no cover - typing only + JaxArray = Any + +BlurScales: TypeAlias = float | tuple[float, ...] | list[float] + +__all__ = [ + "STalignConfig", + "STalignPreprocessConfig", + "STalignPreprocessResult", + "STalignRegistrationConfig", + "STalignResult", + "stalign_points", + "stalign_preprocess", + "transform_points", +] + + +@dataclass(slots=True) +class STalignPreprocessConfig: + dx: float = 30.0 + blur: BlurScales = (2.0, 1.0, 0.5) + expand: float = 1.1 + + +@dataclass(slots=True) +class STalignRegistrationConfig: + """LDDMM registration hyperparameters. + + Field names (``sigmaM``, ``epL``, etc.) preserve the conventions from + the STalign paper and reference implementation to keep them + recognisable when cross-referencing the literature. + """ + + a: float = 500.0 + p: float = 2.0 + expand: float = 2.0 + nt: int = 3 + niter: int = 5000 + diffeo_start: int = 0 + epL: float = 2e-8 + epT: float = 2e-1 + epV: float = 2e3 + sigmaM: float = 1.0 + sigmaB: float = 2.0 + sigmaA: float = 5.0 + sigmaR: float = 5e5 + sigmaP: float = 2e1 + + +@dataclass(slots=True) +class STalignConfig: + preprocess: STalignPreprocessConfig = field(default_factory=STalignPreprocessConfig) + registration: STalignRegistrationConfig = field(default_factory=STalignRegistrationConfig) + + +@dataclass(slots=True) +class STalignPreprocessResult: + source_grid: tuple[np.ndarray, np.ndarray] + source_image: np.ndarray + target_grid: tuple[np.ndarray, np.ndarray] + target_image: np.ndarray + + +@dataclass(slots=True) +class STalignResult: + affine: JaxArray + velocity: JaxArray + velocity_grid: tuple[JaxArray, JaxArray] + aligned_points: JaxArray + point_order: PointOrder = "row_col" + + def transform_points( + self, + points: np.ndarray, + *, + direction: Literal["forward", "backward"] = "forward", + point_order: PointOrder | None = None, + ) -> JaxArray: + """Transform arbitrary point arrays with the fitted map.""" + return transform_points( + self.velocity_grid, + self.velocity, + self.affine, + points, + direction=direction, + point_order=self.point_order if point_order is None else point_order, + ) + + +def stalign_preprocess( + source_points: np.ndarray, + target_points: np.ndarray, + *, + config: STalignPreprocessConfig | None = None, +) -> STalignPreprocessResult: + """Rasterize source and target point clouds for LDDMM registration.""" + config = STalignPreprocessConfig() if config is None else config + source_points = to_row_col(source_points, point_order="row_col") + target_points = to_row_col(target_points, point_order="row_col") + + source_x, source_y, source_image = rasterize( + source_points[:, 1], + source_points[:, 0], + dx=config.dx, + blur=config.blur, + expand=config.expand, + ) + target_x, target_y, target_image = rasterize( + target_points[:, 1], + target_points[:, 0], + dx=config.dx, + blur=config.blur, + expand=config.expand, + ) + + return STalignPreprocessResult( + source_grid=(source_y, source_x), + source_image=source_image, + target_grid=(target_y, target_x), + target_image=target_image, + ) + + +def transform_points( + xv: tuple[JaxArray, JaxArray], + v: JaxArray, + A: JaxArray, + points: np.ndarray, + *, + direction: Literal["forward", "backward"] = "forward", + point_order: PointOrder = "row_col", +) -> JaxArray: + """Transform point arrays with a fitted STalign map.""" + points_rc = to_row_col(points, point_order=point_order) + transformed = transform_points_row_col( + xv, + jnp.asarray(v), + jnp.asarray(A), + jnp.asarray(points_rc, dtype=jax_dtype()), + direction=direction, + ) + return jnp.asarray(from_row_col(np.asarray(transformed), point_order=point_order)) + + +def stalign_points( + source_points: np.ndarray, + target_points: np.ndarray, + *, + preprocessed: STalignPreprocessResult | None = None, + config: STalignConfig | None = None, + landmarks_source: np.ndarray | None = None, + landmarks_target: np.ndarray | None = None, +) -> STalignResult: + """Align source point cloud to target with a JAX LDDMM solver.""" + config = STalignConfig() if config is None else config + registration = config.registration + source_points = to_row_col(source_points, point_order="row_col") + target_points = to_row_col(target_points, point_order="row_col") + if preprocessed is None: + preprocessed = stalign_preprocess(source_points, target_points, config=config.preprocess) + + if (landmarks_source is None) != (landmarks_target is None): + raise ValueError("Expected both landmark arrays to be provided together.") + + if landmarks_source is None: + linear = np.eye(2, dtype=float) + translation = np.zeros(2, dtype=float) + source_landmarks = None + target_landmarks = None + else: + source_landmarks = to_row_col(landmarks_source, point_order="row_col") + target_landmarks = to_row_col(landmarks_target, point_order="row_col") + linear, translation = affine_from_points(source_landmarks, target_landmarks) + + dtype = jax_dtype() + result = lddmm( + preprocessed.source_grid, + preprocessed.source_image, + preprocessed.target_grid, + preprocessed.target_image, + L=jnp.asarray(linear, dtype=dtype), + T=jnp.asarray(translation, dtype=dtype), + points_source=None if source_landmarks is None else jnp.asarray(source_landmarks, dtype=dtype), + points_target=None if target_landmarks is None else jnp.asarray(target_landmarks, dtype=dtype), + **asdict(registration), + ) + aligned_points = transform_points( + result["xv"], + result["v"], + result["A"], + source_points, + direction="forward", + point_order="row_col", + ) + return STalignResult( + affine=result["A"], + velocity=result["v"], + velocity_grid=result["xv"], + aligned_points=aligned_points, + point_order="row_col", + ) diff --git a/src/squidpy/experimental/_fit/_registry.py b/src/squidpy/experimental/_fit/_registry.py new file mode 100644 index 000000000..0df6b0d5f --- /dev/null +++ b/src/squidpy/experimental/_fit/_registry.py @@ -0,0 +1,46 @@ +"""A flat registry mapping method names to estimator classes.""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import TYPE_CHECKING, TypeVar + +if TYPE_CHECKING: + from squidpy.experimental.fit._estimator import Estimator + +E = TypeVar("E", bound="type[Estimator]") + + +class Registry: + """A flat ``name -> Estimator`` registry for one *family* of methods. + + One :class:`Registry` is created per family (e.g. ``align``, ``impute``), + so keys are plain method names -- there is no ``(method, mode)`` compound + key, because the family already pins the rest. + """ + + def __init__(self, name: str) -> None: + self.name = name + self._estimators: dict[str, type[Estimator]] = {} + + def register(self, key: str) -> Callable[[E], E]: + """Return a class decorator registering an :class:`Estimator` under ``key``.""" + + def decorator(cls: E) -> E: + if key in self._estimators: + raise ValueError(f"Estimator {key!r} is already registered in the {self.name!r} registry.") + self._estimators[key] = cls + return cls + + return decorator + + def get(self, key: str) -> type[Estimator]: + """Return the :class:`Estimator` class registered under ``key``.""" + try: + return self._estimators[key] + except KeyError: + raise ValueError(f"Unknown {self.name} method {key!r}. Available: {sorted(self._estimators)}.") from None + + def keys(self) -> tuple[str, ...]: + """Return the registered method names.""" + return tuple(self._estimators) diff --git a/src/squidpy/experimental/_fit/_result.py b/src/squidpy/experimental/_fit/_result.py new file mode 100644 index 000000000..861f3cbe4 --- /dev/null +++ b/src/squidpy/experimental/_fit/_result.py @@ -0,0 +1,32 @@ +"""Result contract for fitted estimators.""" + +from __future__ import annotations + +from abc import ABC +from typing import Any + + +class FitResult(ABC): + """Base class for the output of :meth:`Estimator.fit`. + + A ``FitResult`` carries the *fitted parameters* of a model -- pointers or + parametric forms (an affine matrix, a displacement field, neighbour + weights), not bulk data -- together with free-form :attr:`metadata`. + + It is deliberately container-agnostic. Turning a result into mutations on + an :class:`~anndata.AnnData` / :class:`~spatialdata.SpatialData` + (write-back) is the caller's responsibility, not the result's: write-back + is a general squidpy concern with several unrelated shapes (set a field, + apply a transformation to a lazy element, ...) and does not belong on the + fit result. + """ + + metadata: dict[str, Any] + + def transform(self, x: Any) -> Any: + """Apply the fitted model to in-memory data ``x`` and return the result. + + Optional. Estimators whose fit does not yield a reusable map (or that + only produce side outputs) need not implement it. + """ + raise NotImplementedError(f"{type(self).__name__} does not implement `transform`.") diff --git a/tests/experimental/fit/__init__.py b/tests/experimental/fit/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/experimental/fit/test_core.py b/tests/experimental/fit/test_core.py new file mode 100644 index 000000000..9d9302bd3 --- /dev/null +++ b/tests/experimental/fit/test_core.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any + +import numpy as np +import pytest + +from squidpy.experimental.fit import Estimator, FitResult, Registry + + +@dataclass +class _MeanShiftResult(FitResult): + """Toy result: a constant per-axis offset baked into ``transform``.""" + + delta: np.ndarray + metadata: dict[str, Any] = field(default_factory=dict) + + def transform(self, x: np.ndarray) -> np.ndarray: + return np.asarray(x, dtype=float) + self.delta + + +class _MeanShiftEstimator(Estimator): + """Toy estimator: fit the offset that maps the query centroid onto the ref centroid.""" + + name = "mean_shift" + + def fit(self, ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: + delta = np.asarray(ref, dtype=float).mean(0) - np.asarray(query, dtype=float).mean(0) + return _MeanShiftResult(delta=delta, metadata={"method": self.name}) + + +def test_fit_then_transform_round_trip() -> None: + ref = np.array([[1.0, 1.0], [3.0, 3.0]]) # centroid (2, 2) + query = np.array([[0.0, 0.0], [2.0, 2.0]]) # centroid (1, 1) + + result = _MeanShiftEstimator().fit(ref, query) + + assert isinstance(result, FitResult) + np.testing.assert_allclose(result.delta, [1.0, 1.0]) + np.testing.assert_allclose(result.transform(query), query + 1.0) + assert result.metadata == {"method": "mean_shift"} + + +def test_fit_result_transform_default_not_implemented() -> None: + @dataclass + class _Bare(FitResult): + metadata: dict[str, Any] = field(default_factory=dict) + + with pytest.raises(NotImplementedError, match="does not implement `transform`"): + _Bare().transform(np.zeros((2, 2))) + + +def test_registry_register_get_keys() -> None: + reg = Registry("demo") + + @reg.register("mean_shift") + class _Registered(_MeanShiftEstimator): + pass + + assert reg.keys() == ("mean_shift",) + assert reg.get("mean_shift") is _Registered + assert isinstance(reg.get("mean_shift")().fit(np.ones((2, 2)), np.zeros((2, 2))), FitResult) + + +def test_registry_unknown_key_lists_available() -> None: + reg = Registry("demo") + reg.register("a")(_MeanShiftEstimator) + + with pytest.raises(ValueError, match=r"Unknown demo method 'b'. Available: \['a'\]"): + reg.get("b") + + +def test_registry_rejects_duplicate_key() -> None: + reg = Registry("demo") + reg.register("dup")(_MeanShiftEstimator) + + with pytest.raises(ValueError, match="already registered"): + reg.register("dup")(_MeanShiftEstimator) + + +def test_check_requirements_passes_when_none() -> None: + # _MeanShiftEstimator has the default empty `requires`. + _MeanShiftEstimator().check_requirements() + + +def test_check_requirements_raises_for_missing_dependency() -> None: + class _NeedsGhost(_MeanShiftEstimator): + name = "needs_ghost" + requires = ("squidpy_nonexistent_pkg_xyz",) + + with pytest.raises( + ImportError, match=r"requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]" + ): + _NeedsGhost().check_requirements() diff --git a/tests/experimental/fit/test_stalign.py b/tests/experimental/fit/test_stalign.py new file mode 100644 index 000000000..eb4b84a3d --- /dev/null +++ b/tests/experimental/fit/test_stalign.py @@ -0,0 +1,95 @@ +"""Integration tests for the ported STalign estimator. + +Tiny synthetic fixtures with ``niter=1`` keep these fast; they verify wiring +and shapes (dispatch -> JAX LDDMM -> StalignFitResult), not solver quality. +""" + +from __future__ import annotations + +import numpy as np +import pytest + +pytest.importorskip("jax") + +from squidpy.experimental.fit._methods import ALIGN, StalignEstimator, StalignFitResult +from squidpy.experimental.fit._methods._stalign_tools import ( + STalignConfig, + STalignPreprocessConfig, + STalignRegistrationConfig, +) + + +def _points_xy() -> np.ndarray: + return np.array( + [ + [10.0, 1.0], + [12.0, 1.0], + [11.0, 2.0], + [10.0, 3.0], + [12.0, 3.0], + ] + ) + + +def _tiny_config() -> STalignConfig: + """Single-iteration LDDMM hyperparameters - the smallest possible solve.""" + return STalignConfig( + preprocess=STalignPreprocessConfig(dx=0.5, blur=1.0), + registration=STalignRegistrationConfig(a=1.0, expand=1.0, nt=1, niter=1, epV=1.0), + ) + + +def test_stalign_registered_in_align_family() -> None: + assert "stalign" in ALIGN.keys() + assert ALIGN.get("stalign") is StalignEstimator + + +def test_stalign_fit_returns_displacement_result() -> None: + ref, query = _points_xy(), _points_xy() + + result = StalignEstimator().fit(ref, query, config=_tiny_config()) + + assert isinstance(result, StalignFitResult) + assert result.deltas.shape == query.shape + assert np.all(np.isfinite(result.deltas)) + assert result.affine is None + assert result.metadata["method"] == "stalign" + # Escape hatch: the full diffeomorphic map is preserved for power users. + assert "stalign_result" in result.metadata + + +def test_stalign_transform_bakes_in_deltas() -> None: + ref, query = _points_xy(), _points_xy() + + result = StalignEstimator().fit(ref, query, config=_tiny_config()) + + np.testing.assert_allclose(result.transform(query), query + result.deltas) + + +def test_stalign_transform_rejects_wrong_shape() -> None: + ref, query = _points_xy(), _points_xy() + result = StalignEstimator().fit(ref, query, config=_tiny_config()) + + with pytest.raises(ValueError, match="expects coordinates of shape"): + result.transform(np.zeros((1, 2))) + + +def test_stalign_fit_with_landmarks() -> None: + ref, query = _points_xy(), _points_xy() + landmarks = ref[:3] + + result = StalignEstimator().fit( + ref, + query, + config=_tiny_config(), + landmarks_source=landmarks, + landmarks_target=landmarks, + ) + + assert result.deltas.shape == query.shape + assert "stalign_result" in result.metadata + + +def test_stalign_fit_rejects_non_2d_input() -> None: + with pytest.raises(ValueError, match=r"Expected `query` to be an \(N, 2\) array"): + StalignEstimator().fit(_points_xy(), np.zeros((5, 3)), config=_tiny_config()) From e2266dec67cb33b3fc8e5e11855769864e94d7e8 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 09:21:40 +0200 Subject: [PATCH 070/131] save state --- src/squidpy/experimental/__init__.py | 4 +- src/squidpy/experimental/_fit/__init__.py | 8 +- src/squidpy/experimental/_fit/_estimator.py | 2 +- .../experimental/_fit/_methods/__init__.py | 27 +- .../experimental/_fit/_methods/_families.py | 16 + .../experimental/_fit/_methods/_landmark.py | 142 + .../_fit/_methods/_stalign/__init__.py | 11 + .../_fit/_methods/_stalign/_estimator.py | 19 +- .../_fit/_methods/_stalign/_helpers.py | 2 +- .../_fit/_methods/_stalign/_tools.py | 6 +- src/squidpy/experimental/_fit/_registry.py | 2 +- tests/experimental/fit/test_core.py | 2 +- tests/experimental/fit/test_stalign.py | 4 +- uv.lock | 6445 +++++++++++++++++ 14 files changed, 6658 insertions(+), 32 deletions(-) create mode 100644 src/squidpy/experimental/_fit/_methods/_families.py create mode 100644 src/squidpy/experimental/_fit/_methods/_landmark.py create mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/__init__.py create mode 100644 uv.lock diff --git a/src/squidpy/experimental/__init__.py b/src/squidpy/experimental/__init__.py index a33215238..3c5b1f537 100644 --- a/src/squidpy/experimental/__init__.py +++ b/src/squidpy/experimental/__init__.py @@ -6,6 +6,6 @@ from __future__ import annotations -from . import fit, im, pl, tl, utils +from . import im, pl, tl, utils -__all__ = ["fit", "im", "pl", "tl", "utils"] +__all__ = ["im", "pl", "tl", "utils"] diff --git a/src/squidpy/experimental/_fit/__init__.py b/src/squidpy/experimental/_fit/__init__.py index b56d9df80..66f88a475 100644 --- a/src/squidpy/experimental/_fit/__init__.py +++ b/src/squidpy/experimental/_fit/__init__.py @@ -17,7 +17,7 @@ Example ------- >>> import numpy as np ->>> from squidpy.experimental.fit import Estimator, FitResult, Registry +>>> from squidpy.experimental._fit import Estimator, FitResult, Registry >>> demo = Registry("demo") >>> class Shift(FitResult): ... def __init__(self, delta): @@ -39,8 +39,8 @@ from __future__ import annotations -from squidpy.experimental.fit._estimator import Estimator -from squidpy.experimental.fit._registry import Registry -from squidpy.experimental.fit._result import FitResult +from squidpy.experimental._fit._estimator import Estimator +from squidpy.experimental._fit._registry import Registry +from squidpy.experimental._fit._result import FitResult __all__ = ["Estimator", "FitResult", "Registry"] diff --git a/src/squidpy/experimental/_fit/_estimator.py b/src/squidpy/experimental/_fit/_estimator.py index c73a3173f..aea3abc93 100644 --- a/src/squidpy/experimental/_fit/_estimator.py +++ b/src/squidpy/experimental/_fit/_estimator.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, ClassVar if TYPE_CHECKING: - from squidpy.experimental.fit._result import FitResult + from squidpy.experimental._fit._result import FitResult class Estimator(ABC): diff --git a/src/squidpy/experimental/_fit/_methods/__init__.py b/src/squidpy/experimental/_fit/_methods/__init__.py index 8b15faa6b..f3b6864ca 100644 --- a/src/squidpy/experimental/_fit/_methods/__init__.py +++ b/src/squidpy/experimental/_fit/_methods/__init__.py @@ -1,12 +1,27 @@ -"""Concrete estimators for :mod:`squidpy.experimental.fit`. +"""Concrete estimators for :mod:`squidpy.experimental._fit`. -Importing this package registers the bundled estimators (e.g. STalign in the -``align`` family) but stays cheap: optional dependencies such as JAX are pulled -in lazily, only when an estimator's ``fit`` is actually called. +Importing this package registers the bundled estimators into their family +registries (``ALIGN``: STalign; ``LANDMARK``: similarity / affine) but stays +cheap: optional dependencies such as JAX are pulled in lazily, only when an +estimator's ``fit`` is actually called. """ from __future__ import annotations -from squidpy.experimental.fit._methods._stalign import ALIGN, StalignEstimator, StalignFitResult +from squidpy.experimental._fit._methods._families import ALIGN, LANDMARK +from squidpy.experimental._fit._methods._landmark import ( + AffineFitResult, + LandmarkAffineEstimator, + LandmarkSimilarityEstimator, +) +from squidpy.experimental._fit._methods._stalign import StalignEstimator, StalignFitResult -__all__ = ["ALIGN", "StalignEstimator", "StalignFitResult"] +__all__ = [ + "ALIGN", + "LANDMARK", + "AffineFitResult", + "LandmarkAffineEstimator", + "LandmarkSimilarityEstimator", + "StalignEstimator", + "StalignFitResult", +] diff --git a/src/squidpy/experimental/_fit/_methods/_families.py b/src/squidpy/experimental/_fit/_methods/_families.py new file mode 100644 index 000000000..867f6fa02 --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_families.py @@ -0,0 +1,16 @@ +"""Estimator family registries. + +Defined in their own module so individual estimator modules can register into +them without importing :mod:`squidpy.experimental._fit._methods` (which imports +the estimator modules in turn -- that would be circular). +""" + +from __future__ import annotations + +from squidpy.experimental._fit._registry import Registry + +#: Data-driven alignment estimators (consumed by ``experimental.tl.align``). +ALIGN = Registry("align") + +#: Closed-form landmark estimators (consumed by ``experimental.tl.align_by_landmarks``). +LANDMARK = Registry("landmark") diff --git a/src/squidpy/experimental/_fit/_methods/_landmark.py b/src/squidpy/experimental/_fit/_methods/_landmark.py new file mode 100644 index 000000000..3dc475235 --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_landmark.py @@ -0,0 +1,142 @@ +"""Closed-form landmark alignment estimators. + +Ported from scverse/squidpy#1162 (``fit_landmark_affine``). Two pure models, no +JAX: + +- ``"similarity"`` (4 DOF: rotation + uniform scale + translation) via + :func:`spatialdata.transformations.get_transformation_between_landmarks`. +- ``"affine"`` (6 DOF) via :func:`skimage.transform.estimate_transform`. + +Both consume **pre-paired** ``(N, 2)`` ``(x, y)`` landmark arrays (row ``i`` of +the query matches row ``i`` of the reference); ``N`` must be at least 3. No +automatic correspondence matching is performed. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, ClassVar, Literal + +import numpy as np + +from squidpy.experimental._fit._estimator import Estimator +from squidpy.experimental._fit._methods._families import LANDMARK +from squidpy.experimental._fit._result import FitResult + + +@dataclass +class AffineFitResult(FitResult): + """A fitted ``(3, 3)`` homogeneous affine mapping query onto ref, in ``(x, y)``.""" + + matrix: np.ndarray + source_cs: str | None = None + target_cs: str | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def __post_init__(self) -> None: + if self.matrix.shape != (3, 3): + raise ValueError(f"Expected a (3, 3) homogeneous matrix, found shape {self.matrix.shape}.") + + def transform(self, x: np.ndarray) -> np.ndarray: + """Apply the affine to an ``(N, 2)`` ``(x, y)`` coordinate array.""" + coords = np.asarray(x, dtype=float) + if coords.ndim != 2 or coords.shape[1] != 2: + raise ValueError(f"Expected an (N, 2) coordinate array, found shape {coords.shape}.") + return coords @ self.matrix[:2, :2].T + self.matrix[:2, 2] + + +class _LandmarkEstimator(Estimator): + """Base for closed-form landmark fits; subclasses pin :attr:`model`.""" + + model: ClassVar[Literal["similarity", "affine"]] + + def fit( + self, + landmarks_ref: np.ndarray, + landmarks_query: np.ndarray, + *, + source_cs: str | None = None, + target_cs: str | None = None, + ) -> AffineFitResult: + """Fit an affine mapping ``landmarks_query`` onto ``landmarks_ref``. + + Parameters + ---------- + landmarks_ref, landmarks_query + Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). + source_cs, target_cs + Optional coordinate-system labels stamped onto the result for + traceability; they do not affect the fit. + """ + ref = _validate_landmarks(landmarks_ref, name="landmarks_ref") + query = _validate_landmarks(landmarks_query, name="landmarks_query") + if ref.shape != query.shape: + raise ValueError( + f"`landmarks_ref` and `landmarks_query` must have the same shape; got {ref.shape} and {query.shape}." + ) + if ref.shape[0] < 3: + raise ValueError(f"`model={self.model!r}` needs at least 3 landmark pairs, got {ref.shape[0]}.") + + matrix = _fit_similarity(ref, query) if self.model == "similarity" else _fit_affine(ref, query) + return AffineFitResult( + matrix=matrix, + source_cs=source_cs, + target_cs=target_cs, + metadata={"method": self.model}, + ) + + +@LANDMARK.register("similarity") +class LandmarkSimilarityEstimator(_LandmarkEstimator): + """4-DOF similarity fit (rotation + uniform scale + translation).""" + + name = "similarity" + model = "similarity" + + +@LANDMARK.register("affine") +class LandmarkAffineEstimator(_LandmarkEstimator): + """6-DOF affine fit (rotation + non-uniform scale + shear + translation).""" + + name = "affine" + model = "affine" + + +def _validate_landmarks(points: np.ndarray, *, name: str) -> np.ndarray: + arr = np.asarray(points, dtype=float) + if arr.ndim != 2 or arr.shape[1] != 2: + raise ValueError(f"`{name}` must be a sequence of (x, y) pairs, got shape {arr.shape}.") + if not np.all(np.isfinite(arr)): + raise ValueError(f"`{name}` must contain only finite values.") + return arr + + +def _fit_similarity(ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: + """4-DOF similarity fit, delegated to spatialdata.""" + from spatialdata.models import PointsModel + from spatialdata.transformations import get_transformation_between_landmarks + + refs_pts = PointsModel.parse(ref_xy) + moving_pts = PointsModel.parse(query_xy) + sd_transform = get_transformation_between_landmarks(refs_pts, moving_pts) + return _extract_affine_matrix(sd_transform) + + +def _fit_affine(ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: + """Full 6-DOF affine fit, delegated to skimage's least-squares estimator.""" + from skimage.transform import estimate_transform + + model_obj = estimate_transform("affine", src=query_xy, dst=ref_xy) + return np.asarray(model_obj.params) + + +def _extract_affine_matrix(sd_transform: object) -> np.ndarray: + """Pull a ``(3, 3)`` homogeneous matrix out of a spatialdata transformation.""" + from spatialdata.transformations import Affine as SDAffine + from spatialdata.transformations import Sequence as SDSequence + + if isinstance(sd_transform, SDAffine): + return np.asarray(sd_transform.matrix) + if isinstance(sd_transform, SDSequence): + return np.asarray(sd_transform.to_affine_matrix(input_axes=("x", "y"), output_axes=("x", "y"))) + raise TypeError(f"Unexpected transformation type from spatialdata: {type(sd_transform).__name__}.") diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py b/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py new file mode 100644 index 000000000..78000bd3e --- /dev/null +++ b/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py @@ -0,0 +1,11 @@ +"""Private grouping of the ported STalign solver and its estimator adapter. + +Importing this package registers :class:`StalignEstimator` in the ``align`` +family and stays cheap -- JAX is pulled in lazily, only when ``fit`` runs. +""" + +from __future__ import annotations + +from squidpy.experimental._fit._methods._stalign._estimator import StalignEstimator, StalignFitResult + +__all__ = ["StalignEstimator", "StalignFitResult"] diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py b/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py index e9cec4735..1d11f29c2 100644 --- a/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py @@ -1,9 +1,9 @@ """STalign estimator: JAX LDDMM point-cloud registration. The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see -:mod:`._stalign_core`, :mod:`._stalign_helpers` and :mod:`._stalign_tools`. -This module only adds the thin :class:`Estimator` / :class:`FitResult` adapter -onto the :mod:`squidpy.experimental.fit` core. JAX is imported lazily inside +:mod:`._core`, :mod:`._helpers` and :mod:`._tools`. This module only adds the +thin :class:`Estimator` / :class:`FitResult` adapter onto the +:mod:`squidpy.experimental._fit` core. JAX is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module is cheap and does not require JAX. """ @@ -15,15 +15,12 @@ import numpy as np -from squidpy.experimental.fit._estimator import Estimator -from squidpy.experimental.fit._registry import Registry -from squidpy.experimental.fit._result import FitResult +from squidpy.experimental._fit._estimator import Estimator +from squidpy.experimental._fit._methods._families import ALIGN +from squidpy.experimental._fit._result import FitResult if TYPE_CHECKING: - from ._stalign_tools import STalignConfig - -#: Registry for the ``align`` family of estimators. -ALIGN = Registry("align") + from ._tools import STalignConfig @dataclass @@ -99,7 +96,7 @@ def fit( # Import the JAX-backed solver only after requirements pass, so callers # without JAX get the clean ImportError from check_requirements rather # than a confusing failure from a module-level `import jax`. - from ._stalign_tools import stalign_points + from ._tools import stalign_points ref_xy = np.asarray(ref, dtype=float) query_xy = np.asarray(query, dtype=float) diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py b/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py index 6d3304188..0be6a63ec 100644 --- a/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py @@ -2,7 +2,7 @@ Lifted from scverse/squidpy#1150 (Selman Özleyen). Container readers (``extract_points`` / ``extract_landmarks``) were dropped on the move into -:mod:`squidpy.experimental.fit`: reading coordinates out of an ``AnnData`` / +:mod:`squidpy.experimental._fit`: reading coordinates out of an ``AnnData`` / ``SpatialData`` is the caller's concern, so everything here operates purely on in-memory NumPy arrays. """ diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py b/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py index be73201fe..4501ac291 100644 --- a/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py +++ b/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py @@ -3,7 +3,7 @@ Lifted from scverse/squidpy#1150 (Selman Özleyen) with import paths adjusted and minor cleanups (config unpacking, lazy dtype resolution). The container write-back path (``STalignResult.transform_adata``) was dropped on the move -into :mod:`squidpy.experimental.fit`: results stay in memory and the caller +into :mod:`squidpy.experimental._fit`: results stay in memory and the caller owns write-back. """ @@ -15,8 +15,8 @@ import jax.numpy as jnp import numpy as np -from ._stalign_core import jax_dtype, lddmm, transform_points_row_col -from ._stalign_helpers import ( +from ._core import jax_dtype, lddmm, transform_points_row_col +from ._helpers import ( PointOrder, affine_from_points, from_row_col, diff --git a/src/squidpy/experimental/_fit/_registry.py b/src/squidpy/experimental/_fit/_registry.py index 0df6b0d5f..4df835f1d 100644 --- a/src/squidpy/experimental/_fit/_registry.py +++ b/src/squidpy/experimental/_fit/_registry.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: - from squidpy.experimental.fit._estimator import Estimator + from squidpy.experimental._fit._estimator import Estimator E = TypeVar("E", bound="type[Estimator]") diff --git a/tests/experimental/fit/test_core.py b/tests/experimental/fit/test_core.py index 9d9302bd3..89968f64a 100644 --- a/tests/experimental/fit/test_core.py +++ b/tests/experimental/fit/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental.fit import Estimator, FitResult, Registry +from squidpy.experimental._fit import Estimator, FitResult, Registry @dataclass diff --git a/tests/experimental/fit/test_stalign.py b/tests/experimental/fit/test_stalign.py index eb4b84a3d..56769e729 100644 --- a/tests/experimental/fit/test_stalign.py +++ b/tests/experimental/fit/test_stalign.py @@ -11,8 +11,8 @@ pytest.importorskip("jax") -from squidpy.experimental.fit._methods import ALIGN, StalignEstimator, StalignFitResult -from squidpy.experimental.fit._methods._stalign_tools import ( +from squidpy.experimental._fit._methods import ALIGN, StalignEstimator, StalignFitResult +from squidpy.experimental._fit._methods._stalign._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..a8b388cdf --- /dev/null +++ b/uv.lock @@ -0,0 +1,6445 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", + "python_full_version < '3.12'", +] + +[[package]] +name = "aiobotocore" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aioitertools" }, + { name = "botocore" }, + { name = "jmespath" }, + { name = "multidict" }, + { name = "python-dateutil" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/75/42cce839c2ec263ff74b10b650fe36b066fbb124cbee6f247eac0983e1ab/aiobotocore-3.7.0.tar.gz", hash = "sha256:c64d871ed5491a6571948dd48eabd185b46c6c23b64e3afd0c059fc7593ada30", size = 127054 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl", hash = "sha256:680bde7c64679a821a9312641b759d9497f790ba8b2e88c6959e6273ee765b8e", size = 89539 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062 }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513 }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748 }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673 }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757 }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152 }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010 }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969 }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871 }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844 }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969 }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193 }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477 }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198 }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321 }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069 }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859 }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876 }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557 }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258 }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199 }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013 }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501 }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981 }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934 }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671 }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219 }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049 }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557 }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931 }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125 }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427 }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534 }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446 }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930 }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927 }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141 }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476 }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507 }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465 }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523 }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113 }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351 }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205 }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618 }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185 }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311 }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147 }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356 }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637 }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896 }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721 }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663 }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094 }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701 }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360 }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023 }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795 }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405 }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082 }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346 }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891 }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113 }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088 }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976 }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444 }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128 }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029 }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758 }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883 }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668 }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461 }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661 }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800 }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382 }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724 }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027 }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644 }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630 }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403 }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924 }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119 }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072 }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819 }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441 }, +] + +[[package]] +name = "aioitertools" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182 }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "anndata" +version = "0.12.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "array-api-compat" }, + { name = "h5py" }, + { name = "legacy-api-wrap" }, + { name = "natsort" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "scverse-misc", version = "0.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scverse-misc", version = "0.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/0f/4c6b3a4c5dc57bd02f0656666aefae7e83b8158b839f60cfb1c5f2fbecab/anndata-0.12.16.tar.gz", hash = "sha256:26d557fd147728993aac99c2eef676e1e316a84bbeee3c176ca6c5d66e5fc443", size = 2255740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/31/bb8da9d45d2b54b7d7ba31945b7024374028274d089828dfd15035411828/anndata-0.12.16-py3-none-any.whl", hash = "sha256:2661332ed8e757886aafddc2f76103846ae17a16e1c003d76c3001ad067d8bc6", size = 175291 }, +] + +[[package]] +name = "annsel" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anndata" }, + { name = "more-itertools" }, + { name = "narwhals", extra = ["pandas"] }, + { name = "pooch" }, + { name = "session-info2" }, + { name = "tqdm" }, + { name = "universal-pathlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/93/cdbe4903acb43fd743832e0c2933b88ec2dfd2a87b147992c45b90f647c8/annsel-0.1.2.tar.gz", hash = "sha256:a9ab14495f3e28c216a5cb74f918542710ac82e22bb04db57af233bbbf536da8", size = 341977 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/43/fff37380b704b60fad17c298b631044d98e3db8b9721df7897c967facf51/annsel-0.1.2-py3-none-any.whl", hash = "sha256:9659f60f4ee5545bc030341b6b8e932e0a615b95ac83d9cb6e2bd50951ae26da", size = 19120 }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, +] + +[[package]] +name = "array-api-compat" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/e5/9a12dd1c2b0ad61f3c3ad0fc14b888c65fd735dd9d26805f77317303cbe5/array_api_compat-1.14.0.tar.gz", hash = "sha256:c819ba707f5c507800cb545f7e6348ff1ecc46538381d9ad9b371ffc9cd6d784", size = 106369 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d3/54cd560804a8c2b898824778e86c13c2a14600bc83532a9c4f69f2f469c3/array_api_compat-1.14.0-py3-none-any.whl", hash = "sha256:ed5af1f9b6595a199c942505f281ec994892556b6efc24679a0501e87a7d6279", size = 60124 }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, +] + +[[package]] +name = "async-lru" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403 }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 }, +] + +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 }, +] + +[[package]] +name = "backports-zstd" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/05/480d439b482edf59b786bc19b474d990c61942e372f5de3dc14acac8154d/backports_zstd-1.5.0.tar.gz", hash = "sha256:a5e622a82eb183b4fbe18032755ce0a15fa9a82f2adb9b621620b91247aaedb7", size = 998556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/bc/083c0ebee316f4863ed288c4a5eaa1e98be115e82deb8855da8bab1c7701/backports_zstd-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fbaa5502617dc4f04327c7a2951f0fcdca7aaef93ddf32c15dc8b620208174fa", size = 436838 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/bf778667fff6598dbd0791745123ed964aee94753ae8e4e92aa1e07417b6/backports_zstd-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:204f00d62e95aab987c7c019452b2373bdefb17252443765f2ede7f15b6e669a", size = 363215 }, + { url = "https://files.pythonhosted.org/packages/63/a5/4fae78734dbefcb4b5386137c807e2107c4bc94e85c0d9eaae79206dde84/backports_zstd-1.5.0-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:2c77c0d4c330afd26d2a98f3d689ab922ec3f046014a1614ddcaad437666ac05", size = 507161 }, + { url = "https://files.pythonhosted.org/packages/d8/ec/b64409f0cf56fb65181d6f5d9130058f19d5c3c9f8c581a5e2bd62642630/backports_zstd-1.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bb2f2d2c07358edeaa251cf804b993e9f0d5d93af8a7ea2414d80ff3c105e95", size = 476728 }, + { url = "https://files.pythonhosted.org/packages/4d/10/4c1693cb4e129585a6e4cb565106cad7347e61c43c8375b9e9cadb00eb06/backports_zstd-1.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89f554abcebcb2c487024e63be8059083775c5fd351fec0cc2dc3e9f528714", size = 582388 }, + { url = "https://files.pythonhosted.org/packages/45/b9/dc748a0e7d21ce2228241f6e8af96d297c80ab69c4c49429309b8fa3beb8/backports_zstd-1.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea969758af743000d822fc3a69dc9de059bbbb8d07d2f13e06ff49ac63cce74f", size = 642091 }, + { url = "https://files.pythonhosted.org/packages/de/5f/02366ddae6e008d53df71605e4e3ca8dcea5d1dfcba29040b46883a23127/backports_zstd-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:775ad82d268923639bc924013fc61561df376c148506b241f0f80718b5bb3a2f", size = 492256 }, + { url = "https://files.pythonhosted.org/packages/c0/c7/c5e7824c17abc87dbb24c7c90dc43054d701533cf04d3531cb9b7105cdac/backports_zstd-1.5.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:663128370bbc2ebcc436b8977bc434a7bf29919d92d91fee05ed6fb0fa807646", size = 566214 }, + { url = "https://files.pythonhosted.org/packages/12/7b/ee7368c4ad8f5e00b3fd84fc566fb7714aa766c5672500793990e19efa00/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572c76832e9a24da4084befa52c23f4c03fede2aa250ae6250cbc5a11b980f69", size = 482666 }, + { url = "https://files.pythonhosted.org/packages/77/36/2826f9f04b6c91d5f707f49188ac6f5ec7487b36d73caedfa20db3307826/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9410bcbcd3afd787a15a276d68f954d1703788c780faa421183a61d39da8b862", size = 510594 }, + { url = "https://files.pythonhosted.org/packages/84/3b/95342baf0e301b7d06c6862389f8520a9d71f073a6c1a5b86182e7d89148/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0fab15e6895bef621041dd82d6306ffa24889257dd902c4b98b88e4260b3465d", size = 586713 }, + { url = "https://files.pythonhosted.org/packages/bc/32/73d2b8f572960307406b084bb8932f4ebd9fcedb05d1502e04fecf25970a/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ffde637b6d0082f1c3356657002469cf199c7c12d50d9822a55b13425c778d3", size = 564037 }, + { url = "https://files.pythonhosted.org/packages/8f/a4/6e319fa7fa5851c3ca9701cbded9522c16018432a01a33a95cc0fccb6b4a/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c01d377c1489cb2230bf6a9ff01c73c42863cc96ee648c49923d4f6d4ea4e2d5", size = 632626 }, + { url = "https://files.pythonhosted.org/packages/67/5c/10df0444db05f9276b286d230a3d6948ad47c593fc22925b8fe551d34b26/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4080bb9c8a51bb2bf8caf8018d78278cd49eb924cb06a54f56a411095e2ac912", size = 496270 }, + { url = "https://files.pythonhosted.org/packages/f1/ad/6cd1de5cd858ac653833098f13a4643a4c9db484072350d3dbf299cc46f1/backports_zstd-1.5.0-cp311-cp311-win32.whl", hash = "sha256:9f4fe3fd82c8c6e8a9fdc5c71f92f9fe2442d02e7f59fddef25a955e189e3f38", size = 289754 }, + { url = "https://files.pythonhosted.org/packages/1d/1b/df94ad1cb79705d717f7e1063da642c538a6d7ce6443c8e60355fa507ea4/backports_zstd-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e7c0372fa036751109604c70a8c87e59faaacc195d519c8cb9e0e527ee2b5478", size = 314829 }, + { url = "https://files.pythonhosted.org/packages/e8/e7/24e60da7cc89b9ed1c5b474678e316dd0ddfe7cd1de39b23d04452ca5946/backports_zstd-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:264a66137555bb4648f7e64cfc514d820758072684f373269fcdd2e8d4a90306", size = 291497 }, + { url = "https://files.pythonhosted.org/packages/24/71/29ed213344f8f62b7520745d7df3752d88db456aff9d8b706bdf5eb99a3c/backports_zstd-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1858cacdb3e50105a1b60acdc3dd5b18650077d12dce243e19d5c88e8172bd71", size = 437170 }, + { url = "https://files.pythonhosted.org/packages/d0/e3/a58a3eb8fc54d4e3e4f684ed7b1f688da02e5bda5ae5e2809e94cf2ead2f/backports_zstd-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ccffc0a1974ecc2cc42afa4c15f56d036a4b2bae0abc46e6ba9b3358d9b1c037", size = 363265 }, + { url = "https://files.pythonhosted.org/packages/3f/03/9d13840d206dec1c4698c803f61c58379b3578cb9dc6140ba5fa4ce2f31d/backports_zstd-1.5.0-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:ab3430ab4d4ac3fb1bc1e4174d137731e51363b6abd5e51a1599690fe9c7d61d", size = 507527 }, + { url = "https://files.pythonhosted.org/packages/6a/8f/8dc4b5736dca218cbca9609549a8f6dc202990abdb49afdc6112442f5360/backports_zstd-1.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c737c1cb4a10c2d0f6cba9a347522858094f0a737b4558c67a777bcaa4a795cd", size = 477352 }, + { url = "https://files.pythonhosted.org/packages/96/2c/65a66976a761b5b62eacbaed5ed418c694b24b5c480399315d799751de62/backports_zstd-1.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0379c66510681a6b2780d3f3ef2cff54d01204b52448d64bde1855d40f856a04", size = 582799 }, + { url = "https://files.pythonhosted.org/packages/d3/e9/ee93a66cd28cb3ad7f3c04d1105325a5428671b18bd41ba9ed8b43bc44cf/backports_zstd-1.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c7474b291e264c9609358d3875cf539623f7a65339c2b533020992b1a4c095b", size = 641530 }, + { url = "https://files.pythonhosted.org/packages/e4/4b/2cecd4d6679f175f28ae02022bd2050ff4023e38902fae104dbe2e231911/backports_zstd-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb73c22444617bc5a3abf32dd27b3f2085898cfe3b95e6855300e9189898a3bd", size = 495324 }, + { url = "https://files.pythonhosted.org/packages/4d/20/ee21e4e791e31f38f7a70b3961eb64b350d9be802a335e7a04c02b41b197/backports_zstd-1.5.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6cd7f6c33afd89354f74469e315e72754e3040f91f7b685061e225d9e36e3e8e", size = 569796 }, + { url = "https://files.pythonhosted.org/packages/76/da/86c9a2ea384885b60638b3e47113198449568d0e36ef3834d1f969623092/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2106309071f279b38d3663c55c7fed192733b4f332b50eb3fa707e54bad6967a", size = 483367 }, + { url = "https://files.pythonhosted.org/packages/5d/f0/c95c6e4dd28fc314547782a482839e422283d62c2aaf45d30672109a4a1e/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:56fffa80be74cb11ac843333bbdc56e466c87967706886b3efd6b16d83830d90", size = 510976 }, + { url = "https://files.pythonhosted.org/packages/0e/a2/72777b7e1872228a13b09b0bf77ae6cf626008d462cc2e1a0ae64721fd55/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5e8b8251eec80e67e30ec79dfc5b3b1ada069b9ac48b56b102f3e2c6f8281062", size = 587190 }, + { url = "https://files.pythonhosted.org/packages/f5/a1/db5d1aee59da308eadeaa189764a4ec68e98495c309a13dcb8da5718fef1/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f334dd17ffead361aa9090e40151bd123507ce213a62733121b7145c6711cbde", size = 567395 }, + { url = "https://files.pythonhosted.org/packages/00/0f/39ca1a6e8c5c2dc81da9e06c44d1990cc464f4b16dae214e877afd7adfc0/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:78cbfd061255fef6de5070a54e0f9c00e8aabad5c99dd2ad884a3a7d1acc09ae", size = 632048 }, + { url = "https://files.pythonhosted.org/packages/73/fd/a438ee4fc615016dbe96112b709b6805ee19eb215f46e208c8fbce086d8d/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f55d70df44f49d599e20033013bc1ae705202735c45d4bca8eb963b225e15fd", size = 499833 }, + { url = "https://files.pythonhosted.org/packages/f7/42/f544fde4de32687e28c514288ae3c11106ba644e9dd580992cbd704bbb49/backports_zstd-1.5.0-cp312-cp312-win32.whl", hash = "sha256:a8b096e0383a3bcab34f8c97b79e1a52051189d11258bbc2bc1145997a15dd1d", size = 289876 }, + { url = "https://files.pythonhosted.org/packages/ad/31/9c29cd3175892e5ee909f5e8d14707fa07815301ff24b5c697d1cea62a77/backports_zstd-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:e2802899ba4ef1a062ffe4bb1292c5df32011a54b4c3004c54f46ec975f39554", size = 314933 }, + { url = "https://files.pythonhosted.org/packages/11/ee/1a50acd6446c0d57c4f93ad6ce68e1a631ad920737a6b2d0bbbc47de7f42/backports_zstd-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:3c0353e66942afbd45518788cfbd1e9e117828ceb390fa50517f46f291850d8e", size = 291665 }, + { url = "https://files.pythonhosted.org/packages/c6/e6/252521e3a847eb200bc0a1d528542d651b9c8dc7953e231c39ed2890d5ff/backports_zstd-1.5.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:02a57ee8598dd863c0b11c7af00042ce6bc045bf6f4249fa4c322c62614ca1fd", size = 400134 }, + { url = "https://files.pythonhosted.org/packages/36/43/27ef105ffa2da3d52218d4a7b2e14037974283953b3ee790358af6e9b4df/backports_zstd-1.5.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:c56c11eb3173d540e1fb0216f7ab477cbd3a204eca41f5f329059ee8a5d2ad47", size = 454225 }, + { url = "https://files.pythonhosted.org/packages/0e/c9/cdcba1244347500d00567ce2cd6bf04c92d1b0fb6405fb8e13c07715eb46/backports_zstd-1.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ef98f632026aa8e6ce05d786977092798efbe78677aa71219f22d31787809c90", size = 357229 }, + { url = "https://files.pythonhosted.org/packages/df/da/cea04dab3ffb940bde9a59866bde6f2594a7b3ef2948a63fb3898f73d311/backports_zstd-1.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:c3712300b18f9d07f788b03594b2f34dfad89d77df96938a640c5007522a6b69", size = 365907 }, + { url = "https://files.pythonhosted.org/packages/da/c4/6a71df2e65033f9b7d8017d77ea2bb572fc2ebc814ea383fdcda4187597a/backports_zstd-1.5.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:bdbc75d1f54df70b65bcfbc8aa0cac21475f79665bb045960af606dc07b56090", size = 446453 }, + { url = "https://files.pythonhosted.org/packages/66/e7/f98ad1a6a249c27884df9d28cf6ebc3c368e0e3288a741c1d51a572bb3d7/backports_zstd-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93d306300d25e59f1cbe98cda494bf295be03a20e8b2c5602ee5ddc03ded29f2", size = 436634 }, + { url = "https://files.pythonhosted.org/packages/ba/42/d0393ecc64e2ab6ae1b5ca7edbe26e3fe5196885f15d6cc4bce7254e29cd/backports_zstd-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:305d2e4ae9a595d0fd9d5bea5a7a2163306c6c4dcc5eec35ecd5008219d4580e", size = 362867 }, + { url = "https://files.pythonhosted.org/packages/41/fe/87aa9404763bada695d06e5cb9d0575bae033cbf3a2e4e3bd648760178f7/backports_zstd-1.5.0-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:c8f0967bf8d806b250fb1e905a6b8190e7ae83656d5308989243f84e01fa3774", size = 506844 }, + { url = "https://files.pythonhosted.org/packages/56/94/3af7ce637d148e0b0acb1298b61afe9a934ed425bad9ff05e87afbf6766d/backports_zstd-1.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76b7314ca9a253171e3e9524960e9e6411997323cf10aecbbc330faa7a90278d", size = 476975 }, + { url = "https://files.pythonhosted.org/packages/aa/6c/dc2aa1b48296ac6effc3bacb5a3061d40ed74bf73082dfe38eed2ba8362b/backports_zstd-1.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b1d0bf16bba86b1071731ced389f184e8de61c1afcafa584244f7f726632f92f", size = 582496 }, + { url = "https://files.pythonhosted.org/packages/f6/38/dd49d3dd27eda9b165ccd63d70538fea016a3e9e42923bbbc1d89fae8a43/backports_zstd-1.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:96709d27d406008575ef759405169d538040156704b457d8c0ac035127a46b67", size = 643257 }, + { url = "https://files.pythonhosted.org/packages/59/75/78e819272450aec2462f97a1bceb90bde481f9dba435bf9e76d580b4dec4/backports_zstd-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5737402c29b2bd5bc661d4cde08aed531ed326f2b59a7ad98dc07650dc99a2c9", size = 491958 }, + { url = "https://files.pythonhosted.org/packages/62/ae/d860f9cf21cb59d583a12166353bf71a439538e2b669f4a7736e400ca596/backports_zstd-1.5.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b65f37ddd375114dbf84658e7dd168e10f5a93394940bfefa7fafc2d3234450", size = 567198 }, + { url = "https://files.pythonhosted.org/packages/38/7c/b175d4c9ff60f964c8f6dd43211de905227cfde5a41eb5f654df58483025/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fae7825dde4f81c28b4c66b1e997f893e296c3f1668351952b3ed085eb9f8cd", size = 482792 }, + { url = "https://files.pythonhosted.org/packages/eb/e3/f7b50cf891a10da5f9c412ed4a9c4a772df4d4186d98a41e75c9b462f148/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3aa10e77c0e712d2dfb950910b50591c2fb11f0f1328814e23acc0b4950766df", size = 510363 }, + { url = "https://files.pythonhosted.org/packages/be/50/e7841fd4a65661d527697a0e2dab97295868965ccd4e3e12474472719a60/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:518b2ef54ce0fee6d29379cfd64ef66e639456f1b18943466e929b19677f135f", size = 586917 }, + { url = "https://files.pythonhosted.org/packages/c6/7c/57e985dbd621f0307b8c57cabb258eb976793f2aeaf8a5bc020e15b4a793/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:673a1e5fdaa6cb0c7a967eb33066b6dd564871b3498a93e11e2972998047d11f", size = 565004 }, + { url = "https://files.pythonhosted.org/packages/2f/8f/855ffcd1ee0fcf44c3fe62e36db8e7362292d450cc7c4b3f43edccbcd37a/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1277c07ff2d731586aa05aebd946a1b30184620d886a735dd5d5bf94a4a1061e", size = 633737 }, + { url = "https://files.pythonhosted.org/packages/20/39/c4129a03d268699200dfebe1ccab97c7c332d2794571afb372a62e4ed098/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aff334c7c38b4aea2a899f3138a99c1d58f0686ad7815c74bff506ecf4333296", size = 496309 }, + { url = "https://files.pythonhosted.org/packages/8e/33/34152316dd244dcd43d5300ded3cf6e1b46d343e4e92620c23e533fa91df/backports_zstd-1.5.0-cp313-cp313-win32.whl", hash = "sha256:b932834c4d85360f46d1e7fbf3eee1e26ba594e0eb5c3ee1281e89bc1d48d06f", size = 289560 }, + { url = "https://files.pythonhosted.org/packages/71/c5/f759bc87fd77c88f4fdad2d878535fb7e9537c6a05876d206e6690bf33c6/backports_zstd-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:c71dfbeced720326a8917a6edf921c568dc2396228c6432205c6d7e7fe7f3707", size = 314812 }, + { url = "https://files.pythonhosted.org/packages/47/96/d7970dbb2fef34b549b34146090f48f41903cc7268b1ed1c7542eaa1852e/backports_zstd-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:7b5798b20ffff71ee4620a01f56fe0b50271724b4251db08c90a069446cc4752", size = 291411 }, + { url = "https://files.pythonhosted.org/packages/89/92/8e8769e1e3ebec16d39f455e317a0f137a191b1f122853d0377c660666ce/backports_zstd-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0ca2d4ac4901eada2cfb86fda692e5d4a1e09485d9f2ec5777dc6cd3154b3b46", size = 410809 }, + { url = "https://files.pythonhosted.org/packages/63/5c/741a2923020c45b85cad4dffffcb86dbfa2d4aaed27f18ee793428ef4c24/backports_zstd-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:20796211a623ec6e0061cef4d7cca760e9e0a0a951bb30dc9ba89ed4a3fea5e4", size = 340342 }, + { url = "https://files.pythonhosted.org/packages/c8/3b/68c4fe8a551d3f47ed75ddcf15dc7c777bb9d869fc0e0f5b7cacc9f158f5/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:5232cd2a58c60da4ceb0e09e42dbc579b92dda4a9301a756af0c738223a23487", size = 421476 }, + { url = "https://files.pythonhosted.org/packages/a8/4d/ab5dcd6ab9a7ac02ec42c4507211da7dadb9498abb655115c296077e2b8b/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:012d88a9ae08f331e1adc03dfbda4ff2ae7f76ea62455975827b215677a11aec", size = 395020 }, + { url = "https://files.pythonhosted.org/packages/55/aa/ec512a0d14552bbb4e75693f7065434b865956abd045ceb67f0574146241/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbb7d79f8e43b6e0e17616961e425b9f8b32d9933e1db69242baa6e21f44a978", size = 414985 }, + { url = "https://files.pythonhosted.org/packages/aa/31/759d077aa680555e17c9d2bb09edf4c3428d895fe5d35a8df67684401b84/backports_zstd-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6172dcdd664ef243e55a35e6b45f1c866767c61043f0ddcd908abd14df662065", size = 300853 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, +] + +[[package]] +name = "bleach" +version = "6.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, +] + +[package.optional-dependencies] +css = [ + { name = "tinycss2" }, +] + +[[package]] +name = "botocore" +version = "1.43.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/79/2f4be1896db3db7ccf44504253a175d56b6bd6b669619edc5147d1aa21ea/botocore-1.43.0.tar.gz", hash = "sha256:e933b31a2d644253e1d029d7d39e99ba41b87e29300534f189744cc438cdf928", size = 15286817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl", hash = "sha256:cc5b15eaec3c6eac05d8012cb5ef17ebe891beb88a16ca13c374bfaece1241e6", size = 14970102 }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134 }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344 }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560 }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613 }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374 }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597 }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574 }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971 }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972 }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078 }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076 }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820 }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635 }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271 }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048 }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529 }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097 }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983 }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519 }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572 }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963 }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361 }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705 }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419 }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901 }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742 }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061 }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239 }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173 }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841 }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304 }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455 }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036 }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739 }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277 }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819 }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281 }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843 }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328 }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061 }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031 }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239 }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589 }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733 }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652 }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229 }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552 }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806 }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316 }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274 }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468 }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460 }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330 }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828 }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627 }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008 }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303 }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282 }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595 }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986 }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711 }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036 }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998 }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056 }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537 }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176 }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723 }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085 }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819 }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915 }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234 }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042 }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706 }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727 }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882 }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860 }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564 }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276 }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238 }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189 }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352 }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024 }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869 }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541 }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384 }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133 }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257 }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851 }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393 }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609 }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014 }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979 }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110 }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824 }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103 }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194 }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827 }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168 }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018 }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958 }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639 }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "colorcet" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/af/b969f541242b84cbbacabdf20862e487689e352bf0f02f90df2795d29da5/colorcet-3.2.1.tar.gz", hash = "sha256:48d9a67e6e59dc5c0a965aa1b46fe5d59cdc95cc36a95949f29313f950ac59f7", size = 2202958 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/24/e95471ae93c08d3606c9c7343cf65d490f154daa88b50581957a0aa780f4/colorcet-3.2.1-py3-none-any.whl", hash = "sha256:3f6fde13cef2169222dd5fe2a2bf847c02d644470fdf167ed566f6421df470f7", size = 262291 }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773 }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149 }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222 }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234 }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555 }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238 }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218 }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867 }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677 }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234 }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123 }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419 }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979 }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653 }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536 }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397 }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601 }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288 }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386 }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018 }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567 }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655 }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257 }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034 }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672 }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234 }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169 }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859 }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062 }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932 }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024 }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578 }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524 }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730 }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897 }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751 }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486 }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106 }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548 }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297 }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023 }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157 }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570 }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713 }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189 }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251 }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810 }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871 }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264 }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819 }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650 }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833 }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692 }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424 }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300 }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769 }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892 }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748 }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554 }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118 }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555 }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027 }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428 }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331 }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831 }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809 }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593 }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202 }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207 }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315 }, +] + +[[package]] +name = "coverage" +version = "7.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/d7/477ad149490e6cb849f28abea1dabb9c823cea72e7500c81b4240ce619c0/coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f", size = 219848 }, + { url = "https://files.pythonhosted.org/packages/91/82/a5eb47257c50601bb7b9a9d2857c67b7a3a85ad74180eb2c98bb1fbe0ce5/coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4", size = 220354 }, + { url = "https://files.pythonhosted.org/packages/43/8b/78419b5391a5cb706b6544390507e469d83ffc9a8248b02c4011aceb9365/coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1", size = 250771 }, + { url = "https://files.pythonhosted.org/packages/77/63/e77aaacd491182210d639636b7a8bba23ffffa9b82aa3762da9431855fa9/coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f", size = 252683 }, + { url = "https://files.pythonhosted.org/packages/65/1c/a022e3cfbec2ac241640003cb3a817e161d9c7f5aa9b49173756cdc03204/coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129", size = 254791 }, + { url = "https://files.pythonhosted.org/packages/61/d6/967e408aca4c1ceb88cb0cc677169110ae7f5995fb5eaf5fb1f5a1bb8f5d/coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860", size = 256748 }, + { url = "https://files.pythonhosted.org/packages/b8/be/869188f7fe28638078ec479331ace6dc5f7b40b7153eb616f47ab79404d8/coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c", size = 250907 }, + { url = "https://files.pythonhosted.org/packages/07/aa/adb7d3b4278d690e68703abcd76ab1b948242e3668d921711551b78f9ddb/coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7", size = 252483 }, + { url = "https://files.pythonhosted.org/packages/43/61/331c74103c62dcb0c4b9b3a0de9a61aca016208b0a90f109592a9f9ecc28/coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec", size = 250545 }, + { url = "https://files.pythonhosted.org/packages/f6/b6/c5dae3c104d89be04828f61810e6b3473825482e4c288cc4ed04553e08ae/coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef", size = 254310 }, + { url = "https://files.pythonhosted.org/packages/ad/a1/2b9d5863e3b83c01ad8199e3c597802fbb3a9dc90b058885804c20296d31/coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df", size = 250266 }, + { url = "https://files.pythonhosted.org/packages/7f/5e/0e511fbdb269359be26fe678a1c3fa1f2aa2a01573cc3f54268c8d6d4797/coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9", size = 251174 }, + { url = "https://files.pythonhosted.org/packages/85/10/e55307b622b3dd9671cb321824502dc10f93e72f2802b9946159a8edadeb/coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548", size = 222354 }, + { url = "https://files.pythonhosted.org/packages/71/cf/107421693cfb71e4f1ca5bf70443f64d4161878068d07a3e51c7ad21d17b/coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e", size = 223290 }, + { url = "https://files.pythonhosted.org/packages/b8/1d/3e3644585eb29e9dafefb19555078529a4d7cce12bd21929664eea989277/coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3", size = 221953 }, + { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022 }, + { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379 }, + { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888 }, + { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624 }, + { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739 }, + { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998 }, + { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296 }, + { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658 }, + { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803 }, + { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873 }, + { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245 }, + { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567 }, + { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372 }, + { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044 }, + { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412 }, + { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412 }, + { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008 }, + { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241 }, + { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373 }, + { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635 }, + { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373 }, + { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341 }, + { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497 }, + { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159 }, + { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934 }, + { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584 }, + { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394 }, + { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015 }, + { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733 }, + { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086 }, + { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381 }, + { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458 }, + { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884 }, + { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022 }, + { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631 }, + { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443 }, + { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069 }, + { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780 }, + { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970 }, + { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157 }, + { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259 }, + { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320 }, + { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577 }, + { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091 }, + { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421 }, + { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466 }, + { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973 }, + { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318 }, + { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633 }, + { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488 }, + { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329 }, + { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291 }, + { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564 }, + { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107 }, + { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764 }, + { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837 }, + { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650 }, + { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218 }, + { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822 }, + { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084 }, + { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454 }, + { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578 }, + { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981 }, + { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112 }, + { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558 }, + { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447 }, + { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048 }, + { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781 }, + { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896 }, + { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214 }, + { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624 }, + { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728 }, + { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752 }, + { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433 }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620 }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283 }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573 }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677 }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808 }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941 }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579 }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326 }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672 }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574 }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776 }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121 }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042 }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526 }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116 }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030 }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640 }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657 }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362 }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580 }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283 }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266 }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228 }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097 }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582 }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479 }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481 }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713 }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165 }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947 }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059 }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575 }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126 }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270 }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797 }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "dask" +version = "2026.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/46/61ecde57bac647ca7eb6ffef8dcd90af6c1c649020874cd7fd8738003d62/dask-2026.1.1.tar.gz", hash = "sha256:12b1dbb0d6e92f287feb4076871600b2fba3a843d35ff214776ada5e9e7a1529", size = 10994732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/4b/9cc373120658a2516aa5f6dcdde631c95d714b876d29ad8f8e009d793f3f/dask-2026.1.1-py3-none-any.whl", hash = "sha256:146b0ef2918eb581e06139183a88801b4a8c52d7c37758a91f8c3b75c54b0e15", size = 1481492 }, +] + +[package.optional-dependencies] +array = [ + { name = "numpy" }, +] + +[[package]] +name = "dask-image" +version = "2026.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dask", extra = ["array"] }, + { name = "numpy" }, + { name = "pims" }, + { name = "scipy" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/49/e592a13a5e1efdcdb8f1faab7c4e309c61648792e276f9ced5fb79381b33/dask_image-2026.5.0.tar.gz", hash = "sha256:ed6b462277e691b2c12b0890ba801a0f9a00cc1894b0aa71b195a7a1419b2b00", size = 80457 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/5b/15d6d6ff8697b188787609be059fe4f07f99fc00f43f68e9e1540fa8733e/dask_image-2026.5.0-py3-none-any.whl", hash = "sha256:acf86cd7f0f1e97804198d30b7cc931efd29f9dec86c65ec004b50405c3f5227", size = 43814 }, +] + +[[package]] +name = "datashader" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorcet" }, + { name = "multipledispatch" }, + { name = "numba" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "param" }, + { name = "pyct" }, + { name = "requests" }, + { name = "scipy" }, + { name = "toolz" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/4b/a9141f286f0c01685e9715ba3404769a6138e74575c4d5ccbaa95a22c3bd/datashader-0.19.1.tar.gz", hash = "sha256:f62d880a4a431813f9bb3959e565feda79c1634f889aaadcf948cb0d0c114cdd", size = 10581968 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/41/247627c8b9fef5c605d00546b85771a8fe42975b9616a557cead5468789b/datashader-0.19.1-py3-none-any.whl", hash = "sha256:7ce7154ff3ed070607f429355f57002fee5a17964e47c0b6447eeafe8cef9c82", size = 10715897 }, +] + +[[package]] +name = "debugpy" +version = "1.8.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/56/c3baf5cbe4dd77427fd9aef99fcdade259ad128feeb8a786c246adb838e5/debugpy-1.8.20-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:eada6042ad88fa1571b74bd5402ee8b86eded7a8f7b827849761700aff171f1b", size = 2208318 }, + { url = "https://files.pythonhosted.org/packages/9a/7d/4fa79a57a8e69fe0d9763e98d1110320f9ecd7f1f362572e3aafd7417c9d/debugpy-1.8.20-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:7de0b7dfeedc504421032afba845ae2a7bcc32ddfb07dae2c3ca5442f821c344", size = 3171493 }, + { url = "https://files.pythonhosted.org/packages/7d/f2/1e8f8affe51e12a26f3a8a8a4277d6e60aa89d0a66512f63b1e799d424a4/debugpy-1.8.20-cp311-cp311-win32.whl", hash = "sha256:773e839380cf459caf73cc533ea45ec2737a5cc184cf1b3b796cd4fd98504fec", size = 5209240 }, + { url = "https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl", hash = "sha256:1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb", size = 5233481 }, + { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686 }, + { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588 }, + { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372 }, + { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835 }, + { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560 }, + { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272 }, + { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208 }, + { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930 }, + { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066 }, + { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425 }, + { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407 }, + { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521 }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658 }, +] + +[[package]] +name = "decorator" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365 }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, +] + +[[package]] +name = "distributed" +version = "2026.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "cloudpickle" }, + { name = "dask" }, + { name = "jinja2" }, + { name = "locket" }, + { name = "msgpack" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "sortedcontainers" }, + { name = "tblib" }, + { name = "toolz" }, + { name = "tornado" }, + { name = "urllib3" }, + { name = "zict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/19/0c13efdffc55cb311594f66c1c8d36a3c4711e427c820155fb9c59138b5e/distributed-2026.1.1.tar.gz", hash = "sha256:3d2709a43912797df3c345af3bb333bbf1a386ec1e9e6a134e5f050521373dbd", size = 2102870 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/dc/6d709bcf8fed7611d8a510aeed23b0436cf6db5b61e63c8eb8451eb0d4d8/distributed-2026.1.1-py3-none-any.whl", hash = "sha256:506759b1ed88e45e12ba65e2a429de9911862db55d27dd8bb293c6268430374e", size = 1008417 }, +] + +[[package]] +name = "docrep" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/8e/250fab1cafeea43f4eb11f1d64cd6313f639965ff62cb0d9da3883655781/docrep-0.3.2.tar.gz", hash = "sha256:ed8a17e201abd829ef8da78a0b6f4d51fb99a4cbd0554adbed3309297f964314", size = 33197 } + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "donfig" +version = "0.8.1.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/71/80cc718ff6d7abfbabacb1f57aaa42e9c1552bfdd01e64ddd704e4a03638/donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52", size = 19506 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl", hash = "sha256:2a3175ce74a06109ff9307d90a230f81215cbac9a751f4d1c6194644b8204f9d", size = 21592 }, +] + +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708 }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, +] + +[[package]] +name = "fast-array-utils" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/ff/93edc904b05a3260d5a690ac6dcdcd3cce10065b6fb56cdc683f80969456/fast_array_utils-1.3.1.tar.gz", hash = "sha256:34d175a63e9208c6fcbcb3cc18f75480ecdeaeed248759da0e74ab8fbcf55808", size = 330367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/cb/ddcf4ad63ba88df95025837e35faf6ea6862bab1327f836801ba02140a22/fast_array_utils-1.3.1-py3-none-any.whl", hash = "sha256:7617322b29c9f3a8e4c046355ecf653bbee581245787243ea06212a1a56fa611", size = 36518 }, +] + +[[package]] +name = "fast-array-utils" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/86/7345025275bd19b1303bb6267133982f4e95309f85a39529036566cf3b29/fast_array_utils-1.4.1.tar.gz", hash = "sha256:466512aa0e19ebfb2f8d7ae8736fc81b5724e9bf1a7958474206c17b8e991e9c", size = 336422 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/28/aad6d6b63085766c994cf50f2e1f7bd514bd20eb149ad15477db8339420a/fast_array_utils-1.4.1-py3-none-any.whl", hash = "sha256:cad55a58359cac4084445d801b50904357093a94863aca7ff7118bd4bd38ce7c", size = 39859 }, +] + +[package.optional-dependencies] +accel = [ + { name = "numba", marker = "python_full_version >= '3.12'" }, +] +sparse = [ + { name = "scipy", marker = "python_full_version >= '3.12'" }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812 }, +] + +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793 }, + { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130 }, + { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952 }, + { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308 }, + { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932 }, + { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271 }, + { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473 }, + { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389 }, + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131 }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704 }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298 }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800 }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666 }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598 }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575 }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211 }, + { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062 }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594 }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840 }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801 }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009 }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892 }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313 }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299 }, + { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338 }, + { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661 }, + { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526 }, + { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946 }, + { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489 }, + { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870 }, + { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026 }, + { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454 }, + { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152 }, + { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809 }, + { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649 }, + { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147 }, + { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237 }, + { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933 }, + { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326 }, + { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829 }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562 }, +] + +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912 }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046 }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119 }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067 }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160 }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544 }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797 }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886 }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731 }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544 }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806 }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382 }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647 }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064 }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937 }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782 }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594 }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448 }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411 }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014 }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909 }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049 }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485 }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619 }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320 }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820 }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518 }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096 }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985 }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591 }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102 }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717 }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651 }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417 }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391 }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048 }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549 }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363 }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314 }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365 }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763 }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110 }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717 }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235 }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742 }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725 }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506 }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161 }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676 }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638 }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067 }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901 }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395 }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659 }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127 }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698 }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749 }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298 }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015 }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038 }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130 }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845 }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131 }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542 }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308 }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210 }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536 }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330 }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627 }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238 }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738 }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739 }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186 }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196 }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830 }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289 }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318 }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814 }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762 }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470 }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042 }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148 }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451 }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, +] + +[[package]] +name = "fsspec" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] +s3 = [ + { name = "s3fs" }, +] + +[[package]] +name = "geopandas" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyogrio" }, + { name = "pyproj" }, + { name = "shapely" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/ba/8e6b2091878e99e86a36a814dcaeff652ed48bdb03d53e78e15aaa63a914/geopandas-1.1.3.tar.gz", hash = "sha256:91a31989b6f566012838d21d5f8033f37dce882079ccb7cfdc40d5ccce7f284f", size = 336718 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/78/6a04792ace63a93e162f1305392d500ae8ddcb620e7eb88a22fd622b35bb/geopandas-1.1.3-py3-none-any.whl", hash = "sha256:90d62a64f95eaa3be2ccc115c5f3d6e24208bb11983b390fdc0621a3eccd0230", size = 342514 }, +] + +[[package]] +name = "google-crc32c" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/ef/21ccfaab3d5078d41efe8612e0ed0bfc9ce22475de074162a91a25f7980d/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8", size = 31298 }, + { url = "https://files.pythonhosted.org/packages/c5/b8/f8413d3f4b676136e965e764ceedec904fe38ae8de0cdc52a12d8eb1096e/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7", size = 30872 }, + { url = "https://files.pythonhosted.org/packages/f6/fd/33aa4ec62b290477181c55bb1c9302c9698c58c0ce9a6ab4874abc8b0d60/google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15", size = 33243 }, + { url = "https://files.pythonhosted.org/packages/71/03/4820b3bd99c9653d1a5210cb32f9ba4da9681619b4d35b6a052432df4773/google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a", size = 33608 }, + { url = "https://files.pythonhosted.org/packages/7c/43/acf61476a11437bf9733fb2f70599b1ced11ec7ed9ea760fdd9a77d0c619/google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2", size = 34439 }, + { url = "https://files.pythonhosted.org/packages/e9/5f/7307325b1198b59324c0fa9807cafb551afb65e831699f2ce211ad5c8240/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113", size = 31300 }, + { url = "https://files.pythonhosted.org/packages/21/8e/58c0d5d86e2220e6a37befe7e6a94dd2f6006044b1a33edf1ff6d9f7e319/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb", size = 30867 }, + { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364 }, + { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740 }, + { url = "https://files.pythonhosted.org/packages/df/c0/87c2073e0c72515bb8733d4eef7b21548e8d189f094b5dad20b0ecaf64f6/google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962", size = 34437 }, + { url = "https://files.pythonhosted.org/packages/d1/db/000f15b41724589b0e7bc24bc7a8967898d8d3bc8caf64c513d91ef1f6c0/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b", size = 31297 }, + { url = "https://files.pythonhosted.org/packages/d7/0d/8ebed0c39c53a7e838e2a486da8abb0e52de135f1b376ae2f0b160eb4c1a/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27", size = 30867 }, + { url = "https://files.pythonhosted.org/packages/ce/42/b468aec74a0354b34c8cbf748db20d6e350a68a2b0912e128cabee49806c/google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa", size = 33344 }, + { url = "https://files.pythonhosted.org/packages/1c/e8/b33784d6fc77fb5062a8a7854e43e1e618b87d5ddf610a88025e4de6226e/google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8", size = 33694 }, + { url = "https://files.pythonhosted.org/packages/92/b1/d3cbd4d988afb3d8e4db94ca953df429ed6db7282ed0e700d25e6c7bfc8d/google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f", size = 34435 }, + { url = "https://files.pythonhosted.org/packages/21/88/8ecf3c2b864a490b9e7010c84fd203ec8cf3b280651106a3a74dd1b0ca72/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697", size = 31301 }, + { url = "https://files.pythonhosted.org/packages/36/c6/f7ff6c11f5ca215d9f43d3629163727a272eabc356e5c9b2853df2bfe965/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651", size = 30868 }, + { url = "https://files.pythonhosted.org/packages/56/15/c25671c7aad70f8179d858c55a6ae8404902abe0cdcf32a29d581792b491/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2", size = 33381 }, + { url = "https://files.pythonhosted.org/packages/42/fa/f50f51260d7b0ef5d4898af122d8a7ec5a84e2984f676f746445f783705f/google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21", size = 33734 }, + { url = "https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2", size = 34878 }, + { url = "https://files.pythonhosted.org/packages/52/c5/c171e4d8c44fec1422d801a6d2e5d7ddabd733eeda505c79730ee9607f07/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93", size = 28615 }, + { url = "https://files.pythonhosted.org/packages/9c/97/7d75fe37a7a6ed171a2cf17117177e7aab7e6e0d115858741b41e9dd4254/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c", size = 28800 }, +] + +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/3c/ff890b466eaba2b0f5e6bdfff025f8c75f41b8ffdc3dbc3d24ad261e764a/greenlet-3.5.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:73f78f9b9f0a5c06e5c946ba1e8e36f5114923b6be109ee618c54f079c3ea14f", size = 284764 }, + { url = "https://files.pythonhosted.org/packages/81/0e/5e5457be3d256918f6a4756f073548a3f0190836e2cc94aa6d0d617a940b/greenlet-3.5.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0cbed8bb44e23c5b199f888f4e4ce096b45ad9f25ff74a7ad0213875e936bb2", size = 603479 }, + { url = "https://files.pythonhosted.org/packages/6d/e1/f89a21d58d308298e6f275f13a1b472ed96c680b601a371b08be6a725989/greenlet-3.5.1-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a203a8bd0acb0701653d3bbb26e404854a68674139ed5cbb778830f42b09bb33", size = 615495 }, + { url = "https://files.pythonhosted.org/packages/2c/f2/8fd452fd81adb9ec79c8275c1375702ab0fd6bee4952da12eaa09b9508d8/greenlet-3.5.1-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ebeb75c81211f5c702576cf81f315e77e23cfdb2c7c6fcb9dd143e6de35c360", size = 623515 }, + { url = "https://files.pythonhosted.org/packages/75/de/af6cef182862d2ccd6975440d21c9058a77c3f9b469abf94e322dfd2e0e3/greenlet-3.5.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a271fcd66c74615cda6a964fda3f304267a12e50a084472218a39bb0376f563", size = 614754 }, + { url = "https://files.pythonhosted.org/packages/ec/bc/c318aa9f3ffc77320fddcee3d892be957b42e2ff947198d9450b004f3a38/greenlet-3.5.1-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:017a544f0385d441e88714160d089d6900ef46c9eff9d99b6715a5ef2d127747", size = 418439 }, + { url = "https://files.pythonhosted.org/packages/1a/c6/50e520283a9f19388a7326b05f9e8637e566003475eacaadad04f558c68d/greenlet-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ded7b068c7c31c1a8657d4fd42d886b3e051ae29f88b80c5ff9d502257b0f071", size = 1574097 }, + { url = "https://files.pythonhosted.org/packages/21/1c/13abd1f4860d987fa5e1170a01930d6e6cd40d328de487a3c9fdaff0ffd0/greenlet-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0932b81d72f552ded9d810d00021b64d89f2195a91ce115b893f943b7a4ab3c", size = 1641058 }, + { url = "https://files.pythonhosted.org/packages/f5/56/5f332b7705545eac2dc01b4e9254d24a793f2656d55d5cc6b94ee59d22ae/greenlet-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:88e300d136eac057b2397aa1cfd7328b4c87c7eb66a09c7bc6a1292234db474e", size = 238089 }, + { url = "https://files.pythonhosted.org/packages/d9/a9/a3c2fa886c5b94863fb0e61b3bc14610b7aa94cf4f17f8741b11708305fc/greenlet-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:cc6ab7e555c8a112ad3a76e368e86e12a2754bcae1652a5602e133ec7b635523", size = 234989 }, + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220 }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585 }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215 }, + { url = "https://files.pythonhosted.org/packages/7c/6c/de5b1b388cd2d9fbdfeab324863daba37d54e6e233ddbefd70b385a8c591/greenlet-3.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89101bfd5011e069be974903cb3a4e4523845e4ece2d62dcd8d358933c0ef249", size = 620094 }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358 }, + { url = "https://files.pythonhosted.org/packages/4a/43/1204baffab8a6476464795a7ccf394a3248d4f22c9f87173a15b36b6d971/greenlet-3.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e6cd99ea59dd5d89f0c956606571d79bfe6f68c9eb7f4a4083a41a7f1587edee", size = 422782 }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475 }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625 }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791 }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580 }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060 }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370 }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987 }, + { url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911 }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911 }, + { url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228 }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689 }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602 }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683 }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499 }, + { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579 }, + { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106 }, + { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478 }, + { url = "https://files.pythonhosted.org/packages/c6/2d/2d80842910da44f78c286532d084b8a5c3717c844ae80ceb3858738ae89a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c09df69dc1712d131332054a858a3e5cca400967fa3a672e2324fbb0971448c", size = 667767 }, + { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800 }, + { url = "https://files.pythonhosted.org/packages/e9/d3/dad2eecedfbb1ed7050a20dcfae40c1442b74bc7423608be2c7e03ee7133/greenlet-3.5.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:a4764e0bfc6a4d114c865b32520805c16a990ef5f286a514413b05d5ecd6a23d", size = 470786 }, + { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518 }, + { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593 }, + { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800 }, + { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862 }, + { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637 }, + { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840 }, + { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416 }, + { url = "https://files.pythonhosted.org/packages/8c/46/5987dcd1a2570ba84f3b187536b2ca3ae97613387e57f5cfa99df068fe5e/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea37d5a157eb9493820d3792ac4ece28619a394391d2b9f2f78057d396ff0f0f", size = 656607 }, + { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397 }, + { url = "https://files.pythonhosted.org/packages/2c/c1/6da0a9ddcc29d7e51ef14883fa3dc1e53b3f4ffba00582106c7bf55da1d8/greenlet-3.5.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:8d8a23250ea3ec7b36de8fa4b541e9e2db3ee82915cc060ab0631609ad8b28de", size = 488287 }, + { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469 }, + { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115 }, + { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246 }, + { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575 }, + { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428 }, + { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064 }, + { url = "https://files.pythonhosted.org/packages/dc/74/807a047255bf1e09303627c46dc043dca596b6958a354d904f32ab382005/greenlet-3.5.1-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:10a9a1c0bfbc93d41156ffcb90c75fbc05544054faf15dcc1fdf9765f8b607f0", size = 672962 }, + { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697 }, + { url = "https://files.pythonhosted.org/packages/76/32/19d4e13225193c29b13e308015223f7d75fd3d8623d49dd19040d2ce8ec1/greenlet-3.5.1-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:ef08c1567c78074b22d1a200183d52d04a14df447bf70bcbb6a3507a48e776fc", size = 476047 }, + { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256 }, + { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956 }, + { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802 }, + { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853 }, + { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877 }, + { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333 }, + { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443 }, + { url = "https://files.pythonhosted.org/packages/c9/9d/1dcdf7b95ab3cf8c7b6d7277c18a5e167312f2b362ddfcc5d5e6d8d84b43/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a57b0d05a0448eed231d59c0ceb287dde984551e54cbc51ac2d4865712838e9c", size = 659998 }, + { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356 }, + { url = "https://files.pythonhosted.org/packages/05/7e/c4959664fc231d587d66d8e81f2095e98056ba1954beafdcbe635e251052/greenlet-3.5.1-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:b0703c2cef53e01baec47f7a3868009913ad71ec678bbecb42a6f40895e4ce62", size = 494470 }, + { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216 }, + { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427 }, + { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225 }, + { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590 }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663 }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630 }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472 }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150 }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544 }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013 }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673 }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834 }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604 }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940 }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852 }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250 }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108 }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216 }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868 }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286 }, + { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808 }, + { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837 }, + { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860 }, + { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417 }, + { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214 }, + { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598 }, + { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509 }, + { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362 }, + { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608 }, + { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773 }, + { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886 }, + { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883 }, + { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039 }, + { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526 }, + { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263 }, + { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450 }, + { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693 }, + { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305 }, + { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061 }, + { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216 }, + { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068 }, + { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253 }, + { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671 }, + { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706 }, +] + +[[package]] +name = "hatch" +version = "1.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-zstd", marker = "python_full_version < '3.14'" }, + { name = "click" }, + { name = "hatchling" }, + { name = "httpx" }, + { name = "hyperlink" }, + { name = "keyring" }, + { name = "packaging" }, + { name = "pexpect" }, + { name = "platformdirs" }, + { name = "pyproject-hooks" }, + { name = "python-discovery" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "tomli-w" }, + { name = "tomlkit" }, + { name = "userpath" }, + { name = "uv" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/02/ce9c4c439fa3f195b21b4b5bb18b44d1076297c86477ef7e3d2de6064ec3/hatch-1.16.5.tar.gz", hash = "sha256:57bdeeaa72577859ce37091a5449583875331c06f9cb6af9077947ad40b3a1de", size = 5220741 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/8a/11ae7e271870f0ad8fa0012e4265982bebe0fdc21766b161fb8b8fc3aefc/hatch-1.16.5-py3-none-any.whl", hash = "sha256:d9b8047f2cd10d3349eb6e8f278ad728a04f91495aace305c257d5c2747188fb", size = 141269 }, +] + +[[package]] +name = "hatchling" +version = "1.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/b4cfe330cd4f49cff17fd771154730555fa4123beb7f292cf0098b4e6c20/hatchling-1.29.0.tar.gz", hash = "sha256:793c31816d952cee405b83488ce001c719f325d9cda69f1fc4cd750527640ea6", size = 55656 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/8a/44032265776062a89171285ede55a0bdaadc8ac00f27f0512a71a9e3e1c8/hatchling-1.29.0-py3-none-any.whl", hash = "sha256:50af9343281f34785fab12da82e445ed987a6efb34fd8c2fc0f6e6630dbcc1b0", size = 76356 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "hyperlink" +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/51/1947bd81d75af87e3bb9e34593a4cf118115a8feb451ce7a69044ef1412e/hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b", size = 140743 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/aa/8caf6a0a3e62863cbb9dab27135660acba46903b703e224f14f447e57934/hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4", size = 74638 }, +] + +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397 }, +] + +[[package]] +name = "idna" +version = "3.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316 }, +] + +[[package]] +name = "igraph" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "texttable" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/be/56bef1919005b4caf1f71522b300d359f7faeb7ae93a3b0baa9b4f146a87/igraph-1.0.0.tar.gz", hash = "sha256:2414d0be2e4d77ee5357807d100974b40f6082bb1bb71988ec46cfb6728651ee", size = 5077105 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/03/3278ad0ceb3ea0e84d8ae3a85bdded4d0e57853aeb802a200feb43847b93/igraph-1.0.0-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:c2cbc415e02523e5a241eecee82319080bf928a70b1ba299f3b3e25bf029b6d4", size = 2257415 }, + { url = "https://files.pythonhosted.org/packages/0d/bc/6281ec7f9baaf71ee57c3b1748da2d3148d15d253e1a03006f204aa68ca5/igraph-1.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a27753cd80680a8f676c2d5a467aaa4a95e510b30748398ec4e4aeb982130e8", size = 2048555 }, + { url = "https://files.pythonhosted.org/packages/2a/38/3cd6428a4ed4c09a56df05998438e7774fd1d799ee4fb8fc481674f5f7fc/igraph-1.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a55dc3a2a4e3fc3eba42479910c1511bfc3ecb33cdf5f0406891fd85f14b5aee", size = 5314141 }, + { url = "https://files.pythonhosted.org/packages/7d/da/dd2867c25adbb41563720f14b5fc895c98bf88be682a3faff4f7b3118d2a/igraph-1.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d04c2c76f686fb1f554ee35dfd3085f5e73b7965ba6b4cf06d53e66b1955522", size = 5683134 }, + { url = "https://files.pythonhosted.org/packages/e5/40/243c118d34ab80382d7009c4dcb99b887384c3d2ce84d29eeac19e2a007a/igraph-1.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2b52dc1757fff0fed29a9f7a276d971a11db4211569ed78b9eab36288dfcc9d", size = 6211583 }, + { url = "https://files.pythonhosted.org/packages/1d/b7/88f433819c54b496cb0315fce28e658970cb20ff5dbd52a5a605ce2888de/igraph-1.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:05c79a2a8fca695b2f217a6fa7f2549f896f757d4db41be32a055400cb19cc30", size = 6594509 }, + { url = "https://files.pythonhosted.org/packages/7b/5d/8f7f6f619d374e959aa3664ebc4b24c10abc90c2e8efbed97f2623fadaf5/igraph-1.0.0-cp39-abi3-win32.whl", hash = "sha256:c2bce3cd472fec3dd9c4d8a3ea5b6b9be65fb30edf760beb4850760dd4f2d479", size = 2725406 }, + { url = "https://files.pythonhosted.org/packages/af/77/a85b3745cf40a0572bae2de8cd9c2a2a8af78e5cf3e880fc0a249114e609/igraph-1.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:faeff8ede0cf15eb4ded44b0fcea6e1886740146e60504c24ad2da14e0939563", size = 3221663 }, + { url = "https://files.pythonhosted.org/packages/ef/7e/5df541c37bdf6493035e89c22bd53f30d99b291bcda6c78e9a8afeecec2b/igraph-1.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:b607cafc24b10a615e713ee96e58208ef27e0764af80140c7cc45d4724a3f2df", size = 2785701 }, + { url = "https://files.pythonhosted.org/packages/b9/73/bf1d4dbbc9123435b3ca14bb608b243a50a4f158ecea564bf196715248d9/igraph-1.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3189c1a8e8a8f58009f3f729040eb3701254d074ed37245691d529869ec940c5", size = 2246636 }, + { url = "https://files.pythonhosted.org/packages/59/ac/28482f2af45cc0a0ca88a69d17a6ea694f58bdbd22cc876e7273a0379282/igraph-1.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ebe9502689b946301584b3cfacdbc70c58c4d664d804e39b6daa31be5c20bf46", size = 2036101 }, + { url = "https://files.pythonhosted.org/packages/56/80/806a093df1d1ddc3b30d0418b1ee56388ae7018f8ae288677ee2b3a1abaf/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f117683108c54330d6dc67a708e3724c13c9989885122a29781296872989a222", size = 3053403 }, + { url = "https://files.pythonhosted.org/packages/56/bf/cf7aeff230a4368c0b8bc6b02f3ea27db41db33714b51e1e8a7c1458f31b/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:077dbff0edb8b4ce0f9fefdf325200346d9d5db02de31872b41743de08e67a16", size = 3262472 }, + { url = "https://files.pythonhosted.org/packages/d8/ca/dbc06072d5eea402a6dc81f387afb1b7e0c415f1d8a75232943fc4d1bfdb/igraph-1.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fe7c693b2a84a4e03ca31e65aa05a2ecd8728137fa9909ccbf6453b4200b856d", size = 3218861 }, +] + +[[package]] +name = "imagecodecs" +version = "2025.11.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/7f/5842fd3e5bb8195d3b3af12af82a5c1d3280611cb01ffd02edec57e55b9e/imagecodecs-2025.11.11.tar.gz", hash = "sha256:a836d329f91c890d17b9dc3f3a2bba944a198ae741592c3cb0c4b9b097d10d34", size = 9505589 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/61/4017d04559736e903cb972bfaac0dc464b2d7bd945d9070cfa2526b7596e/imagecodecs-2025.11.11-cp311-abi3-macosx_10_15_x86_64.whl", hash = "sha256:f5d01cc85ca2fe354302b3650f4848e8f1d3174200277b7cb2624abed4d02ea5", size = 12148485 }, + { url = "https://files.pythonhosted.org/packages/8f/27/0494b31c65a4dbb09206962f7a056ff9df5388ab13e1dddc27ecf47238df/imagecodecs-2025.11.11-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:45a865690275a3a8893c4c54377c678a7b361ebe13dd3a9e22c66443a96b1c2a", size = 10151325 }, + { url = "https://files.pythonhosted.org/packages/ca/5e/d98ce5608a48efff8f07139b11e4e76f7a05471dc327fb28e696afa7504c/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be6e2f4b6601b57cfc9fba2382da7e02ad43c44e3b037adbf275a11f688b231f", size = 22366018 }, + { url = "https://files.pythonhosted.org/packages/a8/ac/fde1f9f99ebe7d8b989bb0005883b69f35abd6ac2d677e0a0caf23f29961/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4a3894b62ca6ad0c4d75cb0a0aac2a525c4a5c4a2a60defc5d7fb716a4bb355", size = 23200222 }, + { url = "https://files.pythonhosted.org/packages/30/fe/ea18b0514128a60830c4909e23add385f8e799ff9719bc0b5de82673baa1/imagecodecs-2025.11.11-cp311-abi3-win32.whl", hash = "sha256:3735ea088321b0685e593c5d44fc1d146a84fcf6645c52ed136fa576e16c3572", size = 14847479 }, + { url = "https://files.pythonhosted.org/packages/01/b8/287a2d64f746e3bdf44693d2013b5fb7440eafe1dca08a35a0fd30458d33/imagecodecs-2025.11.11-cp311-abi3-win_amd64.whl", hash = "sha256:4252b15f5ba55bc7d7aacf621f6bb6e73fd70e66d56277a1f26563198b2cd576", size = 19052271 }, + { url = "https://files.pythonhosted.org/packages/a3/c8/413154b6014d9e1ee2b717bfdfa6617e81557b44487e2fb2a88ca3da125b/imagecodecs-2025.11.11-cp311-abi3-win_arm64.whl", hash = "sha256:f4480973fcf2d826ba6806b7ac6f4d9bd72eeb557cfd3687ac885939d130ec47", size = 14529653 }, + { url = "https://files.pythonhosted.org/packages/8c/2e/6a7117f2fe8e83c6ed58ad237625e4e82e139f67ce4695c457789e43cdc5/imagecodecs-2025.11.11-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bb0f4714e60dd94b94ca778b872fb90b885b83381228743903035d79b7e91a88", size = 12448096 }, + { url = "https://files.pythonhosted.org/packages/24/96/0c479746198723c53326c65edf56331d8e910afaa2fa132358ca8d5a6177/imagecodecs-2025.11.11-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:faf4ab8f2f11694aa2d6ed7548ce1d1598a14e326a6bb3d50669ed5c7da764c6", size = 10433730 }, + { url = "https://files.pythonhosted.org/packages/b4/19/de48587d37aa2bc355313abdfdda801c7a26235ec66bfad19ff3ee0b7b23/imagecodecs-2025.11.11-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdb475f54d0abac3cfcde42ec47f606eea1d4afa52173420577317102d361ae5", size = 25835500 }, + { url = "https://files.pythonhosted.org/packages/17/47/26edea4799c1250ad34305544fb69a65d6151dbe623508ed71537a678e7e/imagecodecs-2025.11.11-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e3d4cb2dc507c0bab057321333927575d1d611efe368e76b752f5ad3c55856ad", size = 25952929 }, + { url = "https://files.pythonhosted.org/packages/31/75/d720db7e0d654d3fd0db253ed8b8a83fad6d87735132fb01326c4f720fd7/imagecodecs-2025.11.11-cp314-cp314t-win32.whl", hash = "sha256:7bcc214d6f6ecd2bd7b648a6ac5589edcde946bcb738b6c763ce1c864934c58c", size = 15467675 }, + { url = "https://files.pythonhosted.org/packages/c0/fd/1a1eb89f9d1f9256d0fbb9f328607a1215710e19d4086bc602e8543423e4/imagecodecs-2025.11.11-cp314-cp314t-win_amd64.whl", hash = "sha256:58dbc0095142dcef55aaac392ee5bd8fb7c3656753e3c37424c3612209c656a4", size = 19915854 }, + { url = "https://files.pythonhosted.org/packages/59/27/2fa20c89149eed89949c34654bb3643f2aa229492e05fbd2ffadb5e4dc41/imagecodecs-2025.11.11-cp314-cp314t-win_arm64.whl", hash = "sha256:dd61871455ae6c3486051e68e0d576d54e53c45d157ce233c4a09269f132bf48", size = 15098090 }, +] + +[[package]] +name = "imageio" +version = "2.37.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646 }, +] + +[[package]] +name = "imagesize" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441 }, +] + +[[package]] +name = "importlib-metadata" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789 }, +] + +[[package]] +name = "inflect" +version = "7.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, + { name = "typeguard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197 }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, +] + +[[package]] +name = "ipykernel" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788 }, +] + +[[package]] +name = "ipython" +version = "9.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "psutil" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, +] + +[[package]] +name = "isoduration" +version = "20.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871 }, +] + +[[package]] +name = "jaraco-functools" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594 }, +] + +[[package]] +name = "jax" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jaxlib" }, + { name = "ml-dtypes" }, + { name = "numpy" }, + { name = "opt-einsum" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/49/b082387119c4a6bc7596296bbdc6bce034628cdd2845ebb27304cbca3624/jax-0.10.1.tar.gz", hash = "sha256:11672410faf8752429eb9a131de203dc488a2a3a012d509baa2b39878008810d", size = 2718178 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/6e/5087e0347188f6970aba1ffbd0018754d23c3f3461e9f21785f2f27a02c2/jax-0.10.1-py3-none-any.whl", hash = "sha256:47f3192c76e9e3358de1b106a8af5e943fccb10510903f25d96ea53652729134", size = 3150973 }, +] + +[[package]] +name = "jaxlib" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ml-dtypes" }, + { name = "numpy" }, + { name = "scipy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e9/43253041a0b28f64a99dfa8a6823de415b1b9723dc8eb8718945bb957b1c/jaxlib-0.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff0c82cb958d180d26687459962e262b6e1c90f239903c8253cc190067124db4", size = 60802893 }, + { url = "https://files.pythonhosted.org/packages/f2/dd/fe5e06087d79ef014a10fc4d5acba76f585278eb54cdbeb880347600a41c/jaxlib-0.10.1-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:349b131c8b393fe5de2aabec1539f18c6a6ce85680b145c78b068fc56d498472", size = 80280956 }, + { url = "https://files.pythonhosted.org/packages/33/89/8c6dff28eaf86a917622e427caf045ecb85772eaa1679937915e4e5ac8d6/jaxlib-0.10.1-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9c2f0d6b910e93dd47bbfc3aa54c83950ff6a05955913892d31b034337d89420", size = 85810989 }, + { url = "https://files.pythonhosted.org/packages/83/fd/6b705478e19af3dd384351c91f4b469fef83bb1fa070c45a937ae7dcc335/jaxlib-0.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f49375e63c4486295e31d3f93b4abeb0a69183cb78484299b135c74bc036ec4c", size = 64802918 }, + { url = "https://files.pythonhosted.org/packages/53/b4/fdb6e989b142d8a8d2093f342cbc5323fe0d4a7217fd899c8ddf9e108a5a/jaxlib-0.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7a4399c7429c87ee6f7ab1e5712c5548ecfabde974ee9f4a12957fea4e35efb8", size = 60816137 }, + { url = "https://files.pythonhosted.org/packages/a7/29/0b4eaaca005708751ff301903a2ca760dcc34175dadb7536498c57a7de85/jaxlib-0.10.1-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:12126603ba472300c62480f86d20972d563143041039d9dea349ad510aa6123c", size = 80294034 }, + { url = "https://files.pythonhosted.org/packages/38/69/2912ab63036e21c72748019e1d8e09e8a1fc3368b3e83fc27898a1858575/jaxlib-0.10.1-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:f3cdf5b7f48470ab5455ab79aab746419694ccb6b52651cc2ce5fb27def03588", size = 85828774 }, + { url = "https://files.pythonhosted.org/packages/ec/8f/993ea419eca6f34fe12613e22a03b93f40e5b1e8e0df18d4060e1313a1fc/jaxlib-0.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:0acf3f8e7dca9074c0327f0f61502845792ca9f82fab23b841b00daa78e85488", size = 64830187 }, + { url = "https://files.pythonhosted.org/packages/cf/76/3b637d4def229015a3035a7b44fac0dcf2536ae337540cdbffc651334d4e/jaxlib-0.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4167213aa00f14bb0d8fbd90f9ded75e976f71ce8baf8c3c44e04c8fb80ea0c1", size = 60815855 }, + { url = "https://files.pythonhosted.org/packages/5b/76/9a1971bc9edb8728a7ba86d2693127ee46add9811230b8452321415fd4e9/jaxlib-0.10.1-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:e53223d8f6861d33c02dd02343fa464700401ff5363784d37c33407218e328b8", size = 80293947 }, + { url = "https://files.pythonhosted.org/packages/20/1d/69a0ba52fb546261e71a7209378ee6059950e9c088a2a18355e01509f474/jaxlib-0.10.1-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:bb073a1224e659e01e8d32d47c000edb52ec2aa8ba97ec22b2228b3a46e5c167", size = 85829861 }, + { url = "https://files.pythonhosted.org/packages/a9/df/48659e2ee57705c63a51525f810fe3e0c87af4ca9f89d4738281a872d58e/jaxlib-0.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:6449f1d4a22324f5f02c843360475783f9fd1d353fe711806cbf4e927d1360ae", size = 64828863 }, + { url = "https://files.pythonhosted.org/packages/c7/34/3f7c95ee1b2555d611f836988a49b522c04b8d186e0528f91d45118089bf/jaxlib-0.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:72a7db1242d6b7773340e430c244e73a8d13f82ce83933c0529a8b4b1520dcf3", size = 60934063 }, + { url = "https://files.pythonhosted.org/packages/3a/84/855a2395d299e00e1d9ffd0aecd516b52b81780f3e4ef537527be6d8c1fc/jaxlib-0.10.1-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:649c26ca92e9bbffb3c35226b37893916f20e6b89fb3911ce79b39e5cfb27b46", size = 80402500 }, + { url = "https://files.pythonhosted.org/packages/be/35/153e91a9c770a981d525d845b3f4cdb71a1e119681594a33908e9536bdff/jaxlib-0.10.1-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:cfe75d8a17e0d33a7bed27f32d7a5344a66e8d4af7073973f396e14ff4a9c503", size = 85941210 }, + { url = "https://files.pythonhosted.org/packages/0b/a1/c4d4c0530313c50dd1ba07fff480cfd0c5f18c5ec49742f4a52a6edfd95f/jaxlib-0.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9a657554dbe56f3691d377cb99d00b29df14e5638e579e57965f9f69c32c9315", size = 60826189 }, + { url = "https://files.pythonhosted.org/packages/98/71/529b2439b88491e0806ee9fa6191ecf90f4447dfe092e80bd19577c85260/jaxlib-0.10.1-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:93cd9989404f86a21b50c7ff4e850a31f55509664312080978bde97a664d92a9", size = 80300654 }, + { url = "https://files.pythonhosted.org/packages/be/08/0bc4132fb2fe224ee9d83fd60e3650fd4d893b4e5148707a98df8f0333a4/jaxlib-0.10.1-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:26b94e9640b01968cc14b8353dd6b6540d723f30579c78b1f46a477fc4aa196d", size = 85841241 }, + { url = "https://files.pythonhosted.org/packages/5c/de/423d748ce3367bd5ea20d8cc34a7ceb6420da4d41c20b247f54194700d04/jaxlib-0.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:375820799bbf7d515dd4e4d40f3334566b73d3fe64d340afbd6aa897d5d7c486", size = 67301520 }, + { url = "https://files.pythonhosted.org/packages/2d/bf/3f7ce089d62f7ac85ea678925471f7ec88038899e67ab02079c1e7d8ad4e/jaxlib-0.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bc52f16bdd61b299efeed7ea8e743e91a118059a463da2ab97196fc05b69ddb7", size = 60935393 }, + { url = "https://files.pythonhosted.org/packages/65/6a/38cf1d4ff8c8f74ec8d567e0aa6e3d2082ab6fc580545f6bb51368da20a9/jaxlib-0.10.1-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:55b0a473fbd57d31dc3935c4cd5c0c38af5f7c1f41300df27923cf46676972ca", size = 80405741 }, + { url = "https://files.pythonhosted.org/packages/16/9e/d3cff171aaf13a09aab26a44d1a27dcbf0d6311e4d855f5d99685965ace3/jaxlib-0.10.1-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:4b0cb8ef960a3723037db63f28ffa20083d90fff6d30085e99d7c63cfa08e4c0", size = 85942183 }, +] + +[[package]] +name = "jedi" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812 }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419 }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, +] + +[[package]] +name = "json5" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271 }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659 }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, +] + +[package.optional-dependencies] +format-nongpl = [ + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "rfc3987-syntax" }, + { name = "uri-template" }, + { name = "webcolors" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, +] + +[[package]] +name = "jupyter-cache" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "click" }, + { name = "importlib-metadata" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "pyyaml" }, + { name = "sqlalchemy" }, + { name = "tabulate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/f7/3627358075f183956e8c4974603232b03afd4ddc7baf72c2bc9fff522291/jupyter_cache-1.0.1.tar.gz", hash = "sha256:16e808eb19e3fb67a223db906e131ea6e01f03aa27f49a7214ce6a5fec186fb9", size = 32048 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/6b/67b87da9d36bff9df7d0efbd1a325fa372a43be7158effaf43ed7b22341d/jupyter_cache-1.0.1-py3-none-any.whl", hash = "sha256:9c3cafd825ba7da8b5830485343091143dff903e4d8c69db9349b728b140abf6", size = 33907 }, +] + +[[package]] +name = "jupyter-client" +version = "8.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371 }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, +] + +[[package]] +name = "jupyter-events" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/6c/6fcde0c8f616ed360ffd3587f7db9e225a7e62b583a04494d2f069cf64ea/jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf", size = 19512 }, +] + +[[package]] +name = "jupyter-lsp" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/e8/9d61dcbd1dce8ef418f06befd4ac084b4720429c26b0b1222bc218685eff/jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81", size = 77513 }, +] + +[[package]] +name = "jupyter-server" +version = "2.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "overrides", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "prometheus-client" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/a0/eb3c511f54df7b54ca5fc7bff3f4d2277d69052d6a7f521643dfed5279d6/jupyter_server-2.19.0.tar.gz", hash = "sha256:1731236bc32b680223e1ceb9d68209a845203475012ef68773a81434b46a31a7", size = 754561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/78/d2881e68894cecdcd05912a9c585cfb776ef1fb38b62c8dba98f12ab3adc/jupyter_server-2.19.0-py3-none-any.whl", hash = "sha256:cb76591b76d7093584c2ad2ae72ac3d58614a4b597507a1bb04e1f9f683cf9ea", size = 392244 }, +] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "terminado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, +] + +[[package]] +name = "jupyterlab" +version = "4.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-lru" }, + { name = "httpx" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "packaging" }, + { name = "setuptools" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/22/8440ec827762146e7cdecf04335bd348795899d29dc6ae82238707353a2c/jupyterlab-4.5.7.tar.gz", hash = "sha256:55a9822c4754da305f41e113452c68383e214dcf96de760146af89ce5d5117b0", size = 23992763 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/aa/537b8f7d80e799af19af35fb3ddfc970b951088a13c57dd9387dcfbb7f61/jupyterlab-4.5.7-py3-none-any.whl", hash = "sha256:fba4cb0e2c44a52859669d8c98b45de029d5e515f8407bf8534d2a8fc5f0964d", size = 12450123 }, +] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, +] + +[[package]] +name = "jupyterlab-server" +version = "2.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, +] + +[[package]] +name = "jupytext" +version = "1.19.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/2d/15624c3d9440d85a280ff13d2d23afd989802f25470ac59932f4fef6f0c6/jupytext-1.19.3.tar.gz", hash = "sha256:713c3ed4441afe0f31474d28ea2e6b61a268c04c40fd78e5ccfd7f7ac9e9f766", size = 4305350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/ec/d9be3bd1db141e76b2f525c265f70e66edd30a51a3307d8edf0ef1909c54/jupytext-1.19.3-py3-none-any.whl", hash = "sha256:acf75492f80895ad8e664fd8db1708b617008dd0e71c341a1abc3d0d07310ed0", size = 170579 }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160 }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798 }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216 }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911 }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888 }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304 }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650 }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949 }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783 }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726 }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738 }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718 }, + { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480 }, + { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930 }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158 }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388 }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068 }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934 }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537 }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685 }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024 }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241 }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742 }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966 }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417 }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238 }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947 }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569 }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997 }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166 }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395 }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065 }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903 }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751 }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793 }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041 }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292 }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865 }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369 }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989 }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645 }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237 }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573 }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998 }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700 }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537 }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514 }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848 }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542 }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447 }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918 }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856 }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580 }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018 }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804 }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482 }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328 }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231 }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489 }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063 }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913 }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782 }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815 }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925 }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322 }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857 }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376 }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680 }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905 }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086 }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577 }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794 }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646 }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511 }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858 }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539 }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310 }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244 }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154 }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377 }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288 }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158 }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260 }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403 }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687 }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032 }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532 }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420 }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892 }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603 }, + { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558 }, +] + +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, +] + +[[package]] +name = "latexcodec" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/dd/4270b2c5e2ee49316c3859e62293bd2ea8e382339d63ab7bbe9f39c0ec3b/latexcodec-3.0.1.tar.gz", hash = "sha256:e78a6911cd72f9dec35031c6ec23584de6842bfbc4610a9678868d14cdfb0357", size = 31222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/40/23569737873cc9637fd488606347e9dd92b9fa37ba4fcda1f98ee5219a97/latexcodec-3.0.1-py3-none-any.whl", hash = "sha256:a9eb8200bff693f0437a69581f7579eb6bca25c4193515c09900ce76451e452e", size = 18532 }, +] + +[[package]] +name = "lazy-loader" +version = "0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/ac/21a1f8aa3777f5658576777ea76bfb124b702c520bbe90edf4ae9915eafa/lazy_loader-0.5.tar.gz", hash = "sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3", size = 15294 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044 }, +] + +[[package]] +name = "legacy-api-wrap" +version = "1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/49/f06f94048c8974205730d40beca879e43b6eee08efb0101cfb8623e60f41/legacy_api_wrap-1.5.tar.gz", hash = "sha256:b41ba6532f3ebfe3a897a35a7f97dec3be04b92a450f6c2bcf89f1b91c9cadf2", size = 11610 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/5b/058db09c45ba58a7321bdf2294cae651b37d6fec68117265af90cde043b0/legacy_api_wrap-1.5-py3-none-any.whl", hash = "sha256:5a8ea50e3e3bcbcdec3447b77034fd0d32cb2cf4089db799238708e4d7e0098d", size = 10182 }, +] + +[[package]] +name = "leidenalg" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igraph" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/a5/853e93441aed7f82b0389f86f37e19413e817ba0c54cc790895935256968/leidenalg-0.11.0.tar.gz", hash = "sha256:f454be96bbc8089ea2a90ca853d8d389ab646de964a03bd58417f8b29ff8ef5d", size = 452850 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/a9/4ab4e244215db0c8b626e4bed0d3e0fbd191c52d2d5f5cb9d160139ecc7e/leidenalg-0.11.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5607589050bfc1926e657b4d8a3b5341fe1eb81018c22cf4a3d3a39e368d1fcb", size = 2256514 }, + { url = "https://files.pythonhosted.org/packages/98/f4/98db342d603671ae0a233f0a624939a47161044a2716cbd62a50440a1132/leidenalg-0.11.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b5781876b1f1faed72a4f9926ff52de286843556b9d6791fe25a2acb33b7a5c", size = 1926003 }, + { url = "https://files.pythonhosted.org/packages/9b/38/fd6ac21af10b12828b472eada4fce0edf2a212581238ad0c8d1afebc6f98/leidenalg-0.11.0-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a80f49477f8e793f27d8e08949177f19e3834cd878af50a662b4f87335d06549", size = 2545535 }, + { url = "https://files.pythonhosted.org/packages/77/87/b087584750a788535b4a8d56ddeb82a175d32b472aa5338a4e2cc593a42c/leidenalg-0.11.0-cp38-abi3-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:2143be3e80485584ccbdf927323fce65345da17facd0f8b438f11015f5dc6c27", size = 2845029 }, + { url = "https://files.pythonhosted.org/packages/b0/a4/a89e2ce16a580f7bea066ed49364f0b3e04a6412f0c3692975bee8515141/leidenalg-0.11.0-cp38-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:571a0934f831a69442d82889d319bdba93de924bd9e09b720cd8cbe6fdc08c17", size = 2738084 }, + { url = "https://files.pythonhosted.org/packages/e8/fe/8923cac6cd7c9e0ac5f38aaa69a4744c93d025575763d05f7a3baae8020d/leidenalg-0.11.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec03e7178b19102dd271b453a39b9865cf283b4113151ba60514e5681046294", size = 4070307 }, + { url = "https://files.pythonhosted.org/packages/fe/94/beaab5ee9968f9389f705532c31ffb868bad8a5ce68fb699ddde5ddc5409/leidenalg-0.11.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:310b9269a11fd1e960590c1a2b6ff685a2cc42aa3234ce67bc2a623ab61f26a9", size = 3797863 }, + { url = "https://files.pythonhosted.org/packages/6e/8e/8caf4ba38fd7d8e6197348b348a4ab666b1b3117225ea2f0934a98a93176/leidenalg-0.11.0-cp38-abi3-win32.whl", hash = "sha256:5ea4cd7ee054540112b28f7e2d64658dcccd59f61a5d6a08a41df808645f96e9", size = 1643351 }, + { url = "https://files.pythonhosted.org/packages/47/15/7d459a8e2a43f17c1db129b997b7bb7aa7f000a0967bab87c28b8c5cf448/leidenalg-0.11.0-cp38-abi3-win_amd64.whl", hash = "sha256:5e789c0960008d185413344a402d0587580c441644d4d20bf57c96f25d4d1710", size = 1990321 }, + { url = "https://files.pythonhosted.org/packages/b2/01/f6bdfb489ce86cb11f7b5428bb8c3cff751436fff4e12c99f3b5f59d622e/leidenalg-0.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:aa9b3bb5e75c5120b57cb8a3ba176610853359a9f5e787ecd6ba7e85c61f4fdc", size = 2254690 }, + { url = "https://files.pythonhosted.org/packages/9b/50/ffad5f00e995ea064d1345867f6aa3534ebfb58e361309e088a12ef278d1/leidenalg-0.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:bed72262f181774de8425d6951c0e9c1d9c3d9bc64e030993e4215d6ab67933b", size = 1924396 }, + { url = "https://files.pythonhosted.org/packages/9d/7c/7683c87e9d0bf70519a0d6cdbb27363b3520a4d5edd9c5f59181e393e2ff/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1672eb4b24c2c484c0f4d2225267a456751f4624ac99619fed4fb9e4a31049", size = 2379529 }, + { url = "https://files.pythonhosted.org/packages/b8/0d/0560646fe75f6d161006cb6f164ef7d25096b9d59d68e836bce931ec8724/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:3d9c74f9c3d97d1daec5cc9f43b56a62bb3f40d5fef9f2b5defd8d715bfb5cdd", size = 2683317 }, + { url = "https://files.pythonhosted.org/packages/79/b9/d84d6225d79fed4fee19faef49e8b8fa58b70fcf55de5061bb1adf602eaa/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:023e97e4c96573e89fdea876da663e38694f6fa91e2c85e98455101409381d88", size = 2570109 }, +] + +[[package]] +name = "llvmlite" +version = "0.47.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/0b/b9d1911cfefa61399821dfb37f486d83e0f42630a8d12f7194270c417002/llvmlite-0.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07", size = 37232770 }, + { url = "https://files.pythonhosted.org/packages/46/27/5799b020e4cdfb25a7c951c06a96397c135efcdc21b78d853bbd9c814c7d/llvmlite-0.47.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb", size = 56275177 }, + { url = "https://files.pythonhosted.org/packages/7e/51/48a53fedf01cb1f3f43ef200be17ebf83c8d9a04018d3783c1a226c342c2/llvmlite-0.47.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8", size = 55128631 }, + { url = "https://files.pythonhosted.org/packages/a2/50/59227d06bdc96e23322713c381af4e77420949d8cd8a042c79e0043096cc/llvmlite-0.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327", size = 38138400 }, + { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769 }, + { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178 }, + { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632 }, + { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692 }, + { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771 }, + { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178 }, + { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632 }, + { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695 }, + { url = "https://files.pythonhosted.org/packages/1c/d4/33c8af00f0bf6f552d74f3a054f648af2c5bc6bece97972f3bfadce4f5ec/llvmlite-0.47.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14", size = 37232773 }, + { url = "https://files.pythonhosted.org/packages/64/1d/a760e993e0c0ba6db38d46b9f48f6c7dceb8ac838824997fb9e25f97bc04/llvmlite-0.47.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa", size = 56275176 }, + { url = "https://files.pythonhosted.org/packages/84/3b/e679bc3b29127182a7f4aa2d2e9e5bea42adb93fb840484147d59c236299/llvmlite-0.47.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca", size = 55128631 }, + { url = "https://files.pythonhosted.org/packages/be/f7/19e2a09c62809c9e63bbd14ce71fb92c6ff7b7b3045741bb00c781efc3c9/llvmlite-0.47.0-cp314-cp314-win_amd64.whl", hash = "sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94", size = 39153826 }, + { url = "https://files.pythonhosted.org/packages/40/a1/581a8c707b5e80efdbbe1dd94527404d33fe50bceb71f39d5a7e11bd57b7/llvmlite-0.47.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c", size = 37232772 }, + { url = "https://files.pythonhosted.org/packages/11/03/16090dd6f74ba2b8b922276047f15962fbeea0a75d5601607edb301ba945/llvmlite-0.47.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b", size = 56275178 }, + { url = "https://files.pythonhosted.org/packages/f5/cb/0abf1dd4c5286a95ffe0c1d8c67aec06b515894a0dd2ac97f5e27b82ab0b/llvmlite-0.47.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8", size = 55128632 }, + { url = "https://files.pythonhosted.org/packages/4f/79/d3bbab197e86e0ff4f9c07122895b66a3e0d024247fcff7f12c473cb36d9/llvmlite-0.47.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453", size = 39153839 }, +] + +[[package]] +name = "locket" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + +[[package]] +name = "matplotlib" +version = "3.10.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860 }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254 }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092 }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691 }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771 }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112 }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310 }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908 }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016 }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336 }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602 }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966 }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462 }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688 }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331 }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461 }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091 }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027 }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269 }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588 }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913 }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019 }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645 }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194 }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684 }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790 }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571 }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292 }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276 }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218 }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145 }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085 }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358 }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970 }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785 }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999 }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543 }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800 }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561 }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884 }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333 }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785 }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420 }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981 }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534 }, +] + +[[package]] +name = "matplotlib-scalebar" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/3d/e76e0e48c96e43b656446c35990824f20136de21940a7436020ab757a54d/matplotlib_scalebar-0.9.0.tar.gz", hash = "sha256:f91adc5e4e67ac920a8fa98a55f904d56e5c2da4d56286748c31ec62146362be", size = 1180588 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/c0/2dfab7b319dabe23f5a7b515a797c74b501d15c72e7a03837cf0cf779b9e/matplotlib_scalebar-0.9.0-py3-none-any.whl", hash = "sha256:5140525cd4e0c60bcade541b86571dabaf446fa69192530bd82d60b54601aa79", size = 16411 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mistune" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/84/620cc3f7e3adf6f5067e10f4dbae71295d8f9e16d5d3f9ef97c40f2f592c/mistune-3.2.1.tar.gz", hash = "sha256:7c8e5501d38bac1582e067e46c8343f17d57ea1aaa735823f3aba1fd59c88a28", size = 98003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/7f/a946aa4f8752b37102b41e64dca18a1976ac705c3a0d1dfe74d820a02552/mistune-3.2.1-py3-none-any.whl", hash = "sha256:78cdb0ba5e938053ccf63651b352508d2efa9411dc8810bfb05f2dc5140c0048", size = 53749 }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734 }, + { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165 }, + { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975 }, + { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742 }, + { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709 }, + { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927 }, + { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464 }, + { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002 }, + { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222 }, + { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793 }, + { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888 }, + { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993 }, + { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956 }, + { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224 }, + { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798 }, + { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083 }, + { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111 }, + { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453 }, + { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612 }, + { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145 }, + { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781 }, + { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145 }, + { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230 }, + { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032 }, + { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353 }, + { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085 }, + { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358 }, + { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332 }, + { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612 }, + { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825 }, +] + +[[package]] +name = "more-itertools" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226 }, +] + +[[package]] +name = "msgpack" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271 }, + { url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914 }, + { url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962 }, + { url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183 }, + { url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454 }, + { url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341 }, + { url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747 }, + { url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633 }, + { url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755 }, + { url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939 }, + { url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064 }, + { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131 }, + { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556 }, + { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920 }, + { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013 }, + { url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096 }, + { url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708 }, + { url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119 }, + { url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212 }, + { url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315 }, + { url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721 }, + { url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657 }, + { url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668 }, + { url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040 }, + { url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037 }, + { url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631 }, + { url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118 }, + { url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127 }, + { url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981 }, + { url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885 }, + { url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658 }, + { url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290 }, + { url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234 }, + { url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391 }, + { url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787 }, + { url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453 }, + { url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264 }, + { url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076 }, + { url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242 }, + { url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509 }, + { url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957 }, + { url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910 }, + { url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197 }, + { url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772 }, + { url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868 }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626 }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706 }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356 }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355 }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433 }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376 }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365 }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747 }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293 }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962 }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360 }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940 }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502 }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065 }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870 }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302 }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981 }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159 }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893 }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456 }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872 }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018 }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883 }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413 }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404 }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456 }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322 }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955 }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254 }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059 }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588 }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642 }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377 }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887 }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053 }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307 }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174 }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116 }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524 }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368 }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952 }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317 }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132 }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140 }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277 }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291 }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156 }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742 }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221 }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664 }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490 }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695 }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884 }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122 }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175 }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460 }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930 }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582 }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031 }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596 }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492 }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899 }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970 }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060 }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888 }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554 }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341 }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391 }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422 }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770 }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109 }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573 }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190 }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486 }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219 }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132 }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420 }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510 }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786 }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483 }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403 }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315 }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528 }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784 }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980 }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602 }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930 }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074 }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471 }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401 }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143 }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507 }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358 }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884 }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878 }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542 }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403 }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889 }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982 }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415 }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337 }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788 }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842 }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237 }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008 }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542 }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719 }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, +] + +[[package]] +name = "multipledispatch" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/3e/a62c3b824c7dec33c4a1578bcc842e6c30300051033a4e5975ed86cc2536/multipledispatch-1.0.0.tar.gz", hash = "sha256:5c839915465c68206c3e9c473357908216c28383b425361e5d144594bf85a7e0", size = 12385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/c0/00c9809d8b9346eb238a6bbd5f83e846a4ce4503da94a4c08cb7284c325b/multipledispatch-1.0.0-py3-none-any.whl", hash = "sha256:0c53cd8b077546da4e48869f49b13164bebafd0c2a5afceb6bb6a316e7fb46e4", size = 12818 }, +] + +[[package]] +name = "multiscale-spatial-image" +version = "2.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dask" }, + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "spatial-image" }, + { name = "xarray" }, + { name = "xarray-dataclass" }, + { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/93/8cab41d5f2d11834e4d077d7a9dd7cdf74596fdc68290e31e77422d4edf2/multiscale_spatial_image-2.0.3.tar.gz", hash = "sha256:0b72b20785c5c8bbae7aea0608ca53e02d61fd76720b10473aeafb97e1616cab", size = 1310669 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/a7/fec56dbac873a18930b2127d400794a91dd53898bff811aa4802ddbbfac9/multiscale_spatial_image-2.0.3-py3-none-any.whl", hash = "sha256:8b20d36e90a104083fe14f5466d582eea453c213d1a368d7e7435b3e13445f61", size = 29169 }, +] + +[[package]] +name = "myst-nb" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "ipykernel" }, + { name = "ipython" }, + { name = "jupyter-cache" }, + { name = "myst-parser" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "pyyaml" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/b4/ff1abeea67e8cfe0a8c033389f6d1d8b0bfecfd611befb5cbdeab884fce6/myst_nb-1.4.0.tar.gz", hash = "sha256:c145598de62446a6fd009773dd071a40d3b76106ace780de1abdfc6961f614c2", size = 82285 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/93/0a378b48488879a1d925b42a804edfc6e0cd0ef854220f2dce738a46e7e9/myst_nb-1.4.0-py3-none-any.whl", hash = "sha256:0e2c86e7d3b82c3aa51383f82d6268f7714f3b772c23a796ab09538a8e68b4e4", size = 82555 }, +] + +[[package]] +name = "myst-parser" +version = "5.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/dc/603751677fff302f34396e206b610f556a59d7fe58b9a2145f54e96b48e8/myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02", size = 101182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/dc/f3dfb7488b770f3f67e6545085bf2abea5172e88f57b8ad25ef860ca704c/myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a", size = 85817 }, +] + +[[package]] +name = "narwhals" +version = "2.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/a0/6198c56d42ef2f3c6ed0c42ba30dbcefdc86a91262d7d449010770ae085b/narwhals-2.21.2.tar.gz", hash = "sha256:5c5b2d0b47aef7c73ea412cfcbcd467f2f2d5be73e3c2ab19d78f4a97718790a", size = 632176 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/77/928ea2e70641ca177a11140062cc5840d421795f2e82749d408d0cce900a/narwhals-2.21.2-py3-none-any.whl", hash = "sha256:7bb57c3700486039215455b9bf2d64261915cc0fd845cc30272d631df696b251", size = 451201 }, +] + +[package.optional-dependencies] +pandas = [ + { name = "pandas" }, +] + +[[package]] +name = "natsort" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, +] + +[[package]] +name = "nbclient" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbformat" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, +] + +[[package]] +name = "nbconvert" +version = "7.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "bleach", extra = ["css"] }, + { name = "defusedxml" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyterlab-pygments" }, + { name = "markupsafe" }, + { name = "mistune" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pandocfilters" }, + { name = "pygments" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/f8/bb0a9d5f46819c821dc1f004aa2cc29b1d91453297dbf5ff20470f00f193/nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8", size = 261927 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "nbsphinx" +version = "0.9.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/d1/82081750f8a78ad0399c6ed831d42623b891904e8e7b8a75878225cf1dce/nbsphinx-0.9.8.tar.gz", hash = "sha256:d0765908399a8ee2b57be7ae881cf2ea58d66db3af7bbf33e6eb48f83bea5495", size = 417469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/78/843bcf0cf31f88d2f8a9a063d2d80817b1901657d83d65b89b3aa835732e/nbsphinx-0.9.8-py3-none-any.whl", hash = "sha256:92d95ee91784e56bc633b60b767a6b6f23a0445f891e24641ce3c3f004759ccf", size = 31961 }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504 }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, +] + +[[package]] +name = "notebook" +version = "7.5.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, + { name = "jupyterlab" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/c2/cf59bd2e6f2c8b976b52477e3e53bf6f97bc714ed046a51821afb428eaee/notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1", size = 14170814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/d6/1fd0646b9bbd9efbb0b8ae21b2325fbef515769a5621c03e31d8eb8da587/notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0", size = 14581730 }, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, +] + +[[package]] +name = "numba" +version = "0.65.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/c5/db2ac3685833d626c0dcae6bd2330cd68433e1fd248d15f70998160d3ad7/numba-0.65.1.tar.gz", hash = "sha256:19357146c32fe9ed25059ab915e8465fb13951cf6b0aace3826b76886373ab23", size = 2765600 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/b3/650500c2eab4534d98e9166f4298e0f3c69c742afdf24e6eabccd1f16ad8/numba-0.65.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:7020d74b19cdb8cff16506542fdd510756e28c5e7f3bd0b7f574f0f42272fcd9", size = 2680563 }, + { url = "https://files.pythonhosted.org/packages/44/0b/0615dbedb98f5b32a35a53290fbdc6e22306968109278d7e58df82d7a9f6/numba-0.65.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f80ed83774b5173abd6581cd8d2165d1d38e13d2e5c8155c0c0b421784745420", size = 3745018 }, + { url = "https://files.pythonhosted.org/packages/49/aa/4361698f35bf63bff67dfe6c90493731177f48ede954f77b0588731537bc/numba-0.65.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ed425a43b0a5f9772f2f4e2dd0bbd12eabecae1af0b24efcfd4e053f012aac6", size = 3450962 }, + { url = "https://files.pythonhosted.org/packages/bd/9a/af61ec03b3116c161fd7a06b9e8a265729a8718458333e8ffbb06d9a3978/numba-0.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:df40a5028a975b9ea66f6a2a3f7abbdbd541a863070e34ed367aff21141248e4", size = 2747417 }, + { url = "https://files.pythonhosted.org/packages/57/bc/76f8f8c5cf9adee47fdb7bbb03be8900f76f902d451d7477cf12b845e1de/numba-0.65.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac3f1e77c352dd0ea9712732c2d8f9ca507717435eec5b5013bf138ac33c4a08", size = 2681371 }, + { url = "https://files.pythonhosted.org/packages/69/47/a415af0283e4db0398104c6d1c11c9861a98dc67a7aa442a7769ed5d6196/numba-0.65.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:52bc6f3ceb8fcaff9b2ae26b4c6b1e9fee39db8d355534c0fe4f39a901246b84", size = 3802467 }, + { url = "https://files.pythonhosted.org/packages/46/36/246f73ec99cfeab2f2cb2ce7d4218766cc36a2da418901223f4f4da9c813/numba-0.65.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90ca10b3463bae0bd70589726fe3c77d01d6b5fc86bee54bcdf9fb6b47c28977", size = 3502628 }, + { url = "https://files.pythonhosted.org/packages/db/9e/3c679b2ee078425b9e99a91e44f8d132a6830d8ccce5227bc5e9181aeed8/numba-0.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:5971c632be2a2351500431f46213821dba8d02b18a9f7d02fd36bd2743e41a6a", size = 2750611 }, + { url = "https://files.pythonhosted.org/packages/79/37/14a4579049c1eb673afd0de0cb4842982acd55b9ce2643e763db858bcea0/numba-0.65.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1735c15c1134a5108b4d6a5c77fc0947924ea066a738dc09a52008c13df9cad3", size = 2681344 }, + { url = "https://files.pythonhosted.org/packages/a0/22/b8d873f6466b20aa563fc9b33acd48dec89a07803ddaa2f1c8ca1cd33126/numba-0.65.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c09f49117ef255e1f1c6dad0c7a1ed39868243862a73be5706793241a3755f1b", size = 3810619 }, + { url = "https://files.pythonhosted.org/packages/62/08/e16a8b5d9a018962ebb5c66be662317cde32b9f5dab08441f90bed5522fb/numba-0.65.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:594a8680b3fadac99e97e489b1fd89007177e5336713745c3b769528c635a464", size = 3509783 }, + { url = "https://files.pythonhosted.org/packages/fd/a5/03c970d57f4c1741354837353ce39fb5206952ae1dba8922d29c86f64805/numba-0.65.1-cp313-cp313-win_amd64.whl", hash = "sha256:85be74c0d036842699a30058f82fb88fc5ffdc59f7615cab5792ea92914c9b62", size = 2750534 }, + { url = "https://files.pythonhosted.org/packages/4f/2e/8aed9b726d9ba5f11ad287645fd479e88278db3060a25cb1225d730eb2b7/numba-0.65.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:33f5eb68eb1c843511615d14663ce60258525d6a4c65ab040e2c2b0c4cf17450", size = 2681554 }, + { url = "https://files.pythonhosted.org/packages/87/96/f3eb235fafa82a34e2ab5dd7dc9ffff998ebf5f0bbc23fa56a96aeb44da6/numba-0.65.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71e73029bf53a62cc6afcf96be4bd942290d8b4c55f0a454fb536158115790f7", size = 3779602 }, + { url = "https://files.pythonhosted.org/packages/09/90/b0f09b48752d23640b8284f22aa597737e8adaddc7fbfacc4708b7f73a4c/numba-0.65.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a07635e0be926b9bdbffb09137c230fb13f6ec0e564914ba937cee12ce3eb35", size = 3479532 }, + { url = "https://files.pythonhosted.org/packages/56/46/3f7fc04fb853559e74b210e0b62c19974ec844cefec611f9e535f4da3761/numba-0.65.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a20fcdabdefbdacf88d85caf70c3b18c4bcb7ebb8f82e6a19486383dd26ab63", size = 2752637 }, + { url = "https://files.pythonhosted.org/packages/81/7b/c1a341a9067367778f4152a5f01061cf281fb09582c92c510ec4918cabf6/numba-0.65.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:548dd4b3a4508d5062768d1514b2cd7b015f9a25ec7af651c50dee243965e652", size = 2684600 }, + { url = "https://files.pythonhosted.org/packages/03/36/98ddbcf3e4f04a6dd07e1c67249955920579ba4af6bb6868e3088f4ed282/numba-0.65.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:78abc28feff2c2ff8307fff3975b6438352759c9acb797ecd6b1fb6e7e39e31d", size = 3817198 }, + { url = "https://files.pythonhosted.org/packages/a3/83/0dad21057ece5a835599f5d24099b091703995e23dbbf894f259e91c010b/numba-0.65.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7676cb389555805f9b9a1840cbcd1ea6c8bd5376ab6918e3a29c5ea1dbda20", size = 3533862 }, + { url = "https://files.pythonhosted.org/packages/32/36/8be7118ffd4c8440881046eac3d0982cc5ab42909508cf5d67024d62a2e4/numba-0.65.1-cp314-cp314t-win_amd64.whl", hash = "sha256:20609346e3bd75204950dcbbfe383a8d7dbf4902f442aedbf00f97fef4aa8f38", size = 2758237 }, +] + +[[package]] +name = "numcodecs" +version = "0.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/bd/8a391e7c356366224734efd24da929cc4796fff468bfb179fe1af6548535/numcodecs-0.16.5.tar.gz", hash = "sha256:0d0fb60852f84c0bd9543cc4d2ab9eefd37fc8efcc410acd4777e62a1d300318", size = 6276387 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/85/1ac101a40ead81eaa1c7dc49a8827a30e2e436211b43ebdc63c590eb1347/numcodecs-0.16.5-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:78382dcea50622f2ef1e6e7a71dbe7f861d8fe376b27b7c297c26907304fef1e", size = 1621795 }, + { url = "https://files.pythonhosted.org/packages/0e/cc/0d97ef55dda48cb0f93d7b92d761208e7a99bd2eea6b0e859426e6a99a21/numcodecs-0.16.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2d04a19cb57a3c519b4127ac377cca6471aee1990d7c18f5b1e3a4fe1306689", size = 1153030 }, + { url = "https://files.pythonhosted.org/packages/5e/41/e120ee1b390730ac5987cde2afd82e2b8442cec315ab40b94b0373e93e73/numcodecs-0.16.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c043af648eb280cd61785c99c22ff5c3c3460f906eb51a8511327c4f5111b283", size = 8510503 }, + { url = "https://files.pythonhosted.org/packages/54/4b/195ac84cc8f6077b4f0f421e8daee21b7f1bd88cb7716414234379fe68ec/numcodecs-0.16.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c398919ef2eb0e56b8e97456f622640bfd3deed06de3acc976989cbcb22628a3", size = 9123428 }, + { url = "https://files.pythonhosted.org/packages/0f/5b/af02c417954f46e5c7bd5163ac251f535877d909fce54861c99ae197f6f6/numcodecs-0.16.5-cp311-cp311-win_amd64.whl", hash = "sha256:3820860ed302d4d84a1c66e70981ff959d5eb712555be4e7d8ced49888594773", size = 801542 }, + { url = "https://files.pythonhosted.org/packages/75/cc/55420f3641a67f78392dc0bc5d02cb9eb0a9dcebf2848d1ac77253ca61fa/numcodecs-0.16.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:24e675dc8d1550cd976a99479b87d872cb142632c75cc402fea04c08c4898523", size = 1656287 }, + { url = "https://files.pythonhosted.org/packages/f5/6c/86644987505dcb90ba6d627d6989c27bafb0699f9fd00187e06d05ea8594/numcodecs-0.16.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:94ddfa4341d1a3ab99989d13b01b5134abb687d3dab2ead54b450aefe4ad5bd6", size = 1148899 }, + { url = "https://files.pythonhosted.org/packages/97/1e/98aaddf272552d9fef1f0296a9939d1487914a239e98678f6b20f8b0a5c8/numcodecs-0.16.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b554ab9ecf69de7ca2b6b5e8bc696bd9747559cb4dd5127bd08d7a28bec59c3a", size = 8534814 }, + { url = "https://files.pythonhosted.org/packages/fb/53/78c98ef5c8b2b784453487f3e4d6c017b20747c58b470393e230c78d18e8/numcodecs-0.16.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad1a379a45bd3491deab8ae6548313946744f868c21d5340116977ea3be5b1d6", size = 9173471 }, + { url = "https://files.pythonhosted.org/packages/1c/20/2fdec87fc7f8cec950d2b0bea603c12dc9f05b4966dc5924ba5a36a61bf6/numcodecs-0.16.5-cp312-cp312-win_amd64.whl", hash = "sha256:845a9857886ffe4a3172ba1c537ae5bcc01e65068c31cf1fce1a844bd1da050f", size = 801412 }, + { url = "https://files.pythonhosted.org/packages/38/38/071ced5a5fd1c85ba0e14ba721b66b053823e5176298c2f707e50bed11d9/numcodecs-0.16.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25be3a516ab677dad890760d357cfe081a371d9c0a2e9a204562318ac5969de3", size = 1654359 }, + { url = "https://files.pythonhosted.org/packages/d1/c0/5f84ba7525577c1b9909fc2d06ef11314825fc4ad4378f61d0e4c9883b4a/numcodecs-0.16.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0107e839ef75b854e969cb577e140b1aadb9847893937636582d23a2a4c6ce50", size = 1144237 }, + { url = "https://files.pythonhosted.org/packages/0b/00/787ea5f237b8ea7bc67140c99155f9c00b5baf11c49afc5f3bfefa298f95/numcodecs-0.16.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:015a7c859ecc2a06e2a548f64008c0ec3aaecabc26456c2c62f4278d8fc20597", size = 8483064 }, + { url = "https://files.pythonhosted.org/packages/c4/e6/d359fdd37498e74d26a167f7a51e54542e642ea47181eb4e643a69a066c3/numcodecs-0.16.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84230b4b9dad2392f2a84242bd6e3e659ac137b5a1ce3571d6965fca673e0903", size = 9126063 }, + { url = "https://files.pythonhosted.org/packages/27/72/6663cc0382ddbb866136c255c837bcb96cc7ce5e83562efec55e1b995941/numcodecs-0.16.5-cp313-cp313-win_amd64.whl", hash = "sha256:5088145502ad1ebf677ec47d00eb6f0fd600658217db3e0c070c321c85d6cf3d", size = 799275 }, + { url = "https://files.pythonhosted.org/packages/3c/9e/38e7ca8184c958b51f45d56a4aeceb1134ecde2d8bd157efadc98502cc42/numcodecs-0.16.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b05647b8b769e6bc8016e9fd4843c823ce5c9f2337c089fb5c9c4da05e5275de", size = 1654721 }, + { url = "https://files.pythonhosted.org/packages/a1/37/260fa42e7b2b08e6e00ad632f8dd620961a60a459426c26cea390f8c68d0/numcodecs-0.16.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3832bd1b5af8bb3e413076b7d93318c8e7d7b68935006b9fa36ca057d1725a8f", size = 1146887 }, + { url = "https://files.pythonhosted.org/packages/4e/15/e2e1151b5a8b14a15dfd4bb4abccce7fff7580f39bc34092780088835f3a/numcodecs-0.16.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49f7b7d24f103187f53135bed28bb9f0ed6b2e14c604664726487bb6d7c882e1", size = 8476987 }, + { url = "https://files.pythonhosted.org/packages/6d/30/16a57fc4d9fb0ba06c600408bd6634f2f1753c54a7a351c99c5e09b51ee2/numcodecs-0.16.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aec9736d81b70f337d89c4070ee3ffeff113f386fd789492fa152d26a15043e4", size = 9102377 }, + { url = "https://files.pythonhosted.org/packages/31/a5/a0425af36c20d55a3ea884db4b4efca25a43bea9214ba69ca7932dd997b4/numcodecs-0.16.5-cp314-cp314-win_amd64.whl", hash = "sha256:b16a14303800e9fb88abc39463ab4706c037647ac17e49e297faa5f7d7dbbf1d", size = 819022 }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194 }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111 }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159 }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936 }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692 }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164 }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877 }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487 }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945 }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406 }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528 }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119 }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246 }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410 }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240 }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012 }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538 }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706 }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541 }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825 }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687 }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482 }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648 }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902 }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944 }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392 }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220 }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800 }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600 }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134 }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598 }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272 }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197 }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287 }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763 }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070 }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752 }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024 }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398 }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971 }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532 }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881 }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458 }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559 }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716 }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947 }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197 }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245 }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587 }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226 }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196 }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334 }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678 }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672 }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731 }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805 }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496 }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616 }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145 }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813 }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982 }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908 }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867 }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511 }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064 }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157 }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728 }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374 }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286 }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263 }, +] + +[[package]] +name = "ome-zarr" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "dask" }, + { name = "deprecated" }, + { name = "fsspec", extra = ["s3"] }, + { name = "numpy" }, + { name = "rangehttpserver" }, + { name = "requests" }, + { name = "scikit-image" }, + { name = "toolz" }, + { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/ee/0e99c62dd3d453c9fb84d8c8cc9f64e255c2d7638546836516282fae2be5/ome_zarr-0.15.0.tar.gz", hash = "sha256:e9479d3a9849c194a616355290f6ae574a7d4402559c5f7005be20cf81945435", size = 81497 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/71/159de9929fe98326ada41393d2133fda3f9869ea10bf94996c48846ff68f/ome_zarr-0.15.0-py3-none-any.whl", hash = "sha256:99789b0c4c27fbd197ab1a8bb3ae58b1c7b4e72d0d6b95e9dda6e6416623d3eb", size = 45200 }, +] + +[[package]] +name = "omnipath" +version = "1.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docrep" }, + { name = "inflect" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, + { name = "urllib3" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/b8/f504706093c149092925c1679aa96aced48af4bde1f73620cc2a6edc7f79/omnipath-1.0.12.tar.gz", hash = "sha256:37e33fd0ab4924a5345deab46868fdc472955c2be136c97ccc8dc5b5ea0ecfb8", size = 2058935 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/ba/704f9e56ae80ef66cf0534e23dac22ada34845f86b5e5b8b3294649d96b6/omnipath-1.0.12-py3-none-any.whl", hash = "sha256:28b85360f2f566e3c1e146560f8d3a5c133e221ce93c63ef62b3c71be9a6b003", size = 51621 }, +] + +[[package]] +name = "opt-einsum" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932 }, +] + +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790 }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831 }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267 }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281 }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453 }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361 }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702 }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846 }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618 }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212 }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693 }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002 }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971 }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722 }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 }, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, +] + +[[package]] +name = "param" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/b5/e64cb3d5767dd10ed69c5e3abfab3a94debc1bcf8a56e35b7e720960eb30/param-2.4.0.tar.gz", hash = "sha256:d2c09da9fe80084cb5a8732761f26786317133995344df03db5f09d63ac35233", size = 216559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/84/8b09d8bd4acf852266374ee53c257be11348b214b6febdc4affbf33fa091/param-2.4.0-py3-none-any.whl", hash = "sha256:770ecebb1bb8ffaeb8722a00976ba5092af287344471c513ba51597b4257339b", size = 150655 }, +] + +[[package]] +name = "parso" +version = "0.8.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025 }, +] + +[[package]] +name = "partd" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "locket" }, + { name = "toolz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905 }, +] + +[[package]] +name = "pathlib-abc" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/cb/448649d7f25d228bf0be3a04590ab7afa77f15e056f8fa976ed05ec9a78f/pathlib_abc-0.5.2.tar.gz", hash = "sha256:fcd56f147234645e2c59c7ae22808b34c364bb231f685ddd9f96885aed78a94c", size = 33342 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/29/c028a0731e202035f0e2e0bfbf1a3e46ad6c628cbb17f6f1cc9eea5d9ff1/pathlib_abc-0.5.2-py3-none-any.whl", hash = "sha256:4c9d94cf1b23af417ce7c0417b43333b06a106c01000b286c99de230d95eefbb", size = 19070 }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328 }, +] + +[[package]] +name = "patsy" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/44/ed13eccdd0519eff265f44b670d46fbb0ec813e2274932dc1c0e48520f7d/patsy-1.0.2.tar.gz", hash = "sha256:cdc995455f6233e90e22de72c37fcadb344e7586fb83f06696f54d92f8ce74c0", size = 399942 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl", hash = "sha256:37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a", size = 233301 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347 }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873 }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168 }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188 }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401 }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655 }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105 }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402 }, + { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149 }, + { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626 }, + { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531 }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279 }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490 }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462 }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744 }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371 }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215 }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783 }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112 }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489 }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129 }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612 }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837 }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528 }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401 }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094 }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402 }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005 }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669 }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194 }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423 }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667 }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580 }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896 }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266 }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508 }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927 }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624 }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252 }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550 }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114 }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667 }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966 }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241 }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592 }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542 }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765 }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848 }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515 }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159 }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386 }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384 }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599 }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021 }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360 }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628 }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321 }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723 }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400 }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835 }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225 }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541 }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251 }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807 }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935 }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720 }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498 }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413 }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084 }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579 }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969 }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674 }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479 }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230 }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404 }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215 }, + { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946 }, +] + +[[package]] +name = "pims" +version = "0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "slicerator" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz", hash = "sha256:55907a4c301256086d2aa4e34a5361b9109f24e375c2071e1117b9491e82946b", size = 87779 } + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743 }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "pooch" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "platformdirs" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/43/85ef45e8b36c6a48546af7b266592dc32d7f67837a6514d111bced6d7d75/pooch-1.9.0.tar.gz", hash = "sha256:de46729579b9857ffd3e741987a2f6d5e0e03219892c167c6578c0091fb511ed", size = 61788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl", hash = "sha256:f265597baa9f760d25ceb29d0beb8186c243d6607b0f60b83ecf14078dbc703b", size = 67175 }, +] + +[[package]] +name = "pre-commit" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472 }, +] + +[[package]] +name = "prometheus-client" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744 }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033 }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754 }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573 }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645 }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563 }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888 }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253 }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558 }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007 }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355 }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057 }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938 }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731 }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966 }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135 }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381 }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887 }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654 }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190 }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995 }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422 }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342 }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639 }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588 }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029 }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774 }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532 }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592 }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788 }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514 }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018 }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322 }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172 }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457 }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835 }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545 }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886 }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261 }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184 }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534 }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500 }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994 }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884 }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464 }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588 }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667 }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463 }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621 }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649 }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636 }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872 }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257 }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696 }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378 }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283 }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616 }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773 }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664 }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643 }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595 }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711 }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247 }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102 }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964 }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330 }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521 }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662 }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928 }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650 }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912 }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300 }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208 }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633 }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724 }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069 }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099 }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391 }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626 }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781 }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570 }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436 }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373 }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554 }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395 }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653 }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914 }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567 }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542 }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845 }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985 }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999 }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779 }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796 }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023 }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448 }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329 }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172 }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813 }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140 }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036 }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664 }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087 }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383 }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210 }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228 }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284 }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898 }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915 }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931 }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449 }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949 }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986 }, + { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371 }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559 }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654 }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394 }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122 }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032 }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490 }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660 }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759 }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471 }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981 }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733 }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335 }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748 }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554 }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301 }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929 }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365 }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819 }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252 }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127 }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997 }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720 }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852 }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852 }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207 }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117 }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155 }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387 }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102 }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118 }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765 }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890 }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250 }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282 }, +] + +[[package]] +name = "pybtex" +version = "0.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "latexcodec" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/f5/f30da9c93f0fa6d619332b2f69597219b625f35780473a05164a9981fd9a/pybtex-0.26.1.tar.gz", hash = "sha256:2e5543bea424e60e9e42eef70bff597be48649d8f68ba061a7a092b2477d5464", size = 692991 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/f6/775eb92e865b28cdb4ad1f2bed7a5446197516f76b58a950faa3be3fd08d/pybtex-0.26.1-py3-none-any.whl", hash = "sha256:e26c0412cc54f5f21b2a6d9d175762a2d2af9ccf3a8f651cdb89ec035db77aa1", size = 126134 }, +] + +[[package]] +name = "pybtex-docutils" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "pybtex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/84/796ea94d26188a853660f81bded39f8de4cfe595130aef0dea1088705a11/pybtex-docutils-1.0.3.tar.gz", hash = "sha256:3a7ebdf92b593e00e8c1c538aa9a20bca5d92d84231124715acc964d51d93c6b", size = 18348 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/b1/ce1f4596211efb5410e178a803f08e59b20bedb66837dcf41e21c54f9ec1/pybtex_docutils-1.0.3-py3-none-any.whl", hash = "sha256:8fd290d2ae48e32fcb54d86b0efb8d573198653c7e2447d5bec5847095f430b9", size = 6385 }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, +] + +[[package]] +name = "pyct" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "param" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/c3/78eeacf7cbf478db6bd41de0ee2221cc6769d81e0c10f6c1616c82a25e06/pyct-0.6.0.tar.gz", hash = "sha256:d4e513b2cf35b6165605ae5fce5f2a49985bc67473c579de85134b8c71d374a8", size = 14586 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/b2/23f4032cd1c9744aa8e9ecda43cd4d755fcb209f7f40fae035248f31a679/pyct-0.6.0-py3-none-any.whl", hash = "sha256:cfaded7289fca72ddf6579b81459e3ec8db323a508e61c49aa318ee3cd6ff160", size = 16630 }, +] + +[[package]] +name = "pyenchant" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/ad/64925c937e41be75c7067c85757b3d45b148e9111187b37693269f583156/pyenchant-3.3.0.tar.gz", hash = "sha256:825288246b5debc9436f91967650974ef0d5636458502619e322c476f1283891", size = 60696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/b0/35926bad6885fb7bc24aa7e1b45e6d86540c6c57ee4abc4fed1ef58d4ec0/pyenchant-3.3.0-py3-none-any.whl", hash = "sha256:3da00b1d01314d85aac733bb997415d7a3e875666dc81735ddcf320aa36b7a70", size = 58363 }, + { url = "https://files.pythonhosted.org/packages/d6/7f/1d7b8ad86c2a841d940df7b965fa727e052b95d539e4c563da685c25d0d2/pyenchant-3.3.0-py3-none-win32.whl", hash = "sha256:1d55e075645a6edbb3c590fb42f9e02b4d455e4affe28a2227d5cb6d4868e626", size = 37787278 }, + { url = "https://files.pythonhosted.org/packages/ad/ae/5624803b62ecb0a20248f0d28ed3f78c78746a032582a016d4b2890c7899/pyenchant-3.3.0-py3-none-win_amd64.whl", hash = "sha256:04a5bd0e022ebe2e8c6d9e498ec3d650602e264ec5486e9c6a1b7f99c9507c49", size = 37427576 }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, +] + +[[package]] +name = "pynndescent" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "llvmlite" }, + { name = "numba" }, + { name = "scikit-learn" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/fb/7f58c397fb31666756457ee2ac4c0289ef2daad57f4ae4be8dec12f80b03/pynndescent-0.6.0.tar.gz", hash = "sha256:7ffde0fb5b400741e055a9f7d377e3702e02250616834231f6c209e39aac24f5", size = 2992987 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/e6/94145d714402fd5ade00b5661f2d0ab981219e07f7db9bfa16786cdb9c04/pynndescent-0.6.0-py3-none-any.whl", hash = "sha256:dc8c74844e4c7f5cbd1e0cd6909da86fdc789e6ff4997336e344779c3d5538ef", size = 73511 }, +] + +[[package]] +name = "pyogrio" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "numpy" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/d4/12f86b1ed09721363da4c09622464b604c851a9223fc0c6b393fb2012208/pyogrio-0.12.1.tar.gz", hash = "sha256:e548ab705bb3e5383693717de1e6c76da97f3762ab92522cb310f93128a75ff1", size = 303289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/46/b2c2dcdfd88759b56f103365905fffb85e8b08c1db1ec7c8f8b4c4c26016/pyogrio-0.12.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:01b322dac2a258d24b024d1028dcaa03c9bb6d9c3988b86d298a64873d10dc65", size = 23670744 }, + { url = "https://files.pythonhosted.org/packages/d9/21/b69f1bc51d805c00dd7c484a18e1fd2e75b41da1d9f5b8591d7d9d4a7d2f/pyogrio-0.12.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e10087abcbd6b7e8212560a7002984e5078ac7b3a969ddc2c9929044dbb0d403", size = 25246184 }, + { url = "https://files.pythonhosted.org/packages/19/8c/b6aae08e8fcc4f2a903da5f6bd8f888d2b6d7290e54dde5abe15b4cca8df/pyogrio-0.12.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f6c621972b09fd81a32317e742c69ff4a7763a803da211361a78317f9577765", size = 31434449 }, + { url = "https://files.pythonhosted.org/packages/70/f9/9538fa893c29a3fdfeddf3b4c9f8db77f2d4134bc766587929fec8405ebf/pyogrio-0.12.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c38253427b688464caad5316d4ebcec116b5e13f1f02cc4e3588502f136ca1b4", size = 30987586 }, + { url = "https://files.pythonhosted.org/packages/89/a4/0aef5837b4e11840f501e48e01c31242838476c4f4aff9c05e228a083982/pyogrio-0.12.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5f47787251de7ce13cc06038da93a1214dc283cbccf816be6e03c080358226c8", size = 32534386 }, + { url = "https://files.pythonhosted.org/packages/34/97/e8f2ed8a339152b86f8403c258ae5d5f23ab32d690eeb0545bb3473d0c69/pyogrio-0.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:c1d756cf2da4cdf5609779f260d1e1e89be023184225855d6f3dcd33bbe17cb0", size = 22941718 }, + { url = "https://files.pythonhosted.org/packages/ad/e0/656b6536549d41b5aec57e0deca1f269b4f17532f0636836f587e581603a/pyogrio-0.12.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:7a0d5ca39184030aec4cde30f4258f75b227a854530d2659babc8189d76e657d", size = 23661857 }, + { url = "https://files.pythonhosted.org/packages/14/78/313259e40da728bdb60106ffdc7ea8224d164498cb838ecb79b634aab967/pyogrio-0.12.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:feaff42bbe8087ca0b30e33b09d1ce049ca55fe83ad83db1139ef37d1d04f30c", size = 25237106 }, + { url = "https://files.pythonhosted.org/packages/8f/ca/5368571a8b00b941ccfbe6ea29a5566aaffd45d4eb1553b956f7755af43e/pyogrio-0.12.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81096a5139532de5a8003ef02b41d5d2444cb382a9aecd1165b447eb549180d3", size = 31417048 }, + { url = "https://files.pythonhosted.org/packages/ef/85/6eeb875f27bf498d657eb5dab9f58e4c48b36c9037122787abee9a1ba4ba/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:41b78863f782f7a113ed0d36a5dc74d59735bd3a82af53510899bb02a18b06bb", size = 30952115 }, + { url = "https://files.pythonhosted.org/packages/36/f7/cf8bec9024625947e1a71441906f60a5fa6f9e4c441c4428037e73b1fcc8/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8b65be8c4258b27cc8f919b21929cecdadda4c353e3637fa30850339ef4d15c5", size = 32537246 }, + { url = "https://files.pythonhosted.org/packages/ab/10/7c9f5e428273574e69f217eba3a6c0c42936188ad4dcd9e2c41ebb711188/pyogrio-0.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:1291b866c2c81d991bda15021b08b3621709b40ee3a85689229929e9465788bf", size = 22933980 }, + { url = "https://files.pythonhosted.org/packages/be/56/f56e79f71b84aa9bea25fdde39fab3846841bd7926be96f623eb7253b7e1/pyogrio-0.12.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ec0e47a5a704e575092b2fd5c83fa0472a1d421e590f94093eb837bb0a11125d", size = 23658483 }, + { url = "https://files.pythonhosted.org/packages/66/ac/5559f8a35d58a16cbb2dd7602dd11936ff8796d8c9bf789f14da88764ec3/pyogrio-0.12.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:b4c888fc08f388be4dd99dfca5e84a5cdc5994deeec0230cc45144d3460e2b21", size = 25232737 }, + { url = "https://files.pythonhosted.org/packages/59/58/925f1c129ddd7cbba8dea4e7609797cea7a76dbc863ac9afd318a679c4b9/pyogrio-0.12.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:73a88436f9962750d782853727897ac2722cac5900d920e39fab3e56d7a6a7f1", size = 31377986 }, + { url = "https://files.pythonhosted.org/packages/18/5f/c87034e92847b1844d0e8492a6a8e3301147d32c5e57909397ce64dbedf5/pyogrio-0.12.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b5d248a0d59fe9bbf9a35690b70004c67830ee0ebe7d4f7bb8ffd8659f684b3a", size = 30915791 }, + { url = "https://files.pythonhosted.org/packages/46/35/b874f79d03e9f900012cf609f7fff97b77164f2e14ee5aac282f8a999c1b/pyogrio-0.12.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0622bc1a186421547660271083079b38d42e6f868802936d8538c0b379f1ab6b", size = 32499754 }, + { url = "https://files.pythonhosted.org/packages/c3/c4/705678c9c4200130290b3a104b45c0cc10aaa48fcef3b2585b34e34ab3e1/pyogrio-0.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:207bd60c7ffbcea84584596e3637653aa7095e9ee20fa408f90c7f9460392613", size = 22933945 }, + { url = "https://files.pythonhosted.org/packages/f9/e0/d92d4944001330bc87742d43f112d63d12fc89378b6187e62ff3fc1e8e85/pyogrio-0.12.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1511b39a283fa27cda906cd187a791578942a87a40b6a06697d9b43bb8ac80b0", size = 23692697 }, + { url = "https://files.pythonhosted.org/packages/e5/d7/40acbe06d1b1140e3bb27b79e9163776469c1dc785f1be7d9a7fc7b95c87/pyogrio-0.12.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:e486cd6aa9ea8a15394a5f84e019d61ec18f257eeeb642348bd68c3d1e57280b", size = 25258083 }, + { url = "https://files.pythonhosted.org/packages/87/a1/39fefd9cddd95986700524f43d3093b4350f6e4fc200623c3838424a5080/pyogrio-0.12.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3f1a19f63bfd1d3042e45f37ad1d6598123a5a604b6c4ba3f38b419273486cd", size = 31368995 }, + { url = "https://files.pythonhosted.org/packages/18/d7/da88c566e67d741a03851eb8d01358949d52e0b0fc2cd953582dc6d89ff8/pyogrio-0.12.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:f3dcc59b3316b8a0f59346bcc638a4d69997864a4d21da839192f50c4c92369a", size = 31035589 }, + { url = "https://files.pythonhosted.org/packages/11/ac/8f0199f0d31b8ddbc4b4ea1918df8070fdf3e0a63100b898633ec9396224/pyogrio-0.12.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a0643e041dee3e8e038fce69f52a915ecb486e6d7b674c0f9919f3c9e9629689", size = 32487973 }, + { url = "https://files.pythonhosted.org/packages/bd/64/8541a27e9635a335835d234dfaeb19d6c26097fd88224eda7791f83ca98d/pyogrio-0.12.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5881017f29e110d3613819667657844d8e961b747f2d35cf92f273c27af6d068", size = 22987374 }, + { url = "https://files.pythonhosted.org/packages/f4/6f/b4d5e285e08c0c60bcc23b50d73038ddc7335d8de79cc25678cd486a3db0/pyogrio-0.12.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5a1b0453d1c9e7b03715dd57296c8f3790acb8b50d7e3b5844b3074a18f50709", size = 23660673 }, + { url = "https://files.pythonhosted.org/packages/8d/75/4b29e71489c5551aa1a1c5ca8c5160a60203c94f2f68c87c0e3614d58965/pyogrio-0.12.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e7ee560422239dd09ca7f8284cc8483a8919c30d25f3049bb0249bff4c38dec4", size = 25232194 }, + { url = "https://files.pythonhosted.org/packages/89/6e/e9929d2261a07c36301983de2767bcde90d441ab5bf1d767ce56dd07f8b4/pyogrio-0.12.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:648c6f7f5f214d30e6cf493b4af1d59782907ac068af9119ca35f18153d6865a", size = 31336936 }, + { url = "https://files.pythonhosted.org/packages/1d/9e/c59941d734ed936d4e5c89b4b99cb5541307cc42b3fd466ee78a1850c177/pyogrio-0.12.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:58042584f3fd4cabb0f55d26c1405053f656be8a5c266c38140316a1e981aca0", size = 30902210 }, + { url = "https://files.pythonhosted.org/packages/d1/68/cc07320a63f9c2586e60bf11d148b00e12d0e707673bffe609bbdcb7e754/pyogrio-0.12.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b438e38e4ccbaedaa5cb5824ff5de5539315d9b2fde6547c1e816576924ee8ca", size = 32461674 }, + { url = "https://files.pythonhosted.org/packages/13/bc/e4522f429c45a3b6ad28185849dd76e5c8718b780883c4795e7ee41841ae/pyogrio-0.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:f1d8d8a2fea3781dc2a05982c050259261ebc0f6c5e03732d6d79d582adf9363", size = 23550575 }, + { url = "https://files.pythonhosted.org/packages/bd/ac/34f0664d0e391994a7b68529ae07a96432b2b4926dbac173ddc4ec94d310/pyogrio-0.12.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9fe7286946f35a73e6370dc5855bc7a5e8e7babf9e4a8bad7a3279a1d94c7ea9", size = 23694285 }, + { url = "https://files.pythonhosted.org/packages/8a/93/873255529faff1da09d0b27287e85ec805a318c60c0c74fd7df77f94e557/pyogrio-0.12.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2c50345b382f1be801d654ec22c70ee974d6057d4ba7afe984b55f2192bc94ee", size = 25259825 }, + { url = "https://files.pythonhosted.org/packages/27/95/4d4c3644695d99c6fa0b0b42f0d6266ae9dfaf64478a3371eaac950bdd02/pyogrio-0.12.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0db95765ac0ca935c7fe579e29451294e3ab19c317b0c59c31fbe92a69155e0", size = 31371995 }, + { url = "https://files.pythonhosted.org/packages/4c/6f/71f6bcca8754c8bf55a4b7153c61c91f8ac5ba992568e9fa3e54a0ee76fd/pyogrio-0.12.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fc882779075982b93064b3bf3d8642514a6df00d9dd752493b104817072cfb01", size = 31035498 }, + { url = "https://files.pythonhosted.org/packages/fd/47/75c1aa165a988347317afab9b938a01ad25dbca559b582ea34473703dc38/pyogrio-0.12.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:806f620e0c54b54dbdd65e9b6368d24f344cda84c9343364b40a57eb3e1c4dca", size = 32496390 }, + { url = "https://files.pythonhosted.org/packages/31/93/4641dc5d952f6bdb71dabad2c50e3f8a5d58396cdea6ff8f8a08bfd4f4a6/pyogrio-0.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5399f66730978d8852ef5f44dbafa0f738e7f28f4f784349f36830b69a9d2134", size = 23620996 }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 }, +] + +[[package]] +name = "pyproj" +version = "3.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/bd/f205552cd1713b08f93b09e39a3ec99edef0b3ebbbca67b486fdf1abe2de/pyproj-3.7.2-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2514d61f24c4e0bb9913e2c51487ecdaeca5f8748d8313c933693416ca41d4d5", size = 6227022 }, + { url = "https://files.pythonhosted.org/packages/75/4c/9a937e659b8b418ab573c6d340d27e68716928953273e0837e7922fcac34/pyproj-3.7.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:8693ca3892d82e70de077701ee76dd13d7bca4ae1c9d1e739d72004df015923a", size = 4625810 }, + { url = "https://files.pythonhosted.org/packages/c0/7d/a9f41e814dc4d1dc54e95b2ccaf0b3ebe3eb18b1740df05fe334724c3d89/pyproj-3.7.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5e26484d80fea56273ed1555abaea161e9661d81a6c07815d54b8e883d4ceb25", size = 9638694 }, + { url = "https://files.pythonhosted.org/packages/ad/ab/9bdb4a6216b712a1f9aab1c0fcbee5d3726f34a366f29c3e8c08a78d6b70/pyproj-3.7.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:281cb92847814e8018010c48b4069ff858a30236638631c1a91dd7bfa68f8a8a", size = 9493977 }, + { url = "https://files.pythonhosted.org/packages/c9/db/2db75b1b6190f1137b1c4e8ef6a22e1c338e46320f6329bfac819143e063/pyproj-3.7.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9c8577f0b7bb09118ec2e57e3babdc977127dd66326d6c5d755c76b063e6d9dc", size = 10841151 }, + { url = "https://files.pythonhosted.org/packages/89/f7/989643394ba23a286e9b7b3f09981496172f9e0d4512457ffea7dc47ffc7/pyproj-3.7.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a23f59904fac3a5e7364b3aa44d288234af267ca041adb2c2b14a903cd5d3ac5", size = 10751585 }, + { url = "https://files.pythonhosted.org/packages/53/6d/ad928fe975a6c14a093c92e6a319ca18f479f3336bb353a740bdba335681/pyproj-3.7.2-cp311-cp311-win32.whl", hash = "sha256:f2af4ed34b2cf3e031a2d85b067a3ecbd38df073c567e04b52fa7a0202afde8a", size = 5908533 }, + { url = "https://files.pythonhosted.org/packages/79/e0/b95584605cec9ed50b7ebaf7975d1c4ddeec5a86b7a20554ed8b60042bd7/pyproj-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b7cb633565129677b2a183c4d807c727d1c736fcb0568a12299383056e67433", size = 6320742 }, + { url = "https://files.pythonhosted.org/packages/b7/4d/536e8f93bca808175c2d0a5ac9fdf69b960d8ab6b14f25030dccb07464d7/pyproj-3.7.2-cp311-cp311-win_arm64.whl", hash = "sha256:38b08d85e3a38e455625b80e9eb9f78027c8e2649a21dec4df1f9c3525460c71", size = 6245772 }, + { url = "https://files.pythonhosted.org/packages/8d/ab/9893ea9fb066be70ed9074ae543914a618c131ed8dff2da1e08b3a4df4db/pyproj-3.7.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0a9bb26a6356fb5b033433a6d1b4542158fb71e3c51de49b4c318a1dff3aeaab", size = 6219832 }, + { url = "https://files.pythonhosted.org/packages/53/78/4c64199146eed7184eb0e85bedec60a4aa8853b6ffe1ab1f3a8b962e70a0/pyproj-3.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:567caa03021178861fad27fabde87500ec6d2ee173dd32f3e2d9871e40eebd68", size = 4620650 }, + { url = "https://files.pythonhosted.org/packages/b6/ac/14a78d17943898a93ef4f8c6a9d4169911c994e3161e54a7cedeba9d8dde/pyproj-3.7.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c203101d1dc3c038a56cff0447acc515dd29d6e14811406ac539c21eed422b2a", size = 9667087 }, + { url = "https://files.pythonhosted.org/packages/b8/be/212882c450bba74fc8d7d35cbd57e4af84792f0a56194819d98106b075af/pyproj-3.7.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1edc34266c0c23ced85f95a1ee8b47c9035eae6aca5b6b340327250e8e281630", size = 9552797 }, + { url = "https://files.pythonhosted.org/packages/ba/c0/c0f25c87b5d2a8686341c53c1792a222a480d6c9caf60311fec12c99ec26/pyproj-3.7.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa9f26c21bc0e2dc3d224cb1eb4020cf23e76af179a7c66fea49b828611e4260", size = 10837036 }, + { url = "https://files.pythonhosted.org/packages/5d/37/5cbd6772addde2090c91113332623a86e8c7d583eccb2ad02ea634c4a89f/pyproj-3.7.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9428b318530625cb389b9ddc9c51251e172808a4af79b82809376daaeabe5e9", size = 10775952 }, + { url = "https://files.pythonhosted.org/packages/69/a1/dc250e3cf83eb4b3b9a2cf86fdb5e25288bd40037ae449695550f9e96b2f/pyproj-3.7.2-cp312-cp312-win32.whl", hash = "sha256:b3d99ed57d319da042f175f4554fc7038aa4bcecc4ac89e217e350346b742c9d", size = 5898872 }, + { url = "https://files.pythonhosted.org/packages/4a/a6/6fe724b72b70f2b00152d77282e14964d60ab092ec225e67c196c9b463e5/pyproj-3.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:11614a054cd86a2ed968a657d00987a86eeb91fdcbd9ad3310478685dc14a128", size = 6312176 }, + { url = "https://files.pythonhosted.org/packages/5d/68/915cc32c02a91e76d02c8f55d5a138d6ef9e47a0d96d259df98f4842e558/pyproj-3.7.2-cp312-cp312-win_arm64.whl", hash = "sha256:509a146d1398bafe4f53273398c3bb0b4732535065fa995270e52a9d3676bca3", size = 6233452 }, + { url = "https://files.pythonhosted.org/packages/be/14/faf1b90d267cea68d7e70662e7f88cefdb1bc890bd596c74b959e0517a72/pyproj-3.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:19466e529b1b15eeefdf8ff26b06fa745856c044f2f77bf0edbae94078c1dfa1", size = 6214580 }, + { url = "https://files.pythonhosted.org/packages/35/48/da9a45b184d375f62667f62eba0ca68569b0bd980a0bb7ffcc1d50440520/pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c79b9b84c4a626c5dc324c0d666be0bfcebd99f7538d66e8898c2444221b3da7", size = 4615388 }, + { url = "https://files.pythonhosted.org/packages/5e/e7/d2b459a4a64bca328b712c1b544e109df88e5c800f7c143cfbc404d39bfb/pyproj-3.7.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ceecf374cacca317bc09e165db38ac548ee3cad07c3609442bd70311c59c21aa", size = 9628455 }, + { url = "https://files.pythonhosted.org/packages/f8/85/c2b1706e51942de19076eff082f8495e57d5151364e78b5bef4af4a1d94a/pyproj-3.7.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5141a538ffdbe4bfd157421828bb2e07123a90a7a2d6f30fa1462abcfb5ce681", size = 9514269 }, + { url = "https://files.pythonhosted.org/packages/34/38/07a9b89ae7467872f9a476883a5bad9e4f4d1219d31060f0f2b282276cbe/pyproj-3.7.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f000841e98ea99acbb7b8ca168d67773b0191de95187228a16110245c5d954d5", size = 10808437 }, + { url = "https://files.pythonhosted.org/packages/12/56/fda1daeabbd39dec5b07f67233d09f31facb762587b498e6fc4572be9837/pyproj-3.7.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8115faf2597f281a42ab608ceac346b4eb1383d3b45ab474fd37341c4bf82a67", size = 10745540 }, + { url = "https://files.pythonhosted.org/packages/0d/90/c793182cbba65a39a11db2ac6b479fe76c59e6509ae75e5744c344a0da9d/pyproj-3.7.2-cp313-cp313-win32.whl", hash = "sha256:f18c0579dd6be00b970cb1a6719197fceecc407515bab37da0066f0184aafdf3", size = 5896506 }, + { url = "https://files.pythonhosted.org/packages/be/0f/747974129cf0d800906f81cd25efd098c96509026e454d4b66868779ab04/pyproj-3.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:bb41c29d5f60854b1075853fe80c58950b398d4ebb404eb532536ac8d2834ed7", size = 6310195 }, + { url = "https://files.pythonhosted.org/packages/82/64/fc7598a53172c4931ec6edf5228280663063150625d3f6423b4c20f9daff/pyproj-3.7.2-cp313-cp313-win_arm64.whl", hash = "sha256:2b617d573be4118c11cd96b8891a0b7f65778fa7733ed8ecdb297a447d439100", size = 6230748 }, + { url = "https://files.pythonhosted.org/packages/aa/f0/611dd5cddb0d277f94b7af12981f56e1441bf8d22695065d4f0df5218498/pyproj-3.7.2-cp313-cp313t-macosx_13_0_x86_64.whl", hash = "sha256:d27b48f0e81beeaa2b4d60c516c3a1cfbb0c7ff6ef71256d8e9c07792f735279", size = 6241729 }, + { url = "https://files.pythonhosted.org/packages/15/93/40bd4a6c523ff9965e480870611aed7eda5aa2c6128c6537345a2b77b542/pyproj-3.7.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:55a3610d75023c7b1c6e583e48ef8f62918e85a2ae81300569d9f104d6684bb6", size = 4652497 }, + { url = "https://files.pythonhosted.org/packages/1b/ae/7150ead53c117880b35e0d37960d3138fe640a235feb9605cb9386f50bb0/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8d7349182fa622696787cc9e195508d2a41a64765da9b8a6bee846702b9e6220", size = 9942610 }, + { url = "https://files.pythonhosted.org/packages/d8/17/7a4a7eafecf2b46ab64e5c08176c20ceb5844b503eaa551bf12ccac77322/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:d230b186eb876ed4f29a7c5ee310144c3a0e44e89e55f65fb3607e13f6db337c", size = 9692390 }, + { url = "https://files.pythonhosted.org/packages/c3/55/ae18f040f6410f0ea547a21ada7ef3e26e6c82befa125b303b02759c0e9d/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:237499c7862c578d0369e2b8ac56eec550e391a025ff70e2af8417139dabb41c", size = 11047596 }, + { url = "https://files.pythonhosted.org/packages/e6/2e/d3fff4d2909473f26ae799f9dda04caa322c417a51ff3b25763f7d03b233/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8c225f5978abd506fd9a78eaaf794435e823c9156091cabaab5374efb29d7f69", size = 10896975 }, + { url = "https://files.pythonhosted.org/packages/f2/bc/8fc7d3963d87057b7b51ebe68c1e7c51c23129eee5072ba6b86558544a46/pyproj-3.7.2-cp313-cp313t-win32.whl", hash = "sha256:2da731876d27639ff9d2d81c151f6ab90a1546455fabd93368e753047be344a2", size = 5953057 }, + { url = "https://files.pythonhosted.org/packages/cc/27/ea9809966cc47d2d51e6d5ae631ea895f7c7c7b9b3c29718f900a8f7d197/pyproj-3.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f54d91ae18dd23b6c0ab48126d446820e725419da10617d86a1b69ada6d881d3", size = 6375414 }, + { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501 }, + { url = "https://files.pythonhosted.org/packages/42/17/c2b050d3f5b71b6edd0d96ae16c990fdc42a5f1366464a5c2772146de33a/pyproj-3.7.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:2aaa328605ace41db050d06bac1adc11f01b71fe95c18661497763116c3a0f02", size = 6214541 }, + { url = "https://files.pythonhosted.org/packages/03/68/68ada9c8aea96ded09a66cfd9bf87aa6db8c2edebe93f5bf9b66b0143fbc/pyproj-3.7.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:35dccbce8201313c596a970fde90e33605248b66272595c061b511c8100ccc08", size = 4617456 }, + { url = "https://files.pythonhosted.org/packages/81/e4/4c50ceca7d0e937977866b02cb64e6ccf4df979a5871e521f9e255df6073/pyproj-3.7.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:25b0b7cb0042444c29a164b993c45c1b8013d6c48baa61dc1160d834a277e83b", size = 9615590 }, + { url = "https://files.pythonhosted.org/packages/05/1e/ada6fb15a1d75b5bd9b554355a69a798c55a7dcc93b8d41596265c1772e3/pyproj-3.7.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:85def3a6388e9ba51f964619aa002a9d2098e77c6454ff47773bb68871024281", size = 9474960 }, + { url = "https://files.pythonhosted.org/packages/51/07/9d48ad0a8db36e16f842f2c8a694c1d9d7dcf9137264846bef77585a71f3/pyproj-3.7.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b1bccefec3875ab81eabf49059e2b2ea77362c178b66fd3528c3e4df242f1516", size = 10799478 }, + { url = "https://files.pythonhosted.org/packages/85/cf/2f812b529079f72f51ff2d6456b7fef06c01735e5cfd62d54ffb2b548028/pyproj-3.7.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d5371ca114d6990b675247355a801925814eca53e6c4b2f1b5c0a956336ee36e", size = 10710030 }, + { url = "https://files.pythonhosted.org/packages/99/9b/4626a19e1f03eba4c0e77b91a6cf0f73aa9cb5d51a22ee385c22812bcc2c/pyproj-3.7.2-cp314-cp314-win32.whl", hash = "sha256:77f066626030f41be543274f5ac79f2a511fe89860ecd0914f22131b40a0ec25", size = 5991181 }, + { url = "https://files.pythonhosted.org/packages/04/b2/5a6610554306a83a563080c2cf2c57565563eadd280e15388efa00fb5b33/pyproj-3.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a964da1696b8522806f4276ab04ccfff8f9eb95133a92a25900697609d40112", size = 6434721 }, + { url = "https://files.pythonhosted.org/packages/ae/ce/6c910ea2e1c74ef673c5d48c482564b8a7824a44c4e35cca2e765b68cfcc/pyproj-3.7.2-cp314-cp314-win_arm64.whl", hash = "sha256:e258ab4dbd3cf627809067c0ba8f9884ea76c8e5999d039fb37a1619c6c3e1f6", size = 6363821 }, + { url = "https://files.pythonhosted.org/packages/e4/e4/5532f6f7491812ba782a2177fe9de73fd8e2912b59f46a1d056b84b9b8f2/pyproj-3.7.2-cp314-cp314t-macosx_13_0_x86_64.whl", hash = "sha256:bbbac2f930c6d266f70ec75df35ef851d96fdb3701c674f42fd23a9314573b37", size = 6241773 }, + { url = "https://files.pythonhosted.org/packages/20/1f/0938c3f2bbbef1789132d1726d9b0e662f10cfc22522743937f421ad664e/pyproj-3.7.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b7544e0a3d6339dc9151e9c8f3ea62a936ab7cc446a806ec448bbe86aebb979b", size = 4652537 }, + { url = "https://files.pythonhosted.org/packages/c7/a8/488b1ed47d25972f33874f91f09ca8f2227902f05f63a2b80dc73e7b1c97/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:f7f5133dca4c703e8acadf6f30bc567d39a42c6af321e7f81975c2518f3ed357", size = 9940864 }, + { url = "https://files.pythonhosted.org/packages/c7/cc/7f4c895d0cb98e47b6a85a6d79eaca03eb266129eed2f845125c09cf31ff/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5aff3343038d7426aa5076f07feb88065f50e0502d1b0d7c22ddfdd2c75a3f81", size = 9688868 }, + { url = "https://files.pythonhosted.org/packages/b2/b7/c7e306b8bb0f071d9825b753ee4920f066c40fbfcce9372c4f3cfb2fc4ed/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b0552178c61f2ac1c820d087e8ba6e62b29442debddbb09d51c4bf8acc84d888", size = 11045910 }, + { url = "https://files.pythonhosted.org/packages/42/fb/538a4d2df695980e2dde5c04d965fbdd1fe8c20a3194dc4aaa3952a4d1be/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47d87db2d2c436c5fd0409b34d70bb6cdb875cca2ebe7a9d1c442367b0ab8d59", size = 10895724 }, + { url = "https://files.pythonhosted.org/packages/e8/8b/a3f0618b03957de9db5489a04558a8826f43906628bb0b766033aa3b5548/pyproj-3.7.2-cp314-cp314t-win32.whl", hash = "sha256:c9b6f1d8ad3e80a0ee0903a778b6ece7dca1d1d40f6d114ae01bc8ddbad971aa", size = 6056848 }, + { url = "https://files.pythonhosted.org/packages/bc/56/413240dd5149dd3291eda55aa55a659da4431244a2fd1319d0ae89407cfb/pyproj-3.7.2-cp314-cp314t-win_amd64.whl", hash = "sha256:1914e29e27933ba6f9822663ee0600f169014a2859f851c054c88cf5ea8a333c", size = 6517676 }, + { url = "https://files.pythonhosted.org/packages/15/73/a7141a1a0559bf1a7aa42a11c879ceb19f02f5c6c371c6d57fd86cefd4d1/pyproj-3.7.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d9d25bae416a24397e0d85739f84d323b55f6511e45a522dd7d7eae70d10c7e4", size = 6391844 }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249 }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876 }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095 }, +] + +[[package]] +name = "pytest-rerunfailures" +version = "16.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/74f8e685be7ecd1572c1256132f18fce3a665d7e07649a3f23b7eb2d3bec/pytest_rerunfailures-16.3.tar.gz", hash = "sha256:37c9b1231c8083e9f4e724f50f7a21241822f9516c15c700ebbf218d6452355c", size = 34148 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/98/58a71d68d3126d7f6a6ed1944c37ec207a4ff3dc66cad3bed7b59d38df61/pytest_rerunfailures-16.3-py3-none-any.whl", hash = "sha256:6bdfb8ffb46c46072e6c16bdedee38b6c13eac620d9415ed5b63152cbf283170", size = 15396 }, +] + +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382 }, +] + +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-discovery" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217 }, +] + +[[package]] +name = "python-json-logger" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021 }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141 }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, +] + +[[package]] +name = "pywinpty" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/c3/3e75075c7f71735f22b66fab0481f2c98e3a4d58cba55cb50ba29114bcf6/pywinpty-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:dff25a9a6435f527d7c65608a7e62783fc12076e7d44487a4911ee91be5a8ac8", size = 2114430 }, + { url = "https://files.pythonhosted.org/packages/8d/1e/8a54166a8c5e4f5cb516514bdf4090be4d51a71e8d9f6d98c0aa00fe45d4/pywinpty-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:fbc1e230e5b193eef4431cba3f39996a288f9958f9c9f092c8a961d930ee8f68", size = 236191 }, + { url = "https://files.pythonhosted.org/packages/7c/d4/aeb5e1784d2c5bff6e189138a9ca91a090117459cea0c30378e1f2db3d54/pywinpty-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c9081df0e49ffa86d15db4a6ba61530630e48707f987df42c9d3313537e81fc0", size = 2113098 }, + { url = "https://files.pythonhosted.org/packages/b9/53/7278223c493ccfe4883239cf06c823c56460a8010e0fc778eef67858dc14/pywinpty-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:15e79d870e18b678fb8a5a6105fd38496b55697c66e6fc0378236026bc4d59e9", size = 234901 }, + { url = "https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab", size = 2112686 }, + { url = "https://files.pythonhosted.org/packages/fd/50/724ed5c38c504d4e58a88a072776a1e880d970789deaeb2b9f7bd9a5141a/pywinpty-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:fe1f7911805127c94cf51f89ab14096c6f91ffdcacf993d2da6082b2142a2523", size = 234591 }, + { url = "https://files.pythonhosted.org/packages/f7/ad/90a110538696b12b39fd8758a06d70ded899308198ad2305ac68e361126e/pywinpty-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:3f07a6cf1c1d470d284e614733c3d0f726d2c85e78508ea10a403140c3c0c18a", size = 2112360 }, + { url = "https://files.pythonhosted.org/packages/44/0f/7ffa221757a220402bc79fda44044c3f2cc57338d878ab7d622add6f4581/pywinpty-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:15c7c0b6f8e9d87aabbaff76468dabf6e6121332c40fc1d83548d02a9d6a3759", size = 233107 }, + { url = "https://files.pythonhosted.org/packages/28/88/2ff917caff61e55f38bcdb27de06ee30597881b2cae44fbba7627be015c4/pywinpty-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:d4b6b7b0fe0cdcd02e956bd57cfe9f4e5a06514eecf3b5ae174da4f951b58be9", size = 2113282 }, + { url = "https://files.pythonhosted.org/packages/63/32/40a775343ace542cc43ece3f1d1fce454021521ecac41c4c4573081c2336/pywinpty-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:34789d685fc0d547ce0c8a65e5a70e56f77d732fa6e03c8f74fefb8cbb252019", size = 234207 }, + { url = "https://files.pythonhosted.org/packages/8d/54/5d5e52f4cb75028104ca6faf36c10f9692389b1986d34471663b4ebebd6d/pywinpty-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c37e224a47a971d1a6e08649a1714dac4f63c11920780977829ed5c8cadead1", size = 2112910 }, + { url = "https://files.pythonhosted.org/packages/0a/44/dcd184824e21d4620b06c7db9fbb15c3ad0a0f1fa2e6de79969fb82647ec/pywinpty-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c4e9c3dff7d86ba81937438d5819f19f385a39d8f592d4e8af67148ceb4f6ab5", size = 233425 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328 }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803 }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836 }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038 }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531 }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786 }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220 }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155 }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428 }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436 }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301 }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275 }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469 }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961 }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282 }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468 }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394 }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964 }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029 }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541 }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427 }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929 }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193 }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388 }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316 }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472 }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401 }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170 }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265 }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208 }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862 }, +] + +[[package]] +name = "rangehttpserver" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/3b/1ec139d6028c6e5eb10301f040d6eee5c5427a4b1b4d614a2f78d3bba1bd/rangehttpserver-1.4.0.tar.gz", hash = "sha256:d5ddccee219b359598e41da0c5fbf30a2579297094f5a682755e2586388a5306", size = 6993 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/43/d7e2b9ad768c07b5473bea3ac7db9ca4d995c09399cbea3d4df1c0bd4955/rangehttpserver-1.4.0-py2.py3-none-any.whl", hash = "sha256:2a0c6926e4341de4cc19ec861292b005e4194ff497b1eefdeccb2992a5045452", size = 7773 }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075 }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, +] + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, +] + +[[package]] +name = "rfc3987-syntax" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lark" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654 }, +] + +[[package]] +name = "roman-numerals" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676 }, +] + +[[package]] +name = "rpds-py" +version = "2026.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/a0/acf8b6fc20bfdcd3a45bd3f57680fb198e157b7e997b9123b10763798bd2/rpds_py-2026.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3397a5ed7174dc2786bb214030232fc36fe8e5584fec43a9952cc542b1a12036", size = 355609 }, + { url = "https://files.pythonhosted.org/packages/b6/95/f8203fd997484b1690a6869cd0e503b6c3c6be55b0ecc36d1a491fe742f0/rpds_py-2026.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:99ab6ba7bfa2cb0f96a04e3652355bf04e3f51aceb1e943b8541dab7ba4828cc", size = 348460 }, + { url = "https://files.pythonhosted.org/packages/33/8c/b47326ad2f0be545a5e5c1a55937a12afaea7d392ba2837bb9680f57e6c9/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0efbe45632665e53e3db8fe1e5692db58fc5cb9bab4459d570b83efefe11164", size = 381031 }, + { url = "https://files.pythonhosted.org/packages/22/0b/e83bbd97ffac6f6389b605cd4e1c8ac5761dc7e977769c9255d8c5adb7bd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:01d17b29c0c23d82b1f4751147ec49cf451f1fc2554eb9ef5f957e55d2656ead", size = 387121 }, + { url = "https://files.pythonhosted.org/packages/fd/0e/d285d1bc8864245919c61e1ca82263e4a66d337759c3a4cef72766ff9afc/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7559f72b94ae52659086c595dfa017cde03155f7832071d30959049052cb3ece", size = 501026 }, + { url = "https://files.pythonhosted.org/packages/86/06/ccb2109a1e543437b5e43816f2b43b9554cc6783145528a4e3711e05c011/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e25b7088f9ccbfc0dfcaa52bf969300ca229e10ecf758974ebcbb080a4b37bb", size = 391865 }, + { url = "https://files.pythonhosted.org/packages/3d/33/237173db1cfef10105b3839a24de00eb8d2a523711add4632447cdf0aedd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613fc4ee9eaef26dc5840666214dd6fbcebcf32f46e76f4abc473059f4e13dda", size = 378012 }, + { url = "https://files.pythonhosted.org/packages/97/64/1eae54e34d5161f9969295e80bd6b62a55f2b6ac5f2a5b60d02c2140e758/rpds_py-2026.5.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:85264a90ff4c05c1568dd65f5921c837614b67c60358fb4c17df3b7f2e90690a", size = 391111 }, + { url = "https://files.pythonhosted.org/packages/d8/34/5bb334a5a0f65d77869217c4654f34c78a7d11b93938a3c076a2edeafc52/rpds_py-2026.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe71bca7d547acb17027c7fd1624ff8aae623499c498d3e7011182c4de5c25e0", size = 409225 }, + { url = "https://files.pythonhosted.org/packages/16/0f/007ec21283b5b040b4ec3bd95e0402591e22bfa7d5c93dfe01c465c2d2d7/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05fa4f41f37ec97c9c260441a940450a192f78d774d2b097eee1379f1e1246a", size = 556487 }, + { url = "https://files.pythonhosted.org/packages/ff/10/5437c94508169b6b22d8418fef7a66e9ffb5f3b9e9c94460f2eedafe06ff/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df1d2a1996755b24b9ecee92cb4d36c28f86f464a6a173349c26bab41e94b8c2", size = 620798 }, + { url = "https://files.pythonhosted.org/packages/e0/d5/9937dce4d6bda74157b954e7d1460db05a22f5929dccfeeba1ed27a93df0/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8895840ac4809e5f60c88fd07617cd71326e73d6e5a8aa783c5c0f7c24985de2", size = 584053 }, + { url = "https://files.pythonhosted.org/packages/6c/31/750617dd0ae1752471bf43f9e41d263398fae7cde7849d23b8574a70e617/rpds_py-2026.5.1-cp311-cp311-win32.whl", hash = "sha256:3684a59b158a7683aaeb8e25352e9a9dd2122cec78f2d8530266e4f91b4c7b3f", size = 214390 }, + { url = "https://files.pythonhosted.org/packages/3c/bb/3dcab0e1d9516303f2eb672a5d6f62eca5a69e2886301e9c8c54b520c39b/rpds_py-2026.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:7bd530e6a530bb3ea892f194fafa455f3516ac25ecf7143fd33c09be62b0470a", size = 231097 }, + { url = "https://files.pythonhosted.org/packages/49/d6/c6bbf5cb1cf12b9732df8074b57f6ef8341ba884c95d40632ae8bddb44e4/rpds_py-2026.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:0a5ae4dbe43c1076983b72616496919872ae7bbe7a1e21cc48336bc3154d130b", size = 226361 }, + { url = "https://files.pythonhosted.org/packages/d4/e7/a78582dc57caa592dcc7d4fb69b61390561e908eb3d2f5df5928a8e354c0/rpds_py-2026.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d", size = 353040 }, + { url = "https://files.pythonhosted.org/packages/a3/43/35e3f136343aef451e545ce8c38d36c2f93c0ed88703db8b64ba2b205c68/rpds_py-2026.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c", size = 345775 }, + { url = "https://files.pythonhosted.org/packages/20/e1/0f2160c5982d3157734d5cb3ed63d8b2d583a73c9864f77b666449f32cf8/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08", size = 376329 }, + { url = "https://files.pythonhosted.org/packages/d0/11/ee0ba42aff83bf4effdbc576673c6be64c5e173978c3f6d537e94482f77d/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb", size = 383539 }, + { url = "https://files.pythonhosted.org/packages/11/df/d94aa6a499d4ac40afe2d7620f2c597fd3c0f182e854ad7cf3f596a81cb6/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1", size = 494674 }, + { url = "https://files.pythonhosted.org/packages/1f/75/33d30f43bb2f458de11979486a591b1bf6e5651765ed1704c6197c2dc773/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5", size = 389268 }, + { url = "https://files.pythonhosted.org/packages/f4/1e/2c9096fc19d5fd084b0184ca2b651e659aa0a37e6fdbecf6ece47f147fe1/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644", size = 376280 }, + { url = "https://files.pythonhosted.org/packages/b9/e5/61ec9f8be8211ea7f48448195549e4aaf02004083475493b0e137702ecb2/rpds_py-2026.5.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4", size = 387233 }, + { url = "https://files.pythonhosted.org/packages/0d/ca/bcec1005c4f4a234f92a29078631fee49206c7265ccae966f18fd332e80e/rpds_py-2026.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6", size = 405009 }, + { url = "https://files.pythonhosted.org/packages/72/e6/4d5718c5cf26c522dc7c9999e238da1e77380b81d0c5d1df11e271ddfeb1/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4", size = 553113 }, + { url = "https://files.pythonhosted.org/packages/d4/25/2ee807bdb3e1f0b7eddf7782acd5665a8b5205a331a7d7244a52c4812fd9/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24", size = 618838 }, + { url = "https://files.pythonhosted.org/packages/6a/c1/7d4c26f167f8c41501cc073d30ee22082b16ce358cf5b00ec97cbc7804ea/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732", size = 582436 }, + { url = "https://files.pythonhosted.org/packages/04/1d/9d12b0a337bab46f4769f8857f4007e3b2d639e14f9a44a0efe157696e64/rpds_py-2026.5.1-cp312-cp312-win32.whl", hash = "sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed", size = 212734 }, + { url = "https://files.pythonhosted.org/packages/c5/93/e4116f2de7f56bc7406a76033dc501811ddeb22b7f056b92d632871ebb0c/rpds_py-2026.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870", size = 229045 }, + { url = "https://files.pythonhosted.org/packages/cb/53/6c3419d85eb2ec5938a37627c585b42d76a63bb731d6e42ed4b079ebf486/rpds_py-2026.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473", size = 223967 }, + { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787 }, + { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179 }, + { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173 }, + { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162 }, + { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093 }, + { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829 }, + { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786 }, + { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920 }, + { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059 }, + { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030 }, + { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975 }, + { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178 }, + { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481 }, + { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519 }, + { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446 }, + { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287 }, + { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033 }, + { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891 }, + { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646 }, + { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830 }, + { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830 }, + { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613 }, + { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183 }, + { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578 }, + { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573 }, + { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861 }, + { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633 }, + { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074 }, + { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635 }, + { url = "https://files.pythonhosted.org/packages/d4/6f/19c1918a4b590d8de87e712e4abe4b3875771eff60216fb6153cf6665c68/rpds_py-2026.5.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9", size = 349756 }, + { url = "https://files.pythonhosted.org/packages/e5/60/a06fe7da34eca79dacbf958a2ba0c6eea85bc2b29de20080bf40f72f66fa/rpds_py-2026.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78", size = 343831 }, + { url = "https://files.pythonhosted.org/packages/bf/ec/b2333b97b90e2a6ef6ca8ad386ee284968e74bcfe113b3f1a8d9036429a9/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63", size = 375127 }, + { url = "https://files.pythonhosted.org/packages/14/7f/e00aae54067f2b488c4637961d5f58204d470795fc791085fa3f15060d2e/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a", size = 379034 }, + { url = "https://files.pythonhosted.org/packages/be/cc/423999bbb8ae8dc93c77fc1d5e984ade5eb89d237d3bb884ccfa72ae2890/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195", size = 490823 }, + { url = "https://files.pythonhosted.org/packages/0f/aa/c671bf660f12e68d3c52ff86c7066ed1372df5a0f4f2ff584e419b8207e7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee", size = 388144 }, + { url = "https://files.pythonhosted.org/packages/19/c8/d63bb75b68afe77b229e3021c6031bcaf01da5db5b0e69d0d10f9ba679a7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba", size = 371959 }, + { url = "https://files.pythonhosted.org/packages/82/35/c51122014d8274ff37dc606d60049c3db7d83da02b5b282511e5a906a9a6/rpds_py-2026.5.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec", size = 383558 }, + { url = "https://files.pythonhosted.org/packages/e3/f9/2790cb99c136a5363acdeacf5c27c56f3de0d4118a1f48fca83404c99c89/rpds_py-2026.5.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d", size = 402789 }, + { url = "https://files.pythonhosted.org/packages/e5/1b/e4fb584f8c75d35c38150ff6a332cda949e6f97acba1f4fd123b14ab56fe/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d", size = 551405 }, + { url = "https://files.pythonhosted.org/packages/d8/f7/a6731b4216cb3793ea1af5391da240f5683dacc0d13e034fe5fc3503f240/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02", size = 616975 }, + { url = "https://files.pythonhosted.org/packages/2c/ea/2e051a81d95d8e63f4b35a1c463a87e8766bc3d083c067c5dfb6bf220747/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0", size = 578701 }, + { url = "https://files.pythonhosted.org/packages/65/56/b5f6fdb2083e32bca8a8993d89e70db114b4756c9e2c38421328126689d2/rpds_py-2026.5.1-cp314-cp314-win32.whl", hash = "sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7", size = 209806 }, + { url = "https://files.pythonhosted.org/packages/fb/80/65a5aa96c155e611d1ed844e4e1f57f3e36b021f396d9f8585d756e6b90d/rpds_py-2026.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838", size = 225985 }, + { url = "https://files.pythonhosted.org/packages/27/7c/ad185212e87b05f196daef92bc5f3caf07298eb47c295b5585c3dd3093ac/rpds_py-2026.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8", size = 221219 }, + { url = "https://files.pythonhosted.org/packages/23/58/e14ae18759020334646b031e708ab4158d653a938822bfb7b95ef2e93aa3/rpds_py-2026.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad", size = 352148 }, + { url = "https://files.pythonhosted.org/packages/31/9b/5f4a1e2f960bca3ac5d052b139dd31eed97b259f9d909173821760d542e8/rpds_py-2026.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3", size = 345196 }, + { url = "https://files.pythonhosted.org/packages/1a/71/1d9574d6a2fa20ab60eaa55c7467f5aa20cbc770f341a05f09c0876f59e2/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081", size = 374981 }, + { url = "https://files.pythonhosted.org/packages/0c/9a/37e99f4915a80aa71670263c1267f7ae0af95f53a3f61e6c3bdc016d4515/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6", size = 379961 }, + { url = "https://files.pythonhosted.org/packages/a8/ff/6e73f74b89d2e0715e0fc86b7dde893f9a61ae2f9b256ff3bdfe41ac4e94/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5", size = 495965 }, + { url = "https://files.pythonhosted.org/packages/ea/e0/425faba25f59d74d4638b267f7c7a80e8649d2ef4db10a19b0c4a71e6e6f/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b", size = 389526 }, + { url = "https://files.pythonhosted.org/packages/c6/76/7a41960e3fddae47fab43a28684d5da981401dffd88253de0944148654cb/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964", size = 376190 }, + { url = "https://files.pythonhosted.org/packages/27/60/5f38dc70824fc6951b51d35377e577a3a3a4c81a6769cc5a2de25ebe0ad1/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131", size = 383921 }, + { url = "https://files.pythonhosted.org/packages/60/1a/d60a38caa1505f4b9483c3fbbde12c94e1079154f4f401a6da96f7e77621/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81", size = 404766 }, + { url = "https://files.pythonhosted.org/packages/87/ff/602fd3f174d6425f0bce05ad0dfbec0e96b38d0f7d08a79af5aa20083885/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47", size = 551343 }, + { url = "https://files.pythonhosted.org/packages/b8/c1/1be13327acdbead3eca1fde03b6a34dbb011f1e864e217f0d32cc1779a7f/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a", size = 618502 }, + { url = "https://files.pythonhosted.org/packages/f3/d7/afb49b49d7f2be8b7ba1a9f0977fa5168003437b93086726f066544e8351/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca", size = 581916 }, + { url = "https://files.pythonhosted.org/packages/25/d1/dbef8c1f8a10f07beb62b5f054e20099fd9924b3ec001b8f0b6ac7813a85/rpds_py-2026.5.1-cp314-cp314t-win32.whl", hash = "sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a", size = 207855 }, + { url = "https://files.pythonhosted.org/packages/2a/72/bfa4e61ab8e7dc1c8adf397e05e6cbdd4239357bd72b248d3de662f23915/rpds_py-2026.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6", size = 225422 }, + { url = "https://files.pythonhosted.org/packages/27/3a/7b5da92b640f67b6717ccafc83cdd06bfa7ff2395c3685c68922bb54d703/rpds_py-2026.5.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb", size = 349576 }, + { url = "https://files.pythonhosted.org/packages/d7/8a/2aafd7ad355a1bd48ca76e2262b74b15e6432b5a1efe150efd4d779cd55d/rpds_py-2026.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291", size = 343640 }, + { url = "https://files.pythonhosted.org/packages/f7/7d/6c9523c1abbe840a1b7fba3c516d48e1d3487cc80fea4366c4071cf56784/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1", size = 375322 }, + { url = "https://files.pythonhosted.org/packages/5a/5d/0b7b03fb1dc509321f01de3149784ab773e34c8573022029af8076afcb9c/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8", size = 379066 }, + { url = "https://files.pythonhosted.org/packages/d7/e2/8ef6012999ebf1cb1c22f876d9ce5e63d960fd4631d2af3202d3f480aa25/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2", size = 494586 }, + { url = "https://files.pythonhosted.org/packages/80/af/1eeb029bec67582c226b7809172207cd005073af4ebd906e65ff494f4983/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038", size = 388415 }, + { url = "https://files.pythonhosted.org/packages/18/23/ffbe10711c4d766c1cab0557d6906c074f795814863c67b351355d29354a/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26", size = 372427 }, + { url = "https://files.pythonhosted.org/packages/bd/3a/30ba4a6ad457e5b070c18d742a33fb77d8d922b565cc881f8a5313d63bfe/rpds_py-2026.5.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd", size = 383615 }, + { url = "https://files.pythonhosted.org/packages/d3/69/62e242b53ce39c0814bd24e1a6e6eba6c92be716277745f317f9540a2e7b/rpds_py-2026.5.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9", size = 402786 }, + { url = "https://files.pythonhosted.org/packages/38/c1/a770b9c186928a1ed0f7e6d7ae50e7f3950ed23e3f9e366dbc8e38cb55de/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14", size = 551583 }, + { url = "https://files.pythonhosted.org/packages/21/7c/68e8579b95375b70d2a963103c42e705856cdb98569258bd807f4423891c/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01", size = 616941 }, + { url = "https://files.pythonhosted.org/packages/70/a1/a6135aed5730ff03ab957182259987ac11e55fb392a28dc6f0592048a280/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d", size = 578349 }, + { url = "https://files.pythonhosted.org/packages/09/6e/f24201a76a84e6c49d0bdfdfcb735210e21701e9b21c5bfc0ba497dd62f6/rpds_py-2026.5.1-cp315-cp315-win32.whl", hash = "sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa", size = 209922 }, + { url = "https://files.pythonhosted.org/packages/9e/e4/966bc240bb0485fc265278f6de44d05834bf0b3618886e0b22e33d54c49a/rpds_py-2026.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325", size = 226003 }, + { url = "https://files.pythonhosted.org/packages/5c/5c/a15a59269cd5e74472734516c73795c15eccfc841b3d4b0228c3f53f19d0/rpds_py-2026.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16", size = 221245 }, + { url = "https://files.pythonhosted.org/packages/e0/22/135ce03804e179a71ceb13be095deda4a279bc88f7a6b8fa161c5ad44e12/rpds_py-2026.5.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723", size = 352015 }, + { url = "https://files.pythonhosted.org/packages/3b/5f/f1f6d2652eb9d848f6eb369d8db83a2da6249bb49ad2c2a48f45d54538d3/rpds_py-2026.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41", size = 345016 }, + { url = "https://files.pythonhosted.org/packages/88/66/b74182775691ea2290c99e52ac8d5db844e56fbec90ce421f107658c8314/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a", size = 374775 }, + { url = "https://files.pythonhosted.org/packages/ff/8f/15e5a61d9f0a43902d36561d4f07cae6ae9f4716be825159fd72717f33af/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358", size = 380270 }, + { url = "https://files.pythonhosted.org/packages/02/c3/f859b12763a80540cdf2af0f15b19904cf756a71d7bdd3f82ff3e5b1bbf9/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb", size = 495285 }, + { url = "https://files.pythonhosted.org/packages/1c/c7/ff27c2ac8411d30b03b1829fd88cae8dad1a4d0da48dd25e57c4038042e6/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b", size = 389581 }, + { url = "https://files.pythonhosted.org/packages/6e/67/fe92ee32a6cc05c77228a2f8b1762e7124f386ec20ff83d0757b762d58d0/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc", size = 376041 }, + { url = "https://files.pythonhosted.org/packages/f8/91/b4d6685c27aba55bd82f25b278be8237038117d05f9659a6213ad3408130/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015", size = 383946 }, + { url = "https://files.pythonhosted.org/packages/bd/79/2c1d832a53c8e0f8e98fc970ec257b950fecd4f62be2ab7182b500a0cbc8/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa", size = 405526 }, + { url = "https://files.pythonhosted.org/packages/78/c4/c98117b03c6a8581ab2c2dfccfe9a5ad82bd8128a3c28b46a6ad2d97c393/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972", size = 551165 }, + { url = "https://files.pythonhosted.org/packages/3b/c1/bc479ca069200af730881b1bd525e3114b2b391a351509fcb1b772f28086/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66", size = 618778 }, + { url = "https://files.pythonhosted.org/packages/77/65/38ab2f90df44c2febfb63cc10ced40763d9b4bc94d173e734528663fe7f5/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb", size = 581839 }, + { url = "https://files.pythonhosted.org/packages/15/2d/ce1f605fe036aadd460e5822e578c6c7ec3a860936cca37d6e0f299daa77/rpds_py-2026.5.1-cp315-cp315t-win32.whl", hash = "sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df", size = 207866 }, + { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441 }, + { url = "https://files.pythonhosted.org/packages/42/56/3fe0fb34820ff667be791b3a3c22b85e8bcba54e9c832f47438c191fa7be/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:edf2765d84e42447f112ad877af8fe1db0089aaec5b28e88d6eab45e7fe99cea", size = 357151 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/3eb9ccdb9f143b8c9b003978898cb497f942a324c077401e6b8834238e63/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad3773236e95f7f33991eb125224b7da66f206504d032a253a02da7e134519fb", size = 350195 }, + { url = "https://files.pythonhosted.org/packages/a7/24/dbda232bc4f3ed732120692ab0d2c8402cb020516556d8bee622dcef2413/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04df86b3f0fade39ec8fd0e0aab089b1da9fbd2b48df778a57ef96f5e7d38df", size = 381850 }, + { url = "https://files.pythonhosted.org/packages/40/30/32e769839a358f78810c234f160f2cc21d1e4e47e1c0e0e0d535be5a0219/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6142dbd80c4df62a5d899f0d616d417f84e0bc8d32526c8e5589019d75d028a7", size = 387899 }, + { url = "https://files.pythonhosted.org/packages/ab/86/ec84d243aadb3b34b71dd26a010d0930b2d284ff5fc9a69fec53810ee6fd/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b35217adefe87f2fe4db7e9766cabe84744bfe9616d9667be18988928c7f2dc", size = 501618 }, + { url = "https://files.pythonhosted.org/packages/74/25/b60e52686bbff777a64f9e4f4d3dd57980dc846913777177a2c92e4937aa/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b95d5e11fc712b752081183a55a244c03cd00570489edd7014d8899f8ceb8162", size = 394003 }, + { url = "https://files.pythonhosted.org/packages/9b/c7/b3a6a588cc2219510ef3f42e207483a93950bedd1e3a0fd4015c95cff9e5/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141c9498daf2ace9eda35d2b0e376f9ea8b058d84f2aef4f96fccfd449a2f251", size = 379778 }, + { url = "https://files.pythonhosted.org/packages/31/00/c7dba3fc8a3da8cb3f6db1eb3386be4d79c2e97c6890d20eb9ac66ae8c43/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:6f249f8b860a200ad35193af961183ebe9132710484e6f6ce0cf89fd83c63a9a", size = 392359 }, + { url = "https://files.pythonhosted.org/packages/93/dd/472ba494c70753f93745992c99855bee0636daf74e6984e5e003f150316f/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4abbf391a70be864920858bf360f4fb380577c9a0f732438a1996726e2c195b", size = 412820 }, + { url = "https://files.pythonhosted.org/packages/1d/6f/93831a3bfe789542ed0c1d0d74b78b440f055d6dc3ea4640eba2d95e6e23/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c74005a7bb87752acf351c93897ec63ad77a07a0da7ecad9c050e32e7286ba34", size = 557243 }, + { url = "https://files.pythonhosted.org/packages/1f/ff/0b3d604614ffc77522c6b288fdbce68957eb583da1002aa65ba38ac0ee40/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:8213afbe8a3a906fb9acb2014423fe3359ee783d0bf90995f70623a3217bfa6c", size = 623541 }, + { url = "https://files.pythonhosted.org/packages/ea/ea/e7b0251441da9adfeaebcf29601d10f2a1455fcf0772fae9e7e19032bd96/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049", size = 586326 }, +] + +[[package]] +name = "ruff" +version = "0.15.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652 }, + { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615 }, + { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683 }, + { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064 }, + { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987 }, + { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100 }, + { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903 }, + { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550 }, + { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027 }, + { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041 }, + { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795 }, + { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117 }, + { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867 }, + { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101 }, + { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774 }, + { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463 }, + { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498 }, +] + +[[package]] +name = "s3fs" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiobotocore" }, + { name = "aiohttp" }, + { name = "fsspec" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/d8/76f3dc1558bdf4494b117a9f7a9cc0a5d9d34edadc9e5d7ceabc5a6a7c37/s3fs-2026.4.0.tar.gz", hash = "sha256:5bdce0abb00b0435ee150807a45fea727451dbc22de4cbc116464f8504ab9d37", size = 85986 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a4/9d1ea10ebc9e028a289a72fec84da170689549a8102c8aacfcad26bc5035/s3fs-2026.4.0-py3-none-any.whl", hash = "sha256:de0d2a1f33cdf03831fd2382d278c6e4e31fe57c3bf2f703c61f8aec6b703e2a", size = 32392 }, +] + +[[package]] +name = "scanpy" +version = "1.11.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "anndata", marker = "python_full_version < '3.12'" }, + { name = "h5py", marker = "python_full_version < '3.12'" }, + { name = "joblib", marker = "python_full_version < '3.12'" }, + { name = "legacy-api-wrap", marker = "python_full_version < '3.12'" }, + { name = "matplotlib", marker = "python_full_version < '3.12'" }, + { name = "natsort", marker = "python_full_version < '3.12'" }, + { name = "networkx", marker = "python_full_version < '3.12'" }, + { name = "numba", marker = "python_full_version < '3.12'" }, + { name = "numpy", marker = "python_full_version < '3.12'" }, + { name = "packaging", marker = "python_full_version < '3.12'" }, + { name = "pandas", marker = "python_full_version < '3.12'" }, + { name = "patsy", marker = "python_full_version < '3.12'" }, + { name = "pynndescent", marker = "python_full_version < '3.12'" }, + { name = "scikit-learn", marker = "python_full_version < '3.12'" }, + { name = "scipy", marker = "python_full_version < '3.12'" }, + { name = "seaborn", marker = "python_full_version < '3.12'" }, + { name = "session-info2", marker = "python_full_version < '3.12'" }, + { name = "statsmodels", marker = "python_full_version < '3.12'" }, + { name = "tqdm", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "umap-learn", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/a8/285f1a9c995906b7e0ae3c399208fe67cfba8126dd31359dfef0908f6edc/scanpy-1.11.5.tar.gz", hash = "sha256:b2ef5476dfb1144b7dd0fae90b0198699c7988e6b27f083904150642c7ba6b89", size = 14088122 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/e9/c1d43543da87cd27e8e2a74db85cf0b6c5cff2d5f04a86bd584d2fbc2bb0/scanpy-1.11.5-py3-none-any.whl", hash = "sha256:fcd383ddcf7acbf7c0ca232c25ad51b00aec9f8d2f7c8954b8c6ee0962257166", size = 2097836 }, +] + +[package.optional-dependencies] +leiden = [ + { name = "igraph", marker = "python_full_version < '3.12'" }, + { name = "leidenalg", marker = "python_full_version < '3.12'" }, +] + +[[package]] +name = "scanpy" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "anndata", marker = "python_full_version >= '3.12'" }, + { name = "certifi", marker = "python_full_version >= '3.12'" }, + { name = "fast-array-utils", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, extra = ["accel", "sparse"], marker = "python_full_version >= '3.12'" }, + { name = "h5py", marker = "python_full_version >= '3.12'" }, + { name = "joblib", marker = "python_full_version >= '3.12'" }, + { name = "legacy-api-wrap", marker = "python_full_version >= '3.12'" }, + { name = "matplotlib", marker = "python_full_version >= '3.12'" }, + { name = "natsort", marker = "python_full_version >= '3.12'" }, + { name = "networkx", marker = "python_full_version >= '3.12'" }, + { name = "numba", marker = "python_full_version >= '3.12'" }, + { name = "numpy", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pandas", marker = "python_full_version >= '3.12'" }, + { name = "patsy", marker = "python_full_version >= '3.12'" }, + { name = "pynndescent", marker = "python_full_version >= '3.12'" }, + { name = "scikit-learn", marker = "python_full_version >= '3.12'" }, + { name = "scipy", marker = "python_full_version >= '3.12'" }, + { name = "seaborn", marker = "python_full_version >= '3.12'" }, + { name = "session-info2", marker = "python_full_version >= '3.12'" }, + { name = "statsmodels", marker = "python_full_version >= '3.12'" }, + { name = "tqdm", marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions", marker = "python_full_version == '3.12.*'" }, + { name = "umap-learn", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/18/1ed26bd2231d9bbe9bfe6ba678fdd261d1ec664ab1bca1d30401dc2a9193/scanpy-1.12.1.tar.gz", hash = "sha256:489c6a3763311721089bf0d78e65fd1555606b45f1a9ee97aa85291a12ade67a", size = 14418570 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/e4/b648ac3267001e1582e0b6ffc22b16dada83e206d783115992e83a07773f/scanpy-1.12.1-py3-none-any.whl", hash = "sha256:d482c92d0d7ae7b954a603e16dfcf5f062c511bd5e30e5dfd701c28e1d6b0e78", size = 2148436 }, +] + +[package.optional-dependencies] +leiden = [ + { name = "igraph", marker = "python_full_version >= '3.12'" }, + { name = "leidenalg", marker = "python_full_version >= '3.12'" }, +] + +[[package]] +name = "scikit-image" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio" }, + { name = "lazy-loader" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "scipy" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/16/8a407688b607f86f81f8c649bf0d68a2a6d67375f18c2d660aba20f5b648/scikit_image-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1ede33a0fb3731457eaf53af6361e73dd510f449dac437ab54573b26788baf0", size = 12355510 }, + { url = "https://files.pythonhosted.org/packages/6b/f9/7efc088ececb6f6868fd4475e16cfafc11f242ce9ab5fc3557d78b5da0d4/scikit_image-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7af7aa331c6846bd03fa28b164c18d0c3fd419dbb888fb05e958ac4257a78fdd", size = 12056334 }, + { url = "https://files.pythonhosted.org/packages/9f/1e/bc7fb91fb5ff65ef42346c8b7ee8b09b04eabf89235ab7dbfdfd96cbd1ea/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea6207d9e9d21c3f464efe733121c0504e494dbdc7728649ff3e23c3c5a4953", size = 13297768 }, + { url = "https://files.pythonhosted.org/packages/a5/2a/e71c1a7d90e70da67b88ccc609bd6ae54798d5847369b15d3a8052232f9d/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74aa5518ccea28121f57a95374581d3b979839adc25bb03f289b1bc9b99c58af", size = 13711217 }, + { url = "https://files.pythonhosted.org/packages/d4/59/9637ee12c23726266b91296791465218973ce1ad3e4c56fc81e4d8e7d6e1/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d5c244656de905e195a904e36dbc18585e06ecf67d90f0482cbde63d7f9ad59d", size = 14337782 }, + { url = "https://files.pythonhosted.org/packages/e7/5c/a3e1e0860f9294663f540c117e4bf83d55e5b47c281d475cc06227e88411/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21a818ee6ca2f2131b9e04d8eb7637b5c18773ebe7b399ad23dcc5afaa226d2d", size = 14805997 }, + { url = "https://files.pythonhosted.org/packages/d3/c6/2eeacf173da041a9e388975f54e5c49df750757fcfc3ee293cdbbae1ea0a/scikit_image-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:9490360c8d3f9a7e85c8de87daf7c0c66507960cf4947bb9610d1751928721c7", size = 11878486 }, + { url = "https://files.pythonhosted.org/packages/c3/a4/a852c4949b9058d585e762a66bf7e9a2cd3be4795cd940413dfbfbb0ce79/scikit_image-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:0baa0108d2d027f34d748e84e592b78acc23e965a5de0e4bb03cf371de5c0581", size = 11346518 }, + { url = "https://files.pythonhosted.org/packages/99/e8/e13757982264b33a1621628f86b587e9a73a13f5256dad49b19ba7dc9083/scikit_image-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d454b93a6fa770ac5ae2d33570f8e7a321bb80d29511ce4b6b78058ebe176e8c", size = 12376452 }, + { url = "https://files.pythonhosted.org/packages/e3/be/f8dd17d0510f9911f9f17ba301f7455328bf13dae416560126d428de9568/scikit_image-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3409e89d66eff5734cd2b672d1c48d2759360057e714e1d92a11df82c87cba37", size = 12061567 }, + { url = "https://files.pythonhosted.org/packages/b3/2b/c70120a6880579fb42b91567ad79feb4772f7be72e8d52fec403a3dde0c6/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c717490cec9e276afb0438dd165b7c3072d6c416709cc0f9f5a4c1070d23a44", size = 13084214 }, + { url = "https://files.pythonhosted.org/packages/f4/a2/70401a107d6d7466d64b466927e6b96fcefa99d57494b972608e2f8be50f/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df650e79031634ac90b11e64a9eedaf5a5e06fcd09bcd03a34be01745744466", size = 13561683 }, + { url = "https://files.pythonhosted.org/packages/13/a5/48bdfd92794c5002d664e0910a349d0a1504671ef5ad358150f21643c79a/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cefd85033e66d4ea35b525bb0937d7f42d4cdcfed2d1888e1570d5ce450d3932", size = 14112147 }, + { url = "https://files.pythonhosted.org/packages/ee/b5/ac71694da92f5def5953ca99f18a10fe98eac2dd0a34079389b70b4d0394/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f5bf622d7c0435884e1e141ebbe4b2804e16b2dd23ae4c6183e2ea99233be70", size = 14661625 }, + { url = "https://files.pythonhosted.org/packages/23/4d/a3cc1e96f080e253dad2251bfae7587cf2b7912bcd76fd43fd366ff35a87/scikit_image-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:abed017474593cd3056ae0fe948d07d0747b27a085e92df5474f4955dd65aec0", size = 11911059 }, + { url = "https://files.pythonhosted.org/packages/35/8a/d1b8055f584acc937478abf4550d122936f420352422a1a625eef2c605d8/scikit_image-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d57e39ef67a95d26860c8caf9b14b8fb130f83b34c6656a77f191fa6d1d04d8", size = 11348740 }, + { url = "https://files.pythonhosted.org/packages/4f/48/02357ffb2cca35640f33f2cfe054a4d6d5d7a229b88880a64f1e45c11f4e/scikit_image-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a2e852eccf41d2d322b8e60144e124802873a92b8d43a6f96331aa42888491c7", size = 12346329 }, + { url = "https://files.pythonhosted.org/packages/67/b9/b792c577cea2c1e94cda83b135a656924fc57c428e8a6d302cd69aac1b60/scikit_image-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98329aab3bc87db352b9887f64ce8cdb8e75f7c2daa19927f2e121b797b678d5", size = 12031726 }, + { url = "https://files.pythonhosted.org/packages/07/a9/9564250dfd65cb20404a611016db52afc6268b2b371cd19c7538ea47580f/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915bb3ba66455cf8adac00dc8fdf18a4cd29656aec7ddd38cb4dda90289a6f21", size = 13094910 }, + { url = "https://files.pythonhosted.org/packages/a3/b8/0d8eeb5a9fd7d34ba84f8a55753a0a3e2b5b51b2a5a0ade648a8db4a62f7/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b36ab5e778bf50af5ff386c3ac508027dc3aaeccf2161bdf96bde6848f44d21b", size = 13660939 }, + { url = "https://files.pythonhosted.org/packages/2f/d6/91d8973584d4793d4c1a847d388e34ef1218d835eeddecfc9108d735b467/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09bad6a5d5949c7896c8347424c4cca899f1d11668030e5548813ab9c2865dcb", size = 14138938 }, + { url = "https://files.pythonhosted.org/packages/39/9a/7e15d8dc10d6bbf212195fb39bdeb7f226c46dd53f9c63c312e111e2e175/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aeb14db1ed09ad4bee4ceb9e635547a8d5f3549be67fc6c768c7f923e027e6cd", size = 14752243 }, + { url = "https://files.pythonhosted.org/packages/8f/58/2b11b933097bc427e42b4a8b15f7de8f24f2bac1fd2779d2aea1431b2c31/scikit_image-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac529eb9dbd5954f9aaa2e3fe9a3fd9661bfe24e134c688587d811a0233127f1", size = 11906770 }, + { url = "https://files.pythonhosted.org/packages/ad/ec/96941474a18a04b69b6f6562a5bd79bd68049fa3728d3b350976eccb8b93/scikit_image-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a2d211bc355f59725efdcae699b93b30348a19416cc9e017f7b2fb599faf7219", size = 11342506 }, + { url = "https://files.pythonhosted.org/packages/03/e5/c1a9962b0cf1952f42d32b4a2e48eed520320dbc4d2ff0b981c6fa508b6b/scikit_image-0.26.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9eefb4adad066da408a7601c4c24b07af3b472d90e08c3e7483d4e9e829d8c49", size = 12663278 }, + { url = "https://files.pythonhosted.org/packages/ae/97/c1a276a59ce8e4e24482d65c1a3940d69c6b3873279193b7ebd04e5ee56b/scikit_image-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6caec76e16c970c528d15d1c757363334d5cb3069f9cea93d2bead31820511f3", size = 12405142 }, + { url = "https://files.pythonhosted.org/packages/d4/4a/f1cbd1357caef6c7993f7efd514d6e53d8fd6f7fe01c4714d51614c53289/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07200fe09b9d99fcdab959859fe0f7db8df6333d6204344425d476850ce3604", size = 12942086 }, + { url = "https://files.pythonhosted.org/packages/5b/6f/74d9fb87c5655bd64cf00b0c44dc3d6206d9002e5f6ba1c9aeb13236f6bf/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92242351bccf391fc5df2d1529d15470019496d2498d615beb68da85fe7fdf37", size = 13265667 }, + { url = "https://files.pythonhosted.org/packages/a7/73/faddc2413ae98d863f6fa2e3e14da4467dd38e788e1c23346cf1a2b06b97/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:52c496f75a7e45844d951557f13c08c81487c6a1da2e3c9c8a39fcde958e02cc", size = 14001966 }, + { url = "https://files.pythonhosted.org/packages/02/94/9f46966fa042b5d57c8cd641045372b4e0df0047dd400e77ea9952674110/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20ef4a155e2e78b8ab973998e04d8a361d49d719e65412405f4dadd9155a61d9", size = 14359526 }, + { url = "https://files.pythonhosted.org/packages/5d/b4/2840fe38f10057f40b1c9f8fb98a187a370936bf144a4ac23452c5ef1baf/scikit_image-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c9087cf7d0e7f33ab5c46d2068d86d785e70b05400a891f73a13400f1e1faf6a", size = 12287629 }, + { url = "https://files.pythonhosted.org/packages/22/ba/73b6ca70796e71f83ab222690e35a79612f0117e5aaf167151b7d46f5f2c/scikit_image-0.26.0-cp313-cp313t-win_arm64.whl", hash = "sha256:27d58bc8b2acd351f972c6508c1b557cfed80299826080a4d803dd29c51b707e", size = 11647755 }, + { url = "https://files.pythonhosted.org/packages/51/44/6b744f92b37ae2833fd423cce8f806d2368859ec325a699dc30389e090b9/scikit_image-0.26.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:63af3d3a26125f796f01052052f86806da5b5e54c6abef152edb752683075a9c", size = 12365810 }, + { url = "https://files.pythonhosted.org/packages/40/f5/83590d9355191f86ac663420fec741b82cc547a4afe7c4c1d986bf46e4db/scikit_image-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ce00600cd70d4562ed59f80523e18cdcc1fae0e10676498a01f73c255774aefd", size = 12075717 }, + { url = "https://files.pythonhosted.org/packages/72/48/253e7cf5aee6190459fe136c614e2cbccc562deceb4af96e0863f1b8ee29/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6381edf972b32e4f54085449afde64365a57316637496c1325a736987083e2ab", size = 13161520 }, + { url = "https://files.pythonhosted.org/packages/73/c3/cec6a3cbaadfdcc02bd6ff02f3abfe09eaa7f4d4e0a525a1e3a3f4bce49c/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6624a76c6085218248154cc7e1500e6b488edcd9499004dd0d35040607d7505", size = 13684340 }, + { url = "https://files.pythonhosted.org/packages/d4/0d/39a776f675d24164b3a267aa0db9f677a4cb20127660d8bf4fd7fef66817/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f775f0e420faac9c2aa6757135f4eb468fb7b70e0b67fa77a5e79be3c30ee331", size = 14203839 }, + { url = "https://files.pythonhosted.org/packages/ee/25/2514df226bbcedfe9b2caafa1ba7bc87231a0c339066981b182b08340e06/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede4d6d255cc5da9faeb2f9ba7fedbc990abbc652db429f40a16b22e770bb578", size = 14770021 }, + { url = "https://files.pythonhosted.org/packages/8d/5b/0671dc91c0c79340c3fe202f0549c7d3681eb7640fe34ab68a5f090a7c7f/scikit_image-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:0660b83968c15293fd9135e8d860053ee19500d52bf55ca4fb09de595a1af650", size = 12023490 }, + { url = "https://files.pythonhosted.org/packages/65/08/7c4cb59f91721f3de07719085212a0b3962e3e3f2d1818cbac4eeb1ea53e/scikit_image-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:b8d14d3181c21c11170477a42542c1addc7072a90b986675a71266ad17abc37f", size = 11473782 }, + { url = "https://files.pythonhosted.org/packages/49/41/65c4258137acef3d73cb561ac55512eacd7b30bb4f4a11474cad526bc5db/scikit_image-0.26.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cde0bbd57e6795eba83cb10f71a677f7239271121dc950bc060482834a668ad1", size = 12686060 }, + { url = "https://files.pythonhosted.org/packages/e7/32/76971f8727b87f1420a962406388a50e26667c31756126444baf6668f559/scikit_image-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:163e9afb5b879562b9aeda0dd45208a35316f26cc7a3aed54fd601604e5cf46f", size = 12422628 }, + { url = "https://files.pythonhosted.org/packages/37/0d/996febd39f757c40ee7b01cdb861867327e5c8e5f595a634e8201462d958/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724f79fd9b6cb6f4a37864fe09f81f9f5d5b9646b6868109e1b100d1a7019e59", size = 12962369 }, + { url = "https://files.pythonhosted.org/packages/48/b4/612d354f946c9600e7dea012723c11d47e8d455384e530f6daaaeb9bf62c/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3268f13310e6857508bd87202620df996199a016a1d281b309441d227c822394", size = 13272431 }, + { url = "https://files.pythonhosted.org/packages/0a/6e/26c00b466e06055a086de2c6e2145fe189ccdc9a1d11ccc7de020f2591ad/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fac96a1f9b06cd771cbbb3cd96c5332f36d4efd839b1d8b053f79e5887acde62", size = 14016362 }, + { url = "https://files.pythonhosted.org/packages/47/88/00a90402e1775634043c2a0af8a3c76ad450866d9fa444efcc43b553ba2d/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c1e7bd342f43e7a97e571b3f03ba4c1293ea1a35c3f13f41efdc8a81c1dc8f2", size = 14364151 }, + { url = "https://files.pythonhosted.org/packages/da/ca/918d8d306bd43beacff3b835c6d96fac0ae64c0857092f068b88db531a7c/scikit_image-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b702c3bb115e1dcf4abf5297429b5c90f2189655888cbed14921f3d26f81d3a4", size = 12413484 }, + { url = "https://files.pythonhosted.org/packages/dc/cd/4da01329b5a8d47ff7ec3c99a2b02465a8017b186027590dc7425cee0b56/scikit_image-0.26.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0608aa4a9ec39e0843de10d60edb2785a30c1c47819b67866dd223ebd149acaf", size = 11769501 }, +] + +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835 }, + { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381 }, + { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632 }, + { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788 }, + { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706 }, + { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451 }, + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242 }, + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075 }, + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492 }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904 }, + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359 }, + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898 }, + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770 }, + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458 }, + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341 }, + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022 }, + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409 }, + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760 }, + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045 }, + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324 }, + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651 }, + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045 }, + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994 }, + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518 }, + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667 }, + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524 }, + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133 }, + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223 }, + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518 }, + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546 }, + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305 }, + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257 }, + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673 }, + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467 }, + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395 }, + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647 }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675 }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057 }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032 }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533 }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057 }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300 }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333 }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314 }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512 }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248 }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954 }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662 }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366 }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017 }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842 }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890 }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557 }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856 }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682 }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340 }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199 }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001 }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719 }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429 }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952 }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063 }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449 }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943 }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621 }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708 }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135 }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977 }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601 }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667 }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159 }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771 }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910 }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980 }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543 }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510 }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131 }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032 }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766 }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007 }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333 }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066 }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763 }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984 }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877 }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750 }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858 }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723 }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098 }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397 }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163 }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291 }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317 }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327 }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165 }, +] + +[[package]] +name = "scverse-misc" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "session-info2", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/05/6123a4362e2810ef216f152e249a66799f9c37975dabf89b69abb2d68c42/scverse_misc-0.0.3.tar.gz", hash = "sha256:18c46eeeac8ccef8f435e41a8ee86173b3d7ef6ea1167fde97a17553f70d3210", size = 23128 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/3e/9dad188e00d08f5381b95dd1f8f7b1dc9eb244250d93fea3e9cb7d0e61f0/scverse_misc-0.0.3-py3-none-any.whl", hash = "sha256:295702368db8f5fe946b5296135990492b3fc891a45bd6dd27798775db4c61c6", size = 8845 }, +] + +[[package]] +name = "scverse-misc" +version = "0.0.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "session-info2", marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions", marker = "python_full_version == '3.12.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/6f/991549a8fc56c0e18e53b41819be8115eb767f0354e82bcba10d0eab9a0c/scverse_misc-0.0.7.tar.gz", hash = "sha256:d01467d806b4f783b52675451eccd913945d08d73609ea66470088b5446cca6a", size = 31565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a5/a276bcdece26e2de16daa8de9e88979c40399f4ea6bdc352581c0a3f8b0f/scverse_misc-0.0.7-py3-none-any.whl", hash = "sha256:11c00be7b04fe903b6834df58159ae4910d4faae21356802a99303a18546f109", size = 13933 }, +] + +[[package]] +name = "seaborn" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554 }, +] + +[[package]] +name = "send2trash" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, +] + +[[package]] +name = "session-info2" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/17/c6a81d91781734bd10d7842c32de665f34395b4db234abdc80dac271632b/session_info2-0.4.1.tar.gz", hash = "sha256:3bb2bf7b73b2e13a1737e9aa91a6dae55e2c49e83bee973f24245f31ae264a1f", size = 25207 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/d7/6893c9c2a52e4bcbeca2a2bf2aee970a686cc7bf555f97db13b00f35250e/session_info2-0.4.1-py3-none-any.whl", hash = "sha256:423b3f6bb7023433cfc3f791a6fdbb6a2cfbe226770ae6c127c3b2c4cf5a9d56", size = 17696 }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223 }, +] + +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8d/1ff672dea9ec6a7b5d422eb6d095ed886e2e523733329f75fdcb14ee1149/shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618", size = 1820038 }, + { url = "https://files.pythonhosted.org/packages/4f/ce/28fab8c772ce5db23a0d86bf0adaee0c4c79d5ad1db766055fa3dab442e2/shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d", size = 1626039 }, + { url = "https://files.pythonhosted.org/packages/70/8b/868b7e3f4982f5006e9395c1e12343c66a8155c0374fdc07c0e6a1ab547d/shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09", size = 3001519 }, + { url = "https://files.pythonhosted.org/packages/13/02/58b0b8d9c17c93ab6340edd8b7308c0c5a5b81f94ce65705819b7416dba5/shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26", size = 3110842 }, + { url = "https://files.pythonhosted.org/packages/af/61/8e389c97994d5f331dcffb25e2fa761aeedfb52b3ad9bcdd7b8671f4810a/shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7", size = 4021316 }, + { url = "https://files.pythonhosted.org/packages/d3/d4/9b2a9fe6039f9e42ccf2cb3e84f219fd8364b0c3b8e7bbc857b5fbe9c14c/shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2", size = 4178586 }, + { url = "https://files.pythonhosted.org/packages/16/f6/9840f6963ed4decf76b08fd6d7fed14f8779fb7a62cb45c5617fa8ac6eab/shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6", size = 1543961 }, + { url = "https://files.pythonhosted.org/packages/38/1e/3f8ea46353c2a33c1669eb7327f9665103aa3a8dfe7f2e4ef714c210b2c2/shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc", size = 1722856 }, + { url = "https://files.pythonhosted.org/packages/24/c0/f3b6453cf2dfa99adc0ba6675f9aaff9e526d2224cbd7ff9c1a879238693/shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94", size = 1833550 }, + { url = "https://files.pythonhosted.org/packages/86/07/59dee0bc4b913b7ab59ab1086225baca5b8f19865e6101db9ebb7243e132/shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359", size = 1643556 }, + { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308 }, + { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844 }, + { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842 }, + { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714 }, + { url = "https://files.pythonhosted.org/packages/53/46/319c9dc788884ad0785242543cdffac0e6530e4d0deb6c4862bc4143dcf3/shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454", size = 1542745 }, + { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861 }, + { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644 }, + { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887 }, + { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931 }, + { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855 }, + { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960 }, + { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851 }, + { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890 }, + { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151 }, + { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130 }, + { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802 }, + { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460 }, + { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223 }, + { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760 }, + { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078 }, + { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178 }, + { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756 }, + { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290 }, + { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463 }, + { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145 }, + { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806 }, + { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803 }, + { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301 }, + { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247 }, + { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019 }, + { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137 }, + { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884 }, + { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320 }, + { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931 }, + { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406 }, + { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511 }, + { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607 }, + { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "slicerator" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/52/f38586b82b2935f8b59a09b0a79c545a22ed062e728c9418bafeb51f61e0/slicerator-1.1.0.tar.gz", hash = "sha256:44010a7f5cd87680c07213b5cabe81d1fb71252962943e5373ee7d14605d6046", size = 38283 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/ae/fa6cd331b364ad2bbc31652d025f5747d89cbb75576733dfdf8efe3e4d62/slicerator-1.1.0-py3-none-any.whl", hash = "sha256:167668d48c6d3a5ba0bd3d54b2688e81ee267dc20aef299e547d711e6f3c441a", size = 10274 }, +] + +[[package]] +name = "snowballstemmer" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/ee/67eef9600338e245ad7838230969a34c823ddbdbccc5e1fc43cd75b55bc9/snowballstemmer-3.1.0.tar.gz", hash = "sha256:fd9e34526b23340cd23ffea6c9f9760974ecc2c2ac9e1d81401443ccdb2a801f", size = 122523 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/83/ddbf4533c62dd32667ef1238952abef155f3d3391f5be69a352ad1638a42/snowballstemmer-3.1.0-py3-none-any.whl", hash = "sha256:17e6d1da216aa07db6dad37139ea70cf13c4b2e9a096f6e64a9648fc657d3154", size = 104550 }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, +] + +[[package]] +name = "soupsieve" +version = "2.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304 }, +] + +[[package]] +name = "spatial-image" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "xarray" }, + { name = "xarray-dataclass" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/e7/d82eec7ceb782e17d60df9b68c78b2e13e86b143c4225198b99cfed9650f/spatial_image-1.2.3.tar.gz", hash = "sha256:b0bb8ed84c9c11de00b3bbed7041375048327e0ffcb233f61aca7a24dcd75537", size = 32254 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/5a/8ef888a4f56fa2ea5c10a7d6ff02286f503a93ea298bcaa9f51a41a20df8/spatial_image-1.2.3-py3-none-any.whl", hash = "sha256:b5280386a4d540c32c14cb4fdf74bd0242c99c168a7dac36204b29f8c27ce19a", size = 8737 }, +] + +[[package]] +name = "spatialdata" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anndata" }, + { name = "annsel" }, + { name = "click" }, + { name = "dask" }, + { name = "dask-image" }, + { name = "datashader" }, + { name = "distributed" }, + { name = "fsspec", extra = ["http", "s3"] }, + { name = "geopandas" }, + { name = "multiscale-spatial-image" }, + { name = "networkx" }, + { name = "numba" }, + { name = "numpy" }, + { name = "ome-zarr" }, + { name = "pandas" }, + { name = "pooch" }, + { name = "pyarrow" }, + { name = "rich" }, + { name = "scikit-image" }, + { name = "scipy" }, + { name = "setuptools" }, + { name = "shapely" }, + { name = "spatial-image" }, + { name = "typing-extensions" }, + { name = "universal-pathlib" }, + { name = "xarray" }, + { name = "xarray-spatial", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "xarray-spatial", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/50/248fae79a62f3d9dffbe70e9b66be6e3f8aaf77caa20a8c931a79256b689/spatialdata-0.7.3.tar.gz", hash = "sha256:6054d66d00e19af96368a897faa0b369f3a25dbf9e0371f091129f8522ee6986", size = 359317 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/bd/76cd29c982ed8f06db839411f1f44c521bf36b8714074652368353856b44/spatialdata-0.7.3-py3-none-any.whl", hash = "sha256:15e0df647f149a8be70cef6b866dcf033c71890440583bca8bb1c20c37566e6c", size = 199244 }, +] + +[[package]] +name = "spatialdata-plot" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib" }, + { name = "matplotlib-scalebar" }, + { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scikit-learn" }, + { name = "spatialdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/4c/cf5b141a624d09f6dbcae67d4fac611824a8c922834a5afb5d31b236dbdc/spatialdata_plot-0.4.0.tar.gz", hash = "sha256:2f92664fdfe7b6e91d1d1563a5468a78fd980e3369e85ae7dd8bd812ab16e0ed", size = 18971339 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/ab2141c09b5eda2b13c727d8ddaff56f364f41a2127f30260199629c65dc/spatialdata_plot-0.4.0-py3-none-any.whl", hash = "sha256:e64b713e97d05caf84516007002739c38b84bbd59450424f0c2d83593179b1ed", size = 114794 }, +] + +[[package]] +name = "spatialleiden" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "igraph" }, + { name = "leidenalg" }, + { name = "numpy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/ed7b63a2914379395bf3cc575ac2f1a32849d77eb50fc8d1922ab57c17f6/spatialleiden-0.4.0.tar.gz", hash = "sha256:f3f9080a5707ba2cb76c5afc0099173692781cddcbe499a193614b1cdf3cb67d", size = 27480 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/d6/4d62161e229d799faffd38326cfa483fa7750c05568df6b6a87640758925/spatialleiden-0.4.0-py3-none-any.whl", hash = "sha256:bb1e76a681d2663c28870af04fdd3060e374b037a1b8e542090755ff1d43741e", size = 20932 }, +] + +[[package]] +name = "sphinx" +version = "9.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version < '3.12'" }, + { name = "babel", marker = "python_full_version < '3.12'" }, + { name = "colorama", marker = "python_full_version < '3.12' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.12'" }, + { name = "imagesize", marker = "python_full_version < '3.12'" }, + { name = "jinja2", marker = "python_full_version < '3.12'" }, + { name = "packaging", marker = "python_full_version < '3.12'" }, + { name = "pygments", marker = "python_full_version < '3.12'" }, + { name = "requests", marker = "python_full_version < '3.12'" }, + { name = "roman-numerals", marker = "python_full_version < '3.12'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.12'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713 }, +] + +[[package]] +name = "sphinx" +version = "9.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version >= '3.12'" }, + { name = "babel", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.12'" }, + { name = "imagesize", marker = "python_full_version >= '3.12'" }, + { name = "jinja2", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "requests", marker = "python_full_version >= '3.12'" }, + { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742 }, +] + +[[package]] +name = "sphinx-autodoc-annotation" +version = "1.0.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/2b/81f620d831cd0632ba7583723ab42e840ba766b86868307ee0877c40a2e2/sphinx-autodoc-annotation-1.0-1.tar.gz", hash = "sha256:4a3d03081efe1e5f2bc9b9d00746550f45b9f543b0c79519c523168ca7f7d89a", size = 4049 } + +[[package]] +name = "sphinx-autodoc-typehints" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/6a/c0360b115c81d449b3b73bf74b64ca773464d5c7b1b77bda87c5e874853b/sphinx_autodoc_typehints-3.6.1-py3-none-any.whl", hash = "sha256:dd818ba31d4c97f219a8c0fcacef280424f84a3589cedcb73003ad99c7da41ca", size = 20869 }, +] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "3.10.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/fd/44b8cf4465af19bf6684142f83b9b4123f6b97dc640fbdcd01f7fc01064e/sphinx_autodoc_typehints-3.10.3.tar.gz", hash = "sha256:01798d25041a16b95c5e053a45b133228e66ff37dbeef0ae8b3add4937aa41a2", size = 78698 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/30/4891d0503d4c32d857fb60ee1ea5b46f1bb13880866624f955bcb1dc9834/sphinx_autodoc_typehints-3.10.3-py3-none-any.whl", hash = "sha256:c6f9909dbdf81085f01b8a677f4da9accf97bd403d9bf820718fd5a745da355f", size = 40080 }, +] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343 }, +] + +[[package]] +name = "sphinx-design" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350 }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/68/a1bfbf38c0f7bccc9b10bbf76b94606f64acb1552ae394f0b8285bfaea25/sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c", size = 7620915 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89", size = 7655617 }, +] + +[[package]] +name = "sphinx-tabs" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "pygments" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/30/ca5b0de830f369968d8e3483dd45a8908fd10169c05cd9837f0bd075982e/sphinx_tabs-3.5.0.tar.gz", hash = "sha256:91dba1187e4c35fd37380a56ac228bbd54c6c649b2351829f3bf033718277537", size = 17006 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/45/6adc5efeb19fd5fed4027e520b5c668ce58236a2b271ade5533c4c116276/sphinx_tabs-3.5.0-py3-none-any.whl", hash = "sha256:154be49de4d5c8249ea08c5d9bf88ca8f9c31e00a178305a93cbc33e000339e5", size = 9871 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-bibtex" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "pybtex" }, + { name = "pybtex-docutils" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/6a/8e0b2c2420286389e7fed78ff361ec30e2f1d58c8560af8d64df5e7b61e0/sphinxcontrib_bibtex-2.7.0.tar.gz", hash = "sha256:fee700f7aae29bb8f654c62913f00d34ac44fc0b8ca0fa67ac922ff4453addee", size = 120669 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/c0/d28e62407f4733bbe0169287bc012f0ac3b4a2021066b285570654119c8b/sphinxcontrib_bibtex-2.7.0-py3-none-any.whl", hash = "sha256:28cf0ec7a957d1c7548d5749317ed472ce877e1b629f430f88e3789aa51f87b1", size = 40287 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "sphinxcontrib-spelling" +version = "8.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyenchant" }, + { name = "requests" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/cd/fa8039cedce6295644ff5f03367742d21e7922da426e8c666b7f5a213682/sphinxcontrib_spelling-8.0.2.tar.gz", hash = "sha256:afbc7b8e93721ab88f12bdd39d848b92017b3763b9ed6226b4b0e54b06664fea", size = 30955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c5/bcd32aa919c9e1652cca5bed6478202656a320c407793b547f8c16c179e3/sphinxcontrib_spelling-8.0.2-py3-none-any.whl", hash = "sha256:db8b3b2945683d49e87a8a5133d2b8ed4206cb593038b986ca8686a485f9980d", size = 14587 }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/5d/3172686af1770e4de2805f919a51441085f589ddadf3dd76ec582f84f497/sqlalchemy-2.0.50-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1aa6e403663a9c43c8fef7ce4bdb4cf48bcd8d352e91deda2a99f963270bd508", size = 2161366 }, + { url = "https://files.pythonhosted.org/packages/0f/90/e98dedea3c3e663a17afcd003a34ba45efdac2cea3b6f2e4585e2b1e2537/sqlalchemy-2.0.50-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51b637a84f9fa35ae1f9017e786cb142974a25305085e1b378b3647a67f65ad3", size = 3318926 }, + { url = "https://files.pythonhosted.org/packages/3b/4f/501308c2babb62c11753ecb4ee88ba9eef019419a4d6cbf7cb13e2bad353/sqlalchemy-2.0.50-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2dab927761d9108550f0cf8e66ff21af56f907a0ce0a689793db615e2b55f62c", size = 3319199 }, + { url = "https://files.pythonhosted.org/packages/ac/39/d88996c5e03ed6248c3a788d20f0b8d8b376b9f8a495e4bab9df7c72d2f8/sqlalchemy-2.0.50-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:545eae198d37bcf837a10ede3684e2af32458d6f35c597c35c2de7502dc38fc4", size = 3270301 }, + { url = "https://files.pythonhosted.org/packages/42/1b/1ae0e65161b51cc43e5ca75430ef79d80e23b5042d645586c2c342c3b92e/sqlalchemy-2.0.50-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fec460e18cdbb4c7773531122ce9a27e96c6ca17af3933941d94da475ad2c86", size = 3293465 }, + { url = "https://files.pythonhosted.org/packages/83/29/17c0003f2c0dfa6d1b97672475707e3ec5980db09defd7fa20beb6833bbd/sqlalchemy-2.0.50-cp311-cp311-win32.whl", hash = "sha256:e6e814658818fd165e749e3d8490ef16cc7f379a118c37ada8b0589ffbaaac22", size = 2120694 }, + { url = "https://files.pythonhosted.org/packages/c9/18/280d00654cc19d1fccf236fa5070f6dd04b84dde6f1b2e637bde0ff340a7/sqlalchemy-2.0.50-cp311-cp311-win_amd64.whl", hash = "sha256:1c5f858fe79c9f5d8fda065c06186356acb7f8df3cd52dbd5ee3f200e4b144f5", size = 2145315 }, + { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807 }, + { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358 }, + { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994 }, + { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399 }, + { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216 }, + { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194 }, + { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186 }, + { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697 }, + { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260 }, + { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280 }, + { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580 }, + { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375 }, + { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229 }, + { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126 }, + { url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519 }, + { url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063 }, + { url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069 }, + { url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453 }, + { url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413 }, + { url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063 }, + { url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830 }, + { url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435 }, + { url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059 }, + { url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366 }, + { url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879 }, + { url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823 }, + { url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794 }, + { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861 }, +] + +[[package]] +name = "squidpy" +source = { editable = "." } +dependencies = [ + { name = "aiohttp" }, + { name = "anndata" }, + { name = "cycler" }, + { name = "dask", extra = ["array"] }, + { name = "dask-image" }, + { name = "docrep" }, + { name = "fast-array-utils", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "fast-array-utils", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "fsspec" }, + { name = "imagecodecs" }, + { name = "matplotlib" }, + { name = "matplotlib-scalebar" }, + { name = "networkx" }, + { name = "numba" }, + { name = "numpy" }, + { name = "omnipath" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pooch" }, + { name = "pyyaml" }, + { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scikit-image" }, + { name = "scikit-learn" }, + { name = "spatialdata" }, + { name = "spatialdata-plot" }, + { name = "statsmodels" }, + { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "tqdm" }, + { name = "validators" }, + { name = "xarray" }, + { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] + +[package.optional-dependencies] +jax = [ + { name = "jax" }, +] +leiden = [ + { name = "leidenalg" }, + { name = "spatialleiden" }, +] + +[package.dev-dependencies] +dev = [ + { name = "hatch" }, + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyterlab" }, + { name = "jupytext" }, + { name = "notebook" }, + { name = "pre-commit" }, + { name = "ruff" }, +] +docs = [ + { name = "docutils" }, + { name = "ipython" }, + { name = "ipywidgets" }, + { name = "myst-nb" }, + { name = "nbsphinx" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-autodoc-annotation" }, + { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "sphinx-autodoc-typehints", version = "3.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sphinx-copybutton" }, + { name = "sphinx-design" }, + { name = "sphinx-rtd-theme" }, + { name = "sphinx-tabs" }, + { name = "sphinxcontrib-bibtex" }, + { name = "sphinxcontrib-spelling" }, +] +test = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "pytest-rerunfailures" }, + { name = "pytest-timeout" }, + { name = "pytest-xdist" }, + { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, extra = ["leiden"], marker = "python_full_version < '3.12'" }, + { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, extra = ["leiden"], marker = "python_full_version >= '3.12'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.8.1" }, + { name = "anndata", specifier = ">=0.9" }, + { name = "cycler", specifier = ">=0.11" }, + { name = "dask", extras = ["array"], specifier = ">=2021.2" }, + { name = "dask-image", specifier = ">=0.5" }, + { name = "docrep", specifier = ">=0.3.1" }, + { name = "fast-array-utils" }, + { name = "fsspec", specifier = ">=2021.11" }, + { name = "imagecodecs", specifier = ">=2025.8.2,<2026" }, + { name = "jax", marker = "extra == 'jax'" }, + { name = "leidenalg", marker = "extra == 'leiden'" }, + { name = "matplotlib", specifier = ">=3.3" }, + { name = "matplotlib-scalebar", specifier = ">=0.8" }, + { name = "networkx", specifier = ">=2.6" }, + { name = "numba", specifier = ">=0.56.4" }, + { name = "numpy", specifier = ">=1.23" }, + { name = "omnipath", specifier = ">=1.0.7" }, + { name = "pandas", specifier = ">=2.1" }, + { name = "pillow", specifier = ">=8" }, + { name = "pooch", specifier = ">=1.6" }, + { name = "pyyaml", specifier = ">=6" }, + { name = "scanpy", specifier = ">=1.9.3" }, + { name = "scikit-image", specifier = ">=0.25" }, + { name = "scikit-learn", specifier = ">=0.24" }, + { name = "spatialdata", specifier = ">=0.7.1" }, + { name = "spatialdata-plot", specifier = ">=0.3.3" }, + { name = "spatialleiden", marker = "extra == 'leiden'", specifier = ">=0.4" }, + { name = "statsmodels", specifier = ">=0.12" }, + { name = "tifffile", specifier = "!=2022.4.22" }, + { name = "tqdm", specifier = ">=4.50.2" }, + { name = "validators", specifier = ">=0.18.2" }, + { name = "xarray", specifier = ">=2024.10" }, + { name = "zarr", specifier = ">=3" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "hatch", specifier = ">=1.9" }, + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyterlab" }, + { name = "jupytext" }, + { name = "notebook" }, + { name = "pre-commit", specifier = ">=3" }, + { name = "ruff" }, +] +docs = [ + { name = "docutils", specifier = "<0.22" }, + { name = "ipython" }, + { name = "ipywidgets", specifier = ">=8" }, + { name = "myst-nb", specifier = ">=0.17.1" }, + { name = "nbsphinx", specifier = ">=0.8.1" }, + { name = "sphinx", specifier = ">=5.3" }, + { name = "sphinx-autodoc-annotation" }, + { name = "sphinx-autodoc-typehints", specifier = ">=1.10.3" }, + { name = "sphinx-copybutton", specifier = ">=0.5" }, + { name = "sphinx-design" }, + { name = "sphinx-rtd-theme" }, + { name = "sphinx-tabs" }, + { name = "sphinxcontrib-bibtex", specifier = ">=2.3" }, + { name = "sphinxcontrib-spelling", specifier = ">=7.6.2" }, +] +test = [ + { name = "coverage", extras = ["toml"], specifier = ">=7" }, + { name = "pytest", specifier = ">=7" }, + { name = "pytest-cov", specifier = ">=4" }, + { name = "pytest-mock", specifier = ">=3.5" }, + { name = "pytest-rerunfailures" }, + { name = "pytest-timeout", specifier = ">=2.1" }, + { name = "pytest-xdist", specifier = ">=3" }, + { name = "scanpy", extras = ["leiden"] }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "statsmodels" +version = "0.14.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "patsy" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/81/e8d74b34f85285f7335d30c5e3c2d7c0346997af9f3debf9a0a9a63de184/statsmodels-0.14.6.tar.gz", hash = "sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a", size = 20689085 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/4d/df4dd089b406accfc3bb5ee53ba29bb3bdf5ae61643f86f8f604baa57656/statsmodels-0.14.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ad5c2810fc6c684254a7792bf1cbaf1606cdee2a253f8bd259c43135d87cfb4", size = 10121514 }, + { url = "https://files.pythonhosted.org/packages/82/af/ec48daa7f861f993b91a0dcc791d66e1cf56510a235c5cbd2ab991a31d5c/statsmodels-0.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341fa68a7403e10a95c7b6e41134b0da3a7b835ecff1eb266294408535a06eb6", size = 10003346 }, + { url = "https://files.pythonhosted.org/packages/a9/2c/c8f7aa24cd729970728f3f98822fb45149adc216f445a9301e441f7ac760/statsmodels-0.14.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdf1dfe2a3ca56f5529118baf33a13efed2783c528f4a36409b46bbd2d9d48eb", size = 10129872 }, + { url = "https://files.pythonhosted.org/packages/40/c6/9ae8e9b0721e9b6eb5f340c3a0ce8cd7cce4f66e03dd81f80d60f111987f/statsmodels-0.14.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3764ba8195c9baf0925a96da0743ff218067a269f01d155ca3558deed2658ca", size = 10381964 }, + { url = "https://files.pythonhosted.org/packages/28/8c/cf3d30c8c2da78e2ad1f50ade8b7fabec3ff4cdfc56fbc02e097c4577f90/statsmodels-0.14.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e8d2e519852adb1b420e018f5ac6e6684b2b877478adf7fda2cfdb58f5acb5d", size = 10409611 }, + { url = "https://files.pythonhosted.org/packages/bf/cc/018f14ecb58c6cb89de9d52695740b7d1f5a982aa9ea312483ea3c3d5f77/statsmodels-0.14.6-cp311-cp311-win_amd64.whl", hash = "sha256:2738a00fca51196f5a7d44b06970ace6b8b30289839e4808d656f8a98e35faa7", size = 9580385 }, + { url = "https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5", size = 10091932 }, + { url = "https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c", size = 9997345 }, + { url = "https://files.pythonhosted.org/packages/48/f5/3a73b51e6450c31652c53a8e12e24eac64e3824be816c0c2316e7dbdcb7d/statsmodels-0.14.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c00a42863e4f4733ac9d078bbfad816249c01451740e6f5053ecc7db6d6368", size = 10058649 }, + { url = "https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d", size = 10339446 }, + { url = "https://files.pythonhosted.org/packages/56/4a/dce451c74c4050535fac1ec0c14b80706d8fc134c9da22db3c8a0ec62c33/statsmodels-0.14.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81e7dcc5e9587f2567e52deaff5220b175bf2f648951549eae5fc9383b62bc37", size = 10368705 }, + { url = "https://files.pythonhosted.org/packages/60/15/3daba2df40be8b8a9a027d7f54c8dedf24f0d81b96e54b52293f5f7e3418/statsmodels-0.14.6-cp312-cp312-win_amd64.whl", hash = "sha256:b5eb07acd115aa6208b4058211138393a7e6c2cf12b6f213ede10f658f6a714f", size = 9543991 }, + { url = "https://files.pythonhosted.org/packages/81/59/a5aad5b0cc266f5be013db8cde563ac5d2a025e7efc0c328d83b50c72992/statsmodels-0.14.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47ee7af083623d2091954fa71c7549b8443168f41b7c5dce66510274c50fd73e", size = 10072009 }, + { url = "https://files.pythonhosted.org/packages/53/dd/d8cfa7922fc6dc3c56fa6c59b348ea7de829a94cd73208c6f8202dd33f17/statsmodels-0.14.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa60d82e29fcd0a736e86feb63a11d2380322d77a9369a54be8b0965a3985f71", size = 9980018 }, + { url = "https://files.pythonhosted.org/packages/ee/77/0ec96803eba444efd75dba32f2ef88765ae3e8f567d276805391ec2c98c6/statsmodels-0.14.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89ee7d595f5939cc20bf946faedcb5137d975f03ae080f300ebb4398f16a5bd4", size = 10060269 }, + { url = "https://files.pythonhosted.org/packages/10/b9/fd41f1f6af13a1a1212a06bb377b17762feaa6d656947bf666f76300fc05/statsmodels-0.14.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:730f3297b26749b216a06e4327fe0be59b8d05f7d594fb6caff4287b69654589", size = 10324155 }, + { url = "https://files.pythonhosted.org/packages/ee/0f/a6900e220abd2c69cd0a07e3ad26c71984be6061415a60e0f17b152ecf08/statsmodels-0.14.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f1c08befa85e93acc992b72a390ddb7bd876190f1360e61d10cf43833463bc9c", size = 10349765 }, + { url = "https://files.pythonhosted.org/packages/98/08/b79f0c614f38e566eebbdcff90c0bcacf3c6ba7a5bbb12183c09c29ca400/statsmodels-0.14.6-cp313-cp313-win_amd64.whl", hash = "sha256:8021271a79f35b842c02a1794465a651a9d06ec2080f76ebc3b7adce77d08233", size = 9540043 }, + { url = "https://files.pythonhosted.org/packages/71/de/09540e870318e0c7b58316561d417be45eff731263b4234fdd2eee3511a8/statsmodels-0.14.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:00781869991f8f02ad3610da6627fd26ebe262210287beb59761982a8fa88cae", size = 10069403 }, + { url = "https://files.pythonhosted.org/packages/ab/f0/63c1bfda75dc53cee858006e1f46bd6d6f883853bea1b97949d0087766ca/statsmodels-0.14.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:73f305fbf31607b35ce919fae636ab8b80d175328ed38fdc6f354e813b86ee37", size = 9989253 }, + { url = "https://files.pythonhosted.org/packages/c1/98/b0dfb4f542b2033a3341aa5f1bdd97024230a4ad3670c5b0839d54e3dcab/statsmodels-0.14.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e443e7077a6e2d3faeea72f5a92c9f12c63722686eb80bb40a0f04e4a7e267ad", size = 10090802 }, + { url = "https://files.pythonhosted.org/packages/34/0e/2408735aca9e764643196212f9069912100151414dd617d39ffc72d77eee/statsmodels-0.14.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3414e40c073d725007a6603a18247ab7af3467e1af4a5e5a24e4c27bc26673b4", size = 10337587 }, + { url = "https://files.pythonhosted.org/packages/0f/36/4d44f7035ab3c0b2b6a4c4ebb98dedf36246ccbc1b3e2f51ebcd7ac83abb/statsmodels-0.14.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a518d3f9889ef920116f9fa56d0338069e110f823926356946dae83bc9e33e19", size = 10363350 }, + { url = "https://files.pythonhosted.org/packages/26/33/f1652d0c59fa51de18492ee2345b65372550501ad061daa38f950be390b6/statsmodels-0.14.6-cp314-cp314-win_amd64.whl", hash = "sha256:151b73e29f01fe619dbce7f66d61a356e9d1fe5e906529b78807df9189c37721", size = 9588010 }, +] + +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814 }, +] + +[[package]] +name = "tblib" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8a/14c15ae154895cc131174f858c707790d416c444fc69f93918adfd8c4c0b/tblib-3.2.2.tar.gz", hash = "sha256:e9a652692d91bf4f743d4a15bc174c0b76afc750fe8c7b6d195cc1c1d6d2ccec", size = 35046 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/be/5d2d47b1fb58943194fb59dcf222f7c4e35122ec0ffe8c36e18b5d728f0b/tblib-3.2.2-py3-none-any.whl", hash = "sha256:26bdccf339bcce6a88b2b5432c988b266ebbe63a4e593f6b578b1d2e723d2b76", size = 12893 }, +] + +[[package]] +name = "terminado" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, +] + +[[package]] +name = "texttable" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/dc/0aff23d6036a4d3bf4f1d8c8204c5c79c4437e25e0ae94ffe4bbb55ee3c2/texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638", size = 12831 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tifffile" +version = "2026.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960 }, +] + +[[package]] +name = "tifffile" +version = "2026.5.15" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/66/0aef917d525767a40edebe088f8ed6a4417e6eb489c58f6805bfa872636b/tifffile-2026.5.15.tar.gz", hash = "sha256:ee4f3e07ee0d8ff4745a8c735ac2b72caa3173c7d6059b00fdc3ff492a0b635b", size = 429998 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/6e/7d8850ff112f8f80d394ca45e89b975a3a43559d47af3137b767669b3294/tifffile-2026.5.15-py3-none-any.whl", hash = "sha256:6715515a53cabc0cefc5c9f13a0ae2c250e63e2ca784ce02d0b6c333810c2a17", size = 266665 }, +] + +[[package]] +name = "tinycss2" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704 }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454 }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561 }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824 }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227 }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859 }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204 }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084 }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285 }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924 }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018 }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948 }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341 }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159 }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290 }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141 }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847 }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088 }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866 }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887 }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704 }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628 }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180 }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674 }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976 }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755 }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265 }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726 }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859 }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713 }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084 }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973 }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223 }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973 }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082 }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490 }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263 }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736 }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717 }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461 }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855 }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144 }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683 }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196 }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393 }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583 }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675 }, +] + +[[package]] +name = "tomlkit" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/db/03eaf4331631ef6b27d6e3c9b68c54dc6f0d63d87201fed600cc409307fd/tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3", size = 161875 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328 }, +] + +[[package]] +name = "toolz" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, +] + +[[package]] +name = "tornado" +version = "6.5.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/57/6d7303a77ae439d9189108f76c0c4fd89ee5e2cc8387bffb55232565c4ed/tornado-6.5.6.tar.gz", hash = "sha256:9a365179fe8ff6b8766f602c0f67c185d778193e9bdd828b19f0b6ed7764177d", size = 518139 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/0d/b4f481e18c5a51864e6d12b9a05ecf72919696680b747c958c3fc1f4fbae/tornado-6.5.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:65fcfaafb079435c2c19dc9e07c0f1cf0fa9051759ed0a7d0a3ba7ea7f64919c", size = 447737 }, + { url = "https://files.pythonhosted.org/packages/9e/9c/5430c39fcab1144d35860f457b15e9c08b4bc7ac86764354204e983d6183/tornado-6.5.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:38bc01b4acacded2de63ae78023548e41ebe6fbed3ec05a796d7ae3ad893887e", size = 445899 }, + { url = "https://files.pythonhosted.org/packages/8b/79/fa7e14a2f939c807a8d30619b4eb604eab219601b78792516ebe22d40cf9/tornado-6.5.6-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b942e6a137fda31ff54bf8e6e2c8d1c37f1f50583f3ed53fb840b53b9601d104", size = 448964 }, + { url = "https://files.pythonhosted.org/packages/a7/71/bd67d5f5199f937dafe03a49a37989f60f600ff6fef34c79412a829d97bd/tornado-6.5.6-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8666946e70171b8c3f1fc9b7876fac492e84822c4c7f3746f4e8f8bc9ac92a79", size = 449935 }, + { url = "https://files.pythonhosted.org/packages/cc/a4/c24388c9cf5b3c3a513b56a158af9f23092c9a2810d789e294310797df21/tornado-6.5.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c34cfab7ad6d104f052f55de06d39bbafc5885cfeb4da688803308dbcfa90b7", size = 449767 }, + { url = "https://files.pythonhosted.org/packages/a5/eb/6a07ad550c3f7b37244bd0becdf293ec3d3e961783d8b720a97df50de1b2/tornado-6.5.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:385f35e4e22fb52551dfcda4cdc8c30c61c2c001aef5ddad99cdfe116952efd3", size = 449174 }, + { url = "https://files.pythonhosted.org/packages/bb/84/3469e098dccdb6763130e06aacd786bb4363fca7b590a55c101ddf34ed30/tornado-6.5.6-cp39-abi3-win32.whl", hash = "sha256:db475f1b67b2809b10bb16264829087724ca8d24fe4ed47f7b8675cae453ef86", size = 450230 }, + { url = "https://files.pythonhosted.org/packages/d2/3c/273a04e0b9dd9016f1685cca0c1c8795a71ac88a34a8c889a0b443483226/tornado-6.5.6-cp39-abi3-win_amd64.whl", hash = "sha256:6739bf1e8eb09230f1280ddbd3236f0309db70f2c551a8dbc40f62babdf82f79", size = 450667 }, + { url = "https://files.pythonhosted.org/packages/02/98/0cffe22a224f60c5fb1e3aa0b76f9da2e1ca78b0e9545e3d077c68ce60a7/tornado-6.5.6-cp39-abi3-win_arm64.whl", hash = "sha256:2543597b24a695d72338a9a77818362d72387c03ae173f1f169eadc5c91466ac", size = 449690 }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, +] + +[[package]] +name = "traitlets" +version = "5.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/22/40f55b26baeab80c2d7b3f1db0682f8954e4617fee7d90ce634022ef05c6/traitlets-5.15.0.tar.gz", hash = "sha256:4fead733f81cf1c4c938e06f8ca4633896833c9d89eff878159457f4d4392971", size = 163197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877 }, +] + +[[package]] +name = "trove-classifiers" +version = "2026.5.22.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/b6/1c41aa221b157b624ea1a72e975404ef228724d249011ee411ac211a615e/trove_classifiers-2026.5.22.10.tar.gz", hash = "sha256:5477e9974e91904fb2cfa4a7581ab6e2f30c2c38d847fd00ed866080748101d5", size = 17061 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/02/9a14d3048ffa4f45b7c60956a9b22688dd925d6de50f6baf7e55f3664942/trove_classifiers-2026.5.22.10-py3-none-any.whl", hash = "sha256:01fe864225726e03efb843827ecabfe319fc4dee8dd66d65b8996cb09be46e2c", size = 14225 }, +] + +[[package]] +name = "typeguard" +version = "4.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748 }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321 }, +] + +[[package]] +name = "umap-learn" +version = "0.5.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numba" }, + { name = "numpy" }, + { name = "pynndescent" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/ee/af4171241117f85c74b5ca6448ea1033cc28d599c13651d67289bacd4083/umap_learn-0.5.12.tar.gz", hash = "sha256:6aff02ecac5f2aad9f3c65ee518d7ae93e1a985ae38721fdcffceee4232c33c7", size = 96672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/98/f63318ccbe75c810011fe9233884c5d348d94d90005de1b79e5f93bef9c0/umap_learn-0.5.12-py3-none-any.whl", hash = "sha256:f2a85d2a2adcb52b541bed9b27a23ca169b56bb1b23283abeebfb8dfb8a42fe5", size = 91849 }, +] + +[[package]] +name = "universal-pathlib" +version = "0.3.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec" }, + { name = "pathlib-abc" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/6e/d997a70ee8f4c61f9a7e2f4f8af721cf072a3326848fc881b05187e52558/universal_pathlib-0.3.10.tar.gz", hash = "sha256:4487cbc90730a48cfb64f811d99e14b6faed6d738420cd5f93f59f48e6930bfb", size = 261110 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/1a/5d9a402b39ec892d856bbdd9db502ff73ce28cdf4aff72eb1ce1d6843506/universal_pathlib-0.3.10-py3-none-any.whl", hash = "sha256:dfaf2fb35683d2eb1287a3ed7b215e4d6016aa6eaf339c607023d22f90821c66", size = 83528 }, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087 }, +] + +[[package]] +name = "userpath" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/b7/30753098208505d7ff9be5b3a32112fb8a4cb3ddfccbbb7ba9973f2e29ff/userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815", size = 11140 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/99/3ec6335ded5b88c2f7ed25c56ffd952546f7ed007ffb1e1539dc3b57015a/userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d", size = 9065 }, +] + +[[package]] +name = "uv" +version = "0.11.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/8e/ec34c19d0f254fcbcc5c1ce8c7f06e47e0f69a7e1a0269c1d59cb0b0f279/uv-0.11.17.tar.gz", hash = "sha256:1d1be74deec997db1dda05a7e67541c904d65cbfd72e455d3c0a2a1e4bf2cddf", size = 4203607 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/2e/e6d42f9d39009eee976f1e5dfd31d3d1943e6e593ad7b191cf11e9744a36/uv-0.11.17-py3-none-linux_armv6l.whl", hash = "sha256:8426bfe315564d414cbc5ba5467595dc6348965e19acec742914f47da3ff269f", size = 23551216 }, + { url = "https://files.pythonhosted.org/packages/d0/ee/d72bcc60f3585653a4b768425854d737d98d65c1765547d25c2999547ea9/uv-0.11.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6d1a033cc68cabb4141d6c1e3b66ffc6e970b98ba42e210f33270251e0bd8697", size = 22997377 }, + { url = "https://files.pythonhosted.org/packages/58/34/1bc69798d9ae998fbc42c61b02883f2ba00d04bdd858e589604d01846287/uv-0.11.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:58c07ffc272c847d29cd98ca5082fa4304a645f87c718ec900e3cca9026bd096", size = 21630197 }, + { url = "https://files.pythonhosted.org/packages/6b/93/1be48ec6a8933d9a77d0ce5240ed63f68869f68517ccf5d62268ed03f3e8/uv-0.11.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:036d6e2940afe8b79637530b01b9241d8cfd174b07f1179a1ebbd42409c38ca3", size = 23414940 }, + { url = "https://files.pythonhosted.org/packages/00/31/b7488ff49d80090ea9d05d67a4d381a1b4479502e9853e654caa1c1c678e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:283186700c3e65a4644a73a917232da7d3e4a94d25ea0377a44f5b263fa49577", size = 23096330 }, + { url = "https://files.pythonhosted.org/packages/fe/95/42b6137c5de06278d229c7eef2f314df2a738cd799795bbb44dace21bd6e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2e44dfbfc7778d0d90edc6738f237c91e5e37e4e3cfe94c8a312cec56a41485", size = 23101906 }, + { url = "https://files.pythonhosted.org/packages/17/7c/0ca03b2d19965db6d5dfe0c8cf96a3d0b424503c8cbc3cd2ffdc5869a15d/uv-0.11.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a817eeb3026f27a53d3f4b7855a5105f6787dd192140e201eda4d2b9a11b72e", size = 24444409 }, + { url = "https://files.pythonhosted.org/packages/b5/fb/179f55a3b19d47c30ec1f41b9b964da74dfa7053ff310a70a9c4d8cb998d/uv-0.11.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf8f5ad959583dcd2c4ae445c754a97c05700246ff89259f3fd285c9c20f4c00", size = 25540153 }, + { url = "https://files.pythonhosted.org/packages/f7/29/592f42012765c43ae45c112110e214bca7b0cfc08c4c1b52e1dfa47dedd5/uv-0.11.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce16892a45134d20165c1ceababe06f3e9ce6a58902db1eff812c8c93626823f", size = 24665906 }, + { url = "https://files.pythonhosted.org/packages/0e/51/b75808766f895248553c6370968509cd4f726e6943e310a8f7a171036ad0/uv-0.11.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da839e5a491c9a701d7d327a199cafc76ac27a03ac84fd2a8d4bf32c3af2448", size = 24863325 }, + { url = "https://files.pythonhosted.org/packages/ee/6a/6f27ee69e97f480104bb8ec335f04c2a12add98edfcc4844a68e9538b6e2/uv-0.11.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ec004b3c9bf9cb7756067ad1bd0bf64eb843e6fa2edbfbb3135ee152c14cea91", size = 23521674 }, + { url = "https://files.pythonhosted.org/packages/df/11/1344aca7c710f794750f74de0e552a54ab24193ecc01fa3b3ae22ff822a1/uv-0.11.17-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:659227cac719b618cc91e02be9e274ad5bd72d74fa278123e6373537e9f28216", size = 24224725 }, + { url = "https://files.pythonhosted.org/packages/ad/44/7b11550c1453ea13b81e549c83523e6ab6ed3231d09b2fd6b9eb19acceaf/uv-0.11.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e301d844eed9401f0f0351de12c55f1306ca05372acb0f28d35717c8ba663a22", size = 24301643 }, + { url = "https://files.pythonhosted.org/packages/1a/36/8f683bc60547b8f93d0e752a8574d13fad776999cb978482b360c053ca22/uv-0.11.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f0bf483c0d9fa14283992d56061b498b9d3d4adebd285af8744dc33f64dadfba", size = 23786049 }, + { url = "https://files.pythonhosted.org/packages/10/dc/7a495db39c2970de4fa375c337dbd617b16780911f88f0511f8fe7f6747c/uv-0.11.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:2ccd5487a4a192bc832ea04c867a26883757db8fdfe88bed85d8129c82f9e505", size = 25049786 }, + { url = "https://files.pythonhosted.org/packages/37/dd/74eff72d749eaf7e19f489878e21a368a7fef58d26ea0c63ec044ecd78b1/uv-0.11.17-py3-none-win32.whl", hash = "sha256:12b701fa32c5be3691759a73956e4462f30fa7b0dfa52ec66cb305bbb6ea4129", size = 22479213 }, + { url = "https://files.pythonhosted.org/packages/79/99/8af4a92b99a8a4823297c26df727fe957267e03e1196e3caa803c3f6ccb2/uv-0.11.17-py3-none-win_amd64.whl", hash = "sha256:44ec1fe3af839f87370dcf0400c0cab917cc1ce697d563e860fc7d9ed72655e7", size = 25083161 }, + { url = "https://files.pythonhosted.org/packages/00/76/a689077832d585d29d87f9cd0d65eca1af58abd29a4eab004d0a8a858b9c/uv-0.11.17-py3-none-win_arm64.whl", hash = "sha256:37c915bfcf86f99c1c5be7c9ed21e0d80624067ba47bc8916a3cb0530bc94d27", size = 23544936 }, +] + +[[package]] +name = "validators" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, +] + +[[package]] +name = "virtualenv" +version = "21.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/f0/b47ecf438211a25a97f8f0e4b23c22bc2496ebfea18dd6ec16210f09cc36/virtualenv-21.4.1.tar.gz", hash = "sha256:2ca543c713b72840ceffd94e9bdedfbd09a661defa1f7f69e5429ad4059442e2", size = 7613344 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/dc/ac4f3a987a87e1a18556896f257c4e15c95ed157b7975347ec6b313b75ce/virtualenv-21.4.1-py3-none-any.whl", hash = "sha256:caf4ff72d1b4039057f41d8e8466e859513d67c0400d9c6b62c02c9d1ebc3e12", size = 7594078 }, +] + +[[package]] +name = "wcwidth" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825 }, +] + +[[package]] +name = "webcolors" +version = "25.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, +] + +[[package]] +name = "wrapt" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ac/4370bde262c0e633e6c4f0e56d55095710024cf9a5cecc20c59a10de483c/wrapt-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd57607acc85678925940bd5df0385ff8332083a32fa8d7a43f8767f4997263c", size = 80321 }, + { url = "https://files.pythonhosted.org/packages/eb/79/b8ff3a61e71babf58a8cf4c0d63358e8bad383e15bf7f35e62d2f6b6e4a4/wrapt-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ae574d65c9fa8e86f64f6a7c2668f9fcd507b183e0e577619f504b883cb0a6c", size = 81216 }, + { url = "https://files.pythonhosted.org/packages/6e/fd/c0cac1f77c9c4f6fe58a920ca632ce379bb8be928720e11e8d73de28a5e9/wrapt-2.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a04c28c10ba7fd12842b109d2edb0678872a2fe65277ca4ff06a0d61edee245", size = 159208 }, + { url = "https://files.pythonhosted.org/packages/d9/4f/744132a7b2fbefa6b81118ec5942eca5fc2e9a129f9055a0c5e46885a549/wrapt-2.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2f02472a1cbbf3884b365714a810b5947134a95ad6952b554cb8cce9d492b0", size = 160322 }, + { url = "https://files.pythonhosted.org/packages/d6/95/b7cd9a22a06cf93e6482904ee6afc956248983553593fd1009296d1b3b31/wrapt-2.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac2745950b2bff80219c15ebf2fa9d8427eba7e249739f97e55c9d169e47e9e1", size = 153243 }, + { url = "https://files.pythonhosted.org/packages/4c/4a/eb79423192015f46f0db2872e7e04a3dde8d359b83411e8959e7c9287eaa/wrapt-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a97e5b6c457f0cd3cfc19ebb2d84463e60c3ece754cc831e4281a3ca29bb18", size = 159231 }, + { url = "https://files.pythonhosted.org/packages/ec/dc/435015b58ce33c6fc4104158fa91ddb0e809ab03a5751fb7465d1d461456/wrapt-2.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c803a3d331796255af51ba2c79ed0ac8275865b516c09e61f248d1e7aff31ce9", size = 152351 }, + { url = "https://files.pythonhosted.org/packages/77/ac/5d203f98df8fd136b95c5227139aea02d34505e18baf812d0c005df61963/wrapt-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9b984d1eb252145d6302c1dbd5e87fc6d404d45531447c84eadec04bf1fcb027", size = 158347 }, + { url = "https://files.pythonhosted.org/packages/52/2f/a92427dbdc74e54c1674abbed27e61b2cb5e7a94441b8c1270c70671d928/wrapt-2.2.1-cp311-cp311-win32.whl", hash = "sha256:8a983a603a18c8708f024f7f6991b2e66159219abbf894634c5056243c55f3cd", size = 77562 }, + { url = "https://files.pythonhosted.org/packages/c8/56/987b9c13b3e1c1a3c6de71284076f996b79caec90e75a87c044a40c23db9/wrapt-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:9c210a6994b21aa9b29e81c8d11560e8fdab54c117e9cff37870d0a27bde1343", size = 80616 }, + { url = "https://files.pythonhosted.org/packages/7e/25/d01f560888d99d94a959c85533de349ce68d71ace3f2591d6ea8f632cfed/wrapt-2.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:401229e9d63ca09f9b8891ecf83798d26c11bbb445d11ed9f1836b6d4585b38a", size = 79025 }, + { url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021 }, + { url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692 }, + { url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364 }, + { url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079 }, + { url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205 }, + { url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922 }, + { url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388 }, + { url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682 }, + { url = "https://files.pythonhosted.org/packages/30/72/83ea3790ea352439442349388e29ff07b76e0686265f9088bbb505d1608d/wrapt-2.2.1-cp312-cp312-win32.whl", hash = "sha256:2f8c90c8afde51969487be4e1343ae049b268854877d415c2510baf833775052", size = 77857 }, + { url = "https://files.pythonhosted.org/packages/ef/cb/99450668dd3502d62a54a1c8aa56e44f34cb8c1261b381cfe2e7926c3b75/wrapt-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ce32763ac31ce94fe9aada947e479b1975012bff166da409b4b9e4e376cf7e5", size = 80825 }, + { url = "https://files.pythonhosted.org/packages/e6/3a/87512881be64e743f9ee4c66f4cbe8e884974bef2a5989af71f999653ac7/wrapt-2.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d1b4d0e0c2119587a31f5c029abd547e0c81d93b89d394566fe1588659eb579", size = 79087 }, + { url = "https://files.pythonhosted.org/packages/88/d1/a1b08f8f4fac8cbb156fa51cf64ee2c7f7f74f9875ba3cf70b3c58368694/wrapt-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d2beb1c7cab10603aecdc42f8edd6ff013f9a32e4543474e38e6b77ce9975aeb", size = 80831 }, + { url = "https://files.pythonhosted.org/packages/54/ce/57890814991446a845e09b3445ce8b694f27eb0577004f2c2a36a9772ed4/wrapt-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0cb7e4dd71f4c32e5e84843cd3c4cd65dda034314004bbe1d7f99af2426ab80", size = 81375 }, + { url = "https://files.pythonhosted.org/packages/38/65/08d7a6c76ac4493bdb668205ee9c1de1bd5daca61717c3e9aa49b4c01499/wrapt-2.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95821352042722cd9f1108874579a47989d0a7e12a37d87d2fc4af20fd99ab8a", size = 167417 }, + { url = "https://files.pythonhosted.org/packages/62/ce/f1ccbee7a1bfe5cdc6b3da6bab4b45713d628b9294da32a39f563d648140/wrapt-2.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abd621552ede77c4c69be7fac44ba911225b0c812b6ba604e5964cf98085b474", size = 166948 }, + { url = "https://files.pythonhosted.org/packages/86/2a/f85d48d1cd4869aee6704028d257d740a47c1c467b457ce396b4b5b55d07/wrapt-2.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e3677c7146ce694874941ba82b57092cc4875445aadf29d72807351023105143", size = 158148 }, + { url = "https://files.pythonhosted.org/packages/fe/5c/93939ad11d4a12358ab1aab219a2ef5efa5612e0db6b9fc65af8af1a891b/wrapt-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9a5934eaea872e17936b5f45501eba5ab0bce9a74122e172b663d7c28c459c4a", size = 165905 }, + { url = "https://files.pythonhosted.org/packages/e0/22/b8c2aa89862ff58605934d7abf4b70e6a5a1c33df96656f49035ccdf1c8a/wrapt-2.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f5b9daf6b629fce418e0cc3dd0436eac045188fa35deadb7a7f3941d5b8203f9", size = 156712 }, + { url = "https://files.pythonhosted.org/packages/5d/78/bf00a7b02239c12bb02ddcc3c0b971bfcc36e578c5a44f1ccfef5b458545/wrapt-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f53ac9f3ef573326d009ed809beff4efcac6451931c2b8132586da4b9e53ff31", size = 166560 }, + { url = "https://files.pythonhosted.org/packages/fe/93/6390ca9c5b787683cef588d04f57c8d41b9a2323b5597a65f18638c90ef2/wrapt-2.2.1-cp313-cp313-win32.whl", hash = "sha256:1ffa9cfd4bdb581539951b14ae661ff20ed0c3599b3e911a131ee0ec5ac11337", size = 77817 }, + { url = "https://files.pythonhosted.org/packages/97/73/ce10f0e71c0cfaa1a65faadb8efd4852028b3bb9ba28932b8889df769d38/wrapt-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:368eac1e20fd0bb03dd3cc42bf9887154c3861b60989389ccb5fac032617d215", size = 80736 }, + { url = "https://files.pythonhosted.org/packages/c7/4c/89f4a6818fafbbd840330e4fa3873073e1bfc166133a64cac7f8fde7a5e3/wrapt-2.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:c754dafdf5aaf0b401b644a90a30046929a0dd1a536e0ff0ec959a59155d9c7f", size = 79099 }, + { url = "https://files.pythonhosted.org/packages/bf/f2/9a8741c46f8c208ac0a45b25ba170bcb4fb72a2781d5fb97dbd7b6be73cb/wrapt-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ed928d0fda15fc0adc8d13305c8b3c0f2fba5b0669950c9e6d019d9162a3b3e8", size = 82802 }, + { url = "https://files.pythonhosted.org/packages/9c/0d/e9c855716a3705eef1416456bdf062b60620726fdc59428ff670fc3c60dc/wrapt-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fafb4e739e43544d12cb4abd1605fd4683b6ca6a9ad682b7fd8f4d21973eafa8", size = 83329 }, + { url = "https://files.pythonhosted.org/packages/3b/d6/a88f1c13112b7831adac75cea65d8310e0d696d570c8961844c90a57b865/wrapt-2.2.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:74d6a0c31472fe5d814917266b9f46495d7c61ed890af08b468acea92fb89a8d", size = 202937 }, + { url = "https://files.pythonhosted.org/packages/42/65/e29d54aef06a4d898a5b8a25589a0b3769bde454f922fad8f6f89fbfb650/wrapt-2.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab5be648d5a0b86b7438864f8df3c705a65cef35a2fd3e5561e3e203167e0f27", size = 209997 }, + { url = "https://files.pythonhosted.org/packages/2a/91/e4454263516cf0e12640912fbca9a83654e424f0a6ddb79f5cd7ce14bf33/wrapt-2.2.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d8f204c8e3a8bf9ece17e0a83d137fd807440977f8a5e762d59306795011440", size = 194856 }, + { url = "https://files.pythonhosted.org/packages/de/d0/fe0ee202286afdf4a7f77dd29f195703145764d572aec209c5086e57d924/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d047f6498c973874ba08ac3f97c69a2c4b2211c8de6f4c205f75cb1c9522596e", size = 205654 }, + { url = "https://files.pythonhosted.org/packages/23/b6/87d860dfc6460c246af70b1fd5c8b76df77571b42a493459423ded94fd7d/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:7a4fdb9326aab4a5a477a1640e5ad786a8495901009d7e7b038371edd23a9d2b", size = 192206 }, + { url = "https://files.pythonhosted.org/packages/df/46/3eea8cde077d985f239a38c0257087b8064fd9ee9b1a99e282d2c86da4ef/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8cc5094b08abeae52da9c73c8a32003623be691a5193df2f4e3eac3d557c394", size = 198428 }, + { url = "https://files.pythonhosted.org/packages/18/dc/b927ee9c7fc67adc3a5658f246a0d275425eb840ba36e7b702e70f18bde8/wrapt-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:9907a4402ab6db12b7077a0ea5d7a4d028ecb22c8eee2b53527080d347cd1562", size = 79448 }, + { url = "https://files.pythonhosted.org/packages/ec/b3/fd30b473fe498c70e6b9a5f328b8d3fbaf1b8c3c481465f59724bba8eb70/wrapt-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5590d63f5243251641cf543009b4c9314a79d0598fdb8a8e4cfc918494536c53", size = 83021 }, + { url = "https://files.pythonhosted.org/packages/ee/f3/96c39153a8737a6e9aa85adef254ac4195bea3f2d24efc60472ccc3c9e2e/wrapt-2.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c318a64b53d97b841d7b5e637517e50a27be64bc695128422953d4b21710954e", size = 80295 }, + { url = "https://files.pythonhosted.org/packages/0a/a3/11d7f34ebbf3231bc907a3e6d5ee051b14d034c1bc7b65a97d5cc00516df/wrapt-2.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f56a647e4eaf5f0ca40330fb070f566bdf9f7b0db89a1af20d71c28dcd7a0ab", size = 80879 }, + { url = "https://files.pythonhosted.org/packages/13/3c/b74cfd984cef560b900fb1a727af20352d89e1f06bf2e1114dd3f00f5f5a/wrapt-2.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:64b7deeda4b70408e382328d8bbe52a256fe9bc63ae3db86d804608367e5422c", size = 81462 }, + { url = "https://files.pythonhosted.org/packages/15/a3/7c8f704b8dc07dfe0a5d01c2edbfd88317aa8e5e3fa7c743eb7a085ae767/wrapt-2.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9cf53ba90717db2e292401de290776c498d4bbfb0d4a559ca2895db8b9dcb5c", size = 167251 }, + { url = "https://files.pythonhosted.org/packages/80/85/a34d1888d97247da6c2ff6118c3a721c73ed8cc4dd198c00208bb73b6f80/wrapt-2.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf3638274ab9d9b724c9baa0b4c04e132cd6faefb78b4dd3dd1a02a4bdaad41e", size = 166316 }, + { url = "https://files.pythonhosted.org/packages/e9/d7/72ffaeb01eebc704afe3fb99e840480f4bda45f0fa66e3381b6a39251c8f/wrapt-2.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aed9658797d0b45d6c49adcfc6b41f66e6f2d0c6de3ec79e16cf4b1855df240f", size = 157952 }, + { url = "https://files.pythonhosted.org/packages/24/5b/36f5d6b024e4edfdd90b140742d11ebcf7836daf5c9daf326c55c24db412/wrapt-2.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d676ee388bc42a04d56dd7deb5605244dac2e35cc2fadbb43c9fa25bbd93508", size = 166130 }, + { url = "https://files.pythonhosted.org/packages/81/06/9296d9e97bfdef5483dfcc859d57b095b257144b2bc5300ab521e06f4bc7/wrapt-2.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e395f7bc31851ef9b612050368cb446e9bc14cd7454b025018980349caf25ae5", size = 156604 }, + { url = "https://files.pythonhosted.org/packages/53/37/16953929ed6776175720e58fc966e779926d8d71e2c7b2273230590ca71f/wrapt-2.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f1845c2a8cc1180ccccfa45785dd06f562730d19ef75be180334254012b6283", size = 166007 }, + { url = "https://files.pythonhosted.org/packages/b9/73/20ee58c0612dae7c31131a7095345812ed2c7b389019e175f68cde34e5b4/wrapt-2.2.1-cp314-cp314-win32.whl", hash = "sha256:436addbc4bb4fc0a88c702577f51195d7d73683a7f3e0e5b253d8404d7847243", size = 78327 }, + { url = "https://files.pythonhosted.org/packages/22/b3/ef7c3295d02e0448a71c639a36a057f46d524d057c9486291a7a3039e65c/wrapt-2.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:50972a1d974ea07725a7f6b1cec5f8759008afd030a0024843ebe7d52de47f2b", size = 81144 }, + { url = "https://files.pythonhosted.org/packages/ac/dc/7bdf336953f99f4ceb0a584bb8870e42c8f26f93ea10c87834dad62f1668/wrapt-2.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1c9934ea5d92957e3cd0adbc0845539dccfd62710ebe16195a8c66c53954db36", size = 79569 }, + { url = "https://files.pythonhosted.org/packages/6a/6d/6dfae80150ff1919c356d1dd528f049bcdfaae29b4d284bc957e022caef4/wrapt-2.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17de18fc12cea55b8a9587314cb830573e37fb33b247a7515696350863714188", size = 82892 }, + { url = "https://files.pythonhosted.org/packages/82/7b/4e34766a7d7804ffce9e71befe47e9b3225dc350c49c94493c4ab39fd3a5/wrapt-2.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9dec1aca52dddde7df94818310fa2fe79739c8f385b2014c4cb1035f5508199", size = 83333 }, + { url = "https://files.pythonhosted.org/packages/9d/57/0b34db3e8de44ccfece62d7b337abd1631dd810f5adc5f3db571727836b5/wrapt-2.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69f2e9244542cb34dd59c7f073445b9e54ad9f3fce8d93606c368a1b499fc413", size = 202899 }, + { url = "https://files.pythonhosted.org/packages/e5/45/ac0c459f154b99d92789a6cba7ca727185b83513b986f8ec7fe2aacddcbf/wrapt-2.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d83966dc7f4f45e8b97b5933685ac2e6e67fc0e19246ea314bceb9a8970c956", size = 209986 }, + { url = "https://files.pythonhosted.org/packages/b7/e4/77e37ff33ad018fa81ade52c25fa327b80b56f81d734279a63614fcb4cbc/wrapt-2.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78b0aa6bfb7be8deed0ab23e7aa028cc5210c29bc2d32a04d52b50e517a7307e", size = 194893 }, + { url = "https://files.pythonhosted.org/packages/dd/9d/7ea651d1ab032fc5fa222fbec91d0f8a1397f6ae04ebb93fa7219aa921d7/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:05d5cb74d1b232ec8cfa130a8f900708699ff2491d97b8f85a4cdc5996294b85", size = 205636 }, + { url = "https://files.pythonhosted.org/packages/09/af/8e88031a701275b9085c54e64bc88c0b1cd55c77eadd400691c371cd76c4/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f6518b94edb9150452e9aba08027d4cc293433753ec1fbefb4629a21cbc74181", size = 192267 }, + { url = "https://files.pythonhosted.org/packages/bf/a8/e657ca876b06710194f243d81c4b0896ade646e244bdbec2d87c8c56a8bd/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed55af48b3eb28f43228ca2306788892bcb629eb2b5c4876e2a3659872c2f17a", size = 198378 }, + { url = "https://files.pythonhosted.org/packages/c8/59/822efe4ea722a3961331bfa35b7d90937790d2c20f0616de1997ccc3aebd/wrapt-2.2.1-cp314-cp314t-win32.whl", hash = "sha256:2e08688ab16525897da6589d56d0aebaf417bbe91c2d8e3b96203b1efa596e85", size = 80226 }, + { url = "https://files.pythonhosted.org/packages/ab/31/2a7dc5f6abb2fca0b6e1610e120419f603650aceb4f1d3ac4cae0354e162/wrapt-2.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fd0135d34387f5fd087d9be368ea77ea89cf2451dc1cd1c622d35021bcb3ab50", size = 83835 }, + { url = "https://files.pythonhosted.org/packages/9f/c0/782b86e28d1ceebeb74cccea12d2cd3d2ba0bd68e3dec20b1bc5873f6127/wrapt-2.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:f70db64e8266d7c45d3b735f2e08eeb434b5e03da9a479ae42b2e2e486a21a00", size = 80722 }, + { url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000 }, +] + +[[package]] +name = "xarray" +version = "2026.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/83/6d810a8a9ebc9c307989b418840c20e46907c74d707beb67ab566773e6fc/xarray-2026.4.0-py3-none-any.whl", hash = "sha256:d43751d9fb4a90f9249c30431684f00c41bc874f1edccd862631a40cbc0edf08", size = 1414326 }, +] + +[[package]] +name = "xarray-dataclass" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "typing-extensions" }, + { name = "xarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/99/04705ee6ff4d6d4db986058ceb19f2561014dde41e2f2ab73cc5ed35c958/xarray_dataclass-3.0.0.tar.gz", hash = "sha256:0fa438efda992b859dee07dc2f99c2660886402c366706c074c4c92795b354f1", size = 8376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/ea/bc1de04d06b7c59fc3ff647a11fa248bf80af5a6227647a31c6250c32ce6/xarray_dataclass-3.0.0-py3-none-any.whl", hash = "sha256:7956f29005671b2acaa0da700d8f359105dce84ef84f76ce2dd05b76dba3e826", size = 16862 }, +] + +[[package]] +name = "xarray-spatial" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "datashader", marker = "python_full_version < '3.12'" }, + { name = "numba", marker = "python_full_version < '3.12'" }, + { name = "numpy", marker = "python_full_version < '3.12'" }, + { name = "xarray", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/ae/8501c1a111423be48cf67406e6bf4ae0258109f3c03e27e10d6768a36185/xarray_spatial-0.5.3.tar.gz", hash = "sha256:2563f5fb00acb61065cf534af572290b4f91f134c0dcaaf84c93a4a37113ebed", size = 67748871 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ee/9d0bb9f1bbaf347efe2ca6ad7bae259d6ed9e7a2b4441bd16b88723145a1/xarray_spatial-0.5.3-py3-none-any.whl", hash = "sha256:84f5a3e07a1ac3212bcfff3c4677daa3c529a533f1493eb555f8e498b7d9746e", size = 2068246 }, +] + +[[package]] +name = "xarray-spatial" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "numba", marker = "python_full_version >= '3.12'" }, + { name = "numpy", marker = "python_full_version >= '3.12'" }, + { name = "scipy", marker = "python_full_version >= '3.12'" }, + { name = "urllib3", marker = "python_full_version >= '3.12'" }, + { name = "xarray", marker = "python_full_version >= '3.12'" }, + { name = "zstandard", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/5b/5c69a4b56cabd93e65cbbf2a64b0609b754ea3aaaa69edefe3306cb8af06/xarray_spatial-0.10.0.tar.gz", hash = "sha256:6837e3b14f3f3d7aa641d120fbc43562a2456c6f03de911bbbcdb7d89b2963e6", size = 14375557 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/be/d22d593b7ccbec145d105ea311387819de0f38c3f0fde798a2cd7a2bbb68/xarray_spatial-0.10.0-py3-none-any.whl", hash = "sha256:0cb2ba7a3ddf7407b24258f662665cab82a362060b719c30b298a7df925182e0", size = 14533173 }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971 }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507 }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343 }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704 }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281 }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020 }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450 }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384 }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153 }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322 }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057 }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502 }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253 }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345 }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558 }, + { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808 }, + { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610 }, + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957 }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164 }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688 }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902 }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931 }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030 }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392 }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612 }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487 }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333 }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025 }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507 }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719 }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438 }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719 }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901 }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229 }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978 }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733 }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113 }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899 }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862 }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060 }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613 }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012 }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887 }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620 }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599 }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604 }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161 }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619 }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362 }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667 }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069 }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670 }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916 }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625 }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574 }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534 }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481 }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529 }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338 }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147 }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272 }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962 }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063 }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438 }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458 }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589 }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424 }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248 }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084 }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272 }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497 }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002 }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524 }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165 }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010 }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128 }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382 }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964 }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204 }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510 }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584 }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410 }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980 }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219 }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576 }, +] + +[[package]] +name = "zarr" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12'", +] +dependencies = [ + { name = "donfig", marker = "python_full_version < '3.12'" }, + { name = "google-crc32c", marker = "python_full_version < '3.12'" }, + { name = "numcodecs", marker = "python_full_version < '3.12'" }, + { name = "numpy", marker = "python_full_version < '3.12'" }, + { name = "packaging", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/5a/b8a0cf39a14c770c30bd1f2d120c54000c8cd9e84e8e79f38d9a7ce58071/zarr-3.1.6.tar.gz", hash = "sha256:d95e72cbea4b90e9a70679468b8266400331756232576ae2b43400ac5108d0eb", size = 386531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/7c/ba8ca8cbe9dbef8e83a95fc208fed8e6686c98b4719aaa0aa7f3d31fe390/zarr-3.1.6-py3-none-any.whl", hash = "sha256:b5a82c5079d1c3d4ee8f06746fa3b9a98a7d804300fa3f4be154362a33e1207e", size = 295655 }, +] + +[[package]] +name = "zarr" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] +dependencies = [ + { name = "donfig", marker = "python_full_version >= '3.12'" }, + { name = "google-crc32c", marker = "python_full_version >= '3.12'" }, + { name = "numcodecs", marker = "python_full_version >= '3.12'" }, + { name = "numpy", marker = "python_full_version >= '3.12'" }, + { name = "packaging", marker = "python_full_version >= '3.12'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/8d/aeb164004f87543b06ef54f885d02c342c31ceb274e2bbec470a98927621/zarr-3.2.1.tar.gz", hash = "sha256:71565b738a0e7e8ed226f0516eba8c6bb53440ad7669a8c48ebb3534a161d035", size = 675161 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl", hash = "sha256:f78cdd3d9687ad0e9f9cba2c5683b64f0c52589c19f685eeabe872e93cc0d2c7", size = 319617 }, +] + +[[package]] +name = "zict" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ac/3c494dd7ec5122cff8252c1a209b282c0867af029f805ae9befd73ae37eb/zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5", size = 33238 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/ab/11a76c1e2126084fde2639514f24e6111b789b0bfa4fc6264a8975c7e1f1/zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", size = 43332 }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238 }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254 }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559 }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020 }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126 }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390 }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914 }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635 }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277 }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377 }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493 }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018 }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672 }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753 }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047 }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484 }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183 }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533 }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738 }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436 }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019 }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012 }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148 }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652 }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993 }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806 }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659 }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933 }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008 }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517 }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292 }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237 }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922 }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276 }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679 }, + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735 }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440 }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070 }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001 }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120 }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230 }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173 }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736 }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368 }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022 }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889 }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952 }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054 }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113 }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936 }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232 }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671 }, + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887 }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658 }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849 }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095 }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751 }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818 }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402 }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108 }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248 }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123 }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591 }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513 }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118 }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940 }, +] From b2d25cbb236c7d97a5d53ed4995186deb3e3d76e Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 10:04:54 +0200 Subject: [PATCH 071/131] refactor --- src/squidpy/experimental/_fit/__init__.py | 5 +- src/squidpy/experimental/_fit/_families.py | 20 +++ .../experimental/_fit/_methods/__init__.py | 27 --- .../experimental/_fit/_methods/_families.py | 16 -- .../_fit/_methods/_stalign/__init__.py | 11 -- .../_fit/align_landmarks/__init__.py | 22 +++ .../_landmark.py | 39 +++-- .../_fit/align_samples/__init__.py | 13 ++ .../_stalign.py} | 17 +- .../align_samples/_stalign_impl/__init__.py | 10 ++ .../_stalign_impl}/_core.py | 0 .../_stalign_impl}/_helpers.py | 0 .../_stalign_impl}/_tools.py | 0 src/squidpy/experimental/tl/__init__.py | 3 +- .../experimental/tl/_align/__init__.py | 7 + src/squidpy/experimental/tl/_align/_api.py | 161 ++++++++++++++++++ src/squidpy/experimental/tl/_align/_io.py | 158 +++++++++++++++++ .../experimental/tl/_align/_validation.py | 16 ++ tests/experimental/fit/test_stalign.py | 8 +- tests/experimental/tl/__init__.py | 0 tests/experimental/tl/test_align.py | 83 +++++++++ .../tl/test_align_by_landmarks.py | 108 ++++++++++++ 22 files changed, 641 insertions(+), 83 deletions(-) create mode 100644 src/squidpy/experimental/_fit/_families.py delete mode 100644 src/squidpy/experimental/_fit/_methods/__init__.py delete mode 100644 src/squidpy/experimental/_fit/_methods/_families.py delete mode 100644 src/squidpy/experimental/_fit/_methods/_stalign/__init__.py create mode 100644 src/squidpy/experimental/_fit/align_landmarks/__init__.py rename src/squidpy/experimental/_fit/{_methods => align_landmarks}/_landmark.py (80%) create mode 100644 src/squidpy/experimental/_fit/align_samples/__init__.py rename src/squidpy/experimental/_fit/{_methods/_stalign/_estimator.py => align_samples/_stalign.py} (90%) create mode 100644 src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py rename src/squidpy/experimental/_fit/{_methods/_stalign => align_samples/_stalign_impl}/_core.py (100%) rename src/squidpy/experimental/_fit/{_methods/_stalign => align_samples/_stalign_impl}/_helpers.py (100%) rename src/squidpy/experimental/_fit/{_methods/_stalign => align_samples/_stalign_impl}/_tools.py (100%) create mode 100644 src/squidpy/experimental/tl/_align/__init__.py create mode 100644 src/squidpy/experimental/tl/_align/_api.py create mode 100644 src/squidpy/experimental/tl/_align/_io.py create mode 100644 src/squidpy/experimental/tl/_align/_validation.py create mode 100644 tests/experimental/tl/__init__.py create mode 100644 tests/experimental/tl/test_align.py create mode 100644 tests/experimental/tl/test_align_by_landmarks.py diff --git a/src/squidpy/experimental/_fit/__init__.py b/src/squidpy/experimental/_fit/__init__.py index 66f88a475..68440899d 100644 --- a/src/squidpy/experimental/_fit/__init__.py +++ b/src/squidpy/experimental/_fit/__init__.py @@ -11,8 +11,9 @@ function, not here. - :class:`Registry` -- a flat ``name -> Estimator`` map, one per family. -Concrete estimators live in :mod:`._methods` and pull their optional -dependencies (e.g. JAX) in lazily, so importing this package stays cheap. +Concrete estimators live in per-family subpackages (``align_samples``, +``align_landmarks``, ...) and pull their optional dependencies (e.g. JAX) in +lazily, so importing this package stays cheap. Example ------- diff --git a/src/squidpy/experimental/_fit/_families.py b/src/squidpy/experimental/_fit/_families.py new file mode 100644 index 000000000..1399d9d2c --- /dev/null +++ b/src/squidpy/experimental/_fit/_families.py @@ -0,0 +1,20 @@ +"""Estimator family registries. + +A *family* is the set of interchangeable algorithms behind one public function, +selected by ``method=`` and sharing one ``fit`` contract. One :class:`Registry` +per family. Registries live in this small module so the estimator modules can +register into them without importing their package ``__init__`` (which imports +the estimator modules in turn -- that would be circular). +""" + +from __future__ import annotations + +from squidpy.experimental._fit._registry import Registry + +#: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. +#: Consumed by ``squidpy.experimental.tl.align``. +ALIGN_SAMPLES = Registry("align_samples") + +#: Closed-form landmark alignment estimators -- paired landmarks in, affine out. +#: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. +ALIGN_LANDMARKS = Registry("align_landmarks") diff --git a/src/squidpy/experimental/_fit/_methods/__init__.py b/src/squidpy/experimental/_fit/_methods/__init__.py deleted file mode 100644 index f3b6864ca..000000000 --- a/src/squidpy/experimental/_fit/_methods/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Concrete estimators for :mod:`squidpy.experimental._fit`. - -Importing this package registers the bundled estimators into their family -registries (``ALIGN``: STalign; ``LANDMARK``: similarity / affine) but stays -cheap: optional dependencies such as JAX are pulled in lazily, only when an -estimator's ``fit`` is actually called. -""" - -from __future__ import annotations - -from squidpy.experimental._fit._methods._families import ALIGN, LANDMARK -from squidpy.experimental._fit._methods._landmark import ( - AffineFitResult, - LandmarkAffineEstimator, - LandmarkSimilarityEstimator, -) -from squidpy.experimental._fit._methods._stalign import StalignEstimator, StalignFitResult - -__all__ = [ - "ALIGN", - "LANDMARK", - "AffineFitResult", - "LandmarkAffineEstimator", - "LandmarkSimilarityEstimator", - "StalignEstimator", - "StalignFitResult", -] diff --git a/src/squidpy/experimental/_fit/_methods/_families.py b/src/squidpy/experimental/_fit/_methods/_families.py deleted file mode 100644 index 867f6fa02..000000000 --- a/src/squidpy/experimental/_fit/_methods/_families.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Estimator family registries. - -Defined in their own module so individual estimator modules can register into -them without importing :mod:`squidpy.experimental._fit._methods` (which imports -the estimator modules in turn -- that would be circular). -""" - -from __future__ import annotations - -from squidpy.experimental._fit._registry import Registry - -#: Data-driven alignment estimators (consumed by ``experimental.tl.align``). -ALIGN = Registry("align") - -#: Closed-form landmark estimators (consumed by ``experimental.tl.align_by_landmarks``). -LANDMARK = Registry("landmark") diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py b/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py deleted file mode 100644 index 78000bd3e..000000000 --- a/src/squidpy/experimental/_fit/_methods/_stalign/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Private grouping of the ported STalign solver and its estimator adapter. - -Importing this package registers :class:`StalignEstimator` in the ``align`` -family and stays cheap -- JAX is pulled in lazily, only when ``fit`` runs. -""" - -from __future__ import annotations - -from squidpy.experimental._fit._methods._stalign._estimator import StalignEstimator, StalignFitResult - -__all__ = ["StalignEstimator", "StalignFitResult"] diff --git a/src/squidpy/experimental/_fit/align_landmarks/__init__.py b/src/squidpy/experimental/_fit/align_landmarks/__init__.py new file mode 100644 index 000000000..e4f528e93 --- /dev/null +++ b/src/squidpy/experimental/_fit/align_landmarks/__init__.py @@ -0,0 +1,22 @@ +"""``align_landmarks`` family: closed-form alignment from paired landmarks. + +Importing this package registers the family's estimators into +:data:`~squidpy.experimental._fit._families.ALIGN_LANDMARKS`. Pure NumPy / +spatialdata / skimage -- no JAX. +""" + +from __future__ import annotations + +from squidpy.experimental._fit._families import ALIGN_LANDMARKS +from squidpy.experimental._fit.align_landmarks._landmark import ( + AffineFitResult, + LandmarkAffineEstimator, + LandmarkSimilarityEstimator, +) + +__all__ = [ + "ALIGN_LANDMARKS", + "AffineFitResult", + "LandmarkAffineEstimator", + "LandmarkSimilarityEstimator", +] diff --git a/src/squidpy/experimental/_fit/_methods/_landmark.py b/src/squidpy/experimental/_fit/align_landmarks/_landmark.py similarity index 80% rename from src/squidpy/experimental/_fit/_methods/_landmark.py rename to src/squidpy/experimental/_fit/align_landmarks/_landmark.py index 3dc475235..2fffb3f5e 100644 --- a/src/squidpy/experimental/_fit/_methods/_landmark.py +++ b/src/squidpy/experimental/_fit/align_landmarks/_landmark.py @@ -14,13 +14,14 @@ from __future__ import annotations +from abc import abstractmethod from dataclasses import dataclass, field -from typing import Any, ClassVar, Literal +from typing import Any import numpy as np from squidpy.experimental._fit._estimator import Estimator -from squidpy.experimental._fit._methods._families import LANDMARK +from squidpy.experimental._fit._families import ALIGN_LANDMARKS from squidpy.experimental._fit._result import FitResult @@ -46,9 +47,13 @@ def transform(self, x: np.ndarray) -> np.ndarray: class _LandmarkEstimator(Estimator): - """Base for closed-form landmark fits; subclasses pin :attr:`model`.""" + """Shared logic for closed-form landmark fits. - model: ClassVar[Literal["similarity", "affine"]] + The base :meth:`fit` does all the work that is common across models -- + validate inputs, enforce ``N >= 3``, build the :class:`AffineFitResult`. + Subclasses only provide the matrix solver via :meth:`_solve`, so there is + no per-model branching here. + """ def fit( self, @@ -75,31 +80,39 @@ def fit( f"`landmarks_ref` and `landmarks_query` must have the same shape; got {ref.shape} and {query.shape}." ) if ref.shape[0] < 3: - raise ValueError(f"`model={self.model!r}` needs at least 3 landmark pairs, got {ref.shape[0]}.") + raise ValueError(f"`{self.name}` needs at least 3 landmark pairs, got {ref.shape[0]}.") - matrix = _fit_similarity(ref, query) if self.model == "similarity" else _fit_affine(ref, query) + matrix = self._solve(ref, query) return AffineFitResult( matrix=matrix, source_cs=source_cs, target_cs=target_cs, - metadata={"method": self.model}, + metadata={"method": self.name}, ) + @abstractmethod + def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: + """Return the ``(3, 3)`` matrix mapping ``query_xy`` onto ``ref_xy``.""" -@LANDMARK.register("similarity") + +@ALIGN_LANDMARKS.register("similarity") class LandmarkSimilarityEstimator(_LandmarkEstimator): - """4-DOF similarity fit (rotation + uniform scale + translation).""" + """4-DOF similarity fit (rotation + uniform scale + translation), via spatialdata.""" name = "similarity" - model = "similarity" + + def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: + return _fit_similarity(ref_xy, query_xy) -@LANDMARK.register("affine") +@ALIGN_LANDMARKS.register("affine") class LandmarkAffineEstimator(_LandmarkEstimator): - """6-DOF affine fit (rotation + non-uniform scale + shear + translation).""" + """6-DOF affine fit (rotation + non-uniform scale + shear + translation), via skimage.""" name = "affine" - model = "affine" + + def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: + return _fit_affine(ref_xy, query_xy) def _validate_landmarks(points: np.ndarray, *, name: str) -> np.ndarray: diff --git a/src/squidpy/experimental/_fit/align_samples/__init__.py b/src/squidpy/experimental/_fit/align_samples/__init__.py new file mode 100644 index 000000000..f3abcc568 --- /dev/null +++ b/src/squidpy/experimental/_fit/align_samples/__init__.py @@ -0,0 +1,13 @@ +"""``align_samples`` family: align two samples' point clouds (STalign). + +Importing this package registers the family's estimators into +:data:`~squidpy.experimental._fit._families.ALIGN_SAMPLES`. It stays cheap -- +JAX is pulled in lazily, only when an estimator's ``fit`` runs. +""" + +from __future__ import annotations + +from squidpy.experimental._fit._families import ALIGN_SAMPLES +from squidpy.experimental._fit.align_samples._stalign import StalignEstimator, StalignFitResult + +__all__ = ["ALIGN_SAMPLES", "StalignEstimator", "StalignFitResult"] diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py b/src/squidpy/experimental/_fit/align_samples/_stalign.py similarity index 90% rename from src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py rename to src/squidpy/experimental/_fit/align_samples/_stalign.py index 1d11f29c2..749370a06 100644 --- a/src/squidpy/experimental/_fit/_methods/_stalign/_estimator.py +++ b/src/squidpy/experimental/_fit/align_samples/_stalign.py @@ -1,11 +1,10 @@ """STalign estimator: JAX LDDMM point-cloud registration. The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see -:mod:`._core`, :mod:`._helpers` and :mod:`._tools`. This module only adds the -thin :class:`Estimator` / :class:`FitResult` adapter onto the -:mod:`squidpy.experimental._fit` core. JAX is imported lazily inside -:meth:`StalignEstimator.fit`, so importing this module is cheap and does not -require JAX. +:mod:`._stalign_impl`. This module only adds the thin :class:`Estimator` / +:class:`FitResult` adapter onto the :mod:`squidpy.experimental._fit` core. JAX +is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module +is cheap and does not require JAX. """ from __future__ import annotations @@ -16,11 +15,11 @@ import numpy as np from squidpy.experimental._fit._estimator import Estimator -from squidpy.experimental._fit._methods._families import ALIGN +from squidpy.experimental._fit._families import ALIGN_SAMPLES from squidpy.experimental._fit._result import FitResult if TYPE_CHECKING: - from ._tools import STalignConfig + from ._stalign_impl._tools import STalignConfig @dataclass @@ -55,7 +54,7 @@ def transform(self, x: np.ndarray) -> np.ndarray: return coords + self.deltas -@ALIGN.register("stalign") +@ALIGN_SAMPLES.register("stalign") class StalignEstimator(Estimator): """Align a query point cloud onto a reference with a JAX LDDMM solver.""" @@ -96,7 +95,7 @@ def fit( # Import the JAX-backed solver only after requirements pass, so callers # without JAX get the clean ImportError from check_requirements rather # than a confusing failure from a module-level `import jax`. - from ._tools import stalign_points + from ._stalign_impl._tools import stalign_points ref_xy = np.asarray(ref, dtype=float) query_xy = np.asarray(query, dtype=float) diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py new file mode 100644 index 000000000..958810ce7 --- /dev/null +++ b/src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py @@ -0,0 +1,10 @@ +"""Ported STalign JAX LDDMM solver (provenance: scverse/squidpy#1150). + +Pure numerics only -- no :class:`~squidpy.experimental._fit.Estimator`. The +estimator adapter lives one level up in +:mod:`squidpy.experimental._fit.align_samples._stalign`. JAX is imported at +module load here, so this subpackage must only be imported lazily (the adapter +does so inside ``fit``, after checking requirements). +""" + +from __future__ import annotations diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_core.py b/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_core.py similarity index 100% rename from src/squidpy/experimental/_fit/_methods/_stalign/_core.py rename to src/squidpy/experimental/_fit/align_samples/_stalign_impl/_core.py diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py b/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_helpers.py similarity index 100% rename from src/squidpy/experimental/_fit/_methods/_stalign/_helpers.py rename to src/squidpy/experimental/_fit/align_samples/_stalign_impl/_helpers.py diff --git a/src/squidpy/experimental/_fit/_methods/_stalign/_tools.py b/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_tools.py similarity index 100% rename from src/squidpy/experimental/_fit/_methods/_stalign/_tools.py rename to src/squidpy/experimental/_fit/align_samples/_stalign_impl/_tools.py diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index e4e529771..9dc9150f4 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +from ._align import align, align_by_landmarks from ._tiling_qc import calculate_tiling_qc -__all__ = ["calculate_tiling_qc"] +__all__ = ["align", "align_by_landmarks", "calculate_tiling_qc"] diff --git a/src/squidpy/experimental/tl/_align/__init__.py b/src/squidpy/experimental/tl/_align/__init__.py new file mode 100644 index 000000000..d386fa112 --- /dev/null +++ b/src/squidpy/experimental/tl/_align/__init__.py @@ -0,0 +1,7 @@ +"""Public alignment API for :mod:`squidpy.experimental.tl`.""" + +from __future__ import annotations + +from squidpy.experimental.tl._align._api import align, align_by_landmarks + +__all__ = ["align", "align_by_landmarks"] diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py new file mode 100644 index 000000000..7d31a3ad5 --- /dev/null +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -0,0 +1,161 @@ +"""Public alignment functions built on the :mod:`squidpy.experimental._fit` core. + +These are thin orchestrators: resolve inputs to in-memory arrays, dispatch to a +fit-core estimator, write the result back. All container I/O and write-back live +in :mod:`._io`; the estimators themselves never see a container. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, Literal + +import numpy as np +from anndata import AnnData +from spatialdata import SpatialData + +from squidpy.experimental._fit.align_landmarks import ALIGN_LANDMARKS +from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES +from squidpy.experimental.tl._align._io import ( + get_coords, + resolve_obs_pair, + writeback_affine_sdata, + writeback_obs, +) +from squidpy.experimental.tl._align._validation import validate_on, validate_output_mode + +if TYPE_CHECKING: + from squidpy.experimental._fit import FitResult + +__all__ = ["align", "align_by_landmarks"] + + +def align( + data_ref: AnnData | SpatialData, + data_query: AnnData | SpatialData | None = None, + *, + method: str = "stalign", + on: Literal["obs", "image"] = "obs", + ref_key: str | None = None, + query_key: str | None = None, + spatial_key: str = "spatial", + output_mode: Literal["object", "copy", "inplace"] = "object", + key_added: str | None = None, + **method_kwargs: Any, +) -> FitResult | AnnData | SpatialData | None: + """Align a query sample onto a reference sample. + + Parameters + ---------- + data_ref, data_query + Both :class:`~anndata.AnnData`, or both :class:`~spatialdata.SpatialData`, + or ``data_ref`` a SpatialData with ``data_query=None`` to align two of its + own tables (selected by ``ref_key`` / ``query_key``). + method + Estimator in the ``align_samples`` family. ``"stalign"`` (JAX LDDMM). + on + ``"obs"`` aligns the ``obsm`` point clouds. ``"image"`` is reserved and + currently raises :class:`NotImplementedError`. + ref_key, query_key + Table keys, required (and only valid) for SpatialData inputs. + spatial_key + ``obsm`` key holding the ``(x, y)`` coordinates. Defaults to ``"spatial"``. + output_mode + - ``"object"`` (default) -- return the raw :class:`FitResult`; nothing is written. + - ``"inplace"`` -- write the aligned coordinates into the query and return ``None``. + - ``"copy"`` -- write into a copy of the query and return the copy. + key_added + Destination ``obsm`` key for the aligned coordinates. If ``None`` it + defaults to ``f"aligned_{spatial_key}"``; if that key already exists and + ``key_added`` was not given explicitly, a :class:`ValueError` is raised + (pass ``key_added`` to overwrite intentionally). + method_kwargs + Forwarded to the estimator's ``fit`` (e.g. ``config=STalignConfig(...)``). + """ + validate_on(on) + validate_output_mode(output_mode) + if on == "image": + raise NotImplementedError("`align(on='image')` is not implemented yet; use `on='obs'`.") + + ref_adata, query_adata, container, element_key = resolve_obs_pair(data_ref, data_query, ref_key, query_key) + ref_xy = get_coords(ref_adata, spatial_key) + query_xy = get_coords(query_adata, spatial_key) + + result = ALIGN_SAMPLES.get(method)().fit(ref_xy, query_xy, **method_kwargs) + + return writeback_obs( + result, + output_mode=output_mode, + query_adata=query_adata, + container=container, + element_key=element_key, + spatial_key=spatial_key, + key_added=key_added, + ) + + +def align_by_landmarks( + landmarks_ref: np.ndarray | Sequence[tuple[float, float]], + landmarks_query: np.ndarray | Sequence[tuple[float, float]], + *, + method: Literal["similarity", "affine"] = "similarity", + data: AnnData | SpatialData | None = None, + cs_name_ref: str | None = None, + cs_name_query: str | None = None, + spatial_key: str = "spatial", + output_mode: Literal["object", "copy", "inplace"] = "object", + key_added: str | None = None, +) -> FitResult | AnnData | SpatialData | None: + """Align by a closed-form fit on pre-paired landmarks. + + Parameters + ---------- + landmarks_ref, landmarks_query + Equal-length ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``), paired by + row order. No automatic correspondence matching is performed. + method + Estimator in the ``align_landmarks`` family: ``"similarity"`` (4 DOF) or + ``"affine"`` (6 DOF). + data + Target to write the alignment into. Required for ``output_mode`` other + than ``"object"``. + cs_name_ref, cs_name_query + Coordinate-system names. For a SpatialData ``data`` the fitted affine is + registered on every element in ``cs_name_query``, mapping into + ``cs_name_ref``. + spatial_key + ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. + output_mode + See :func:`align`. ``"object"`` (default) returns the :class:`FitResult`. + key_added + Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). + """ + validate_output_mode(output_mode) + + result = ALIGN_LANDMARKS.get(method)().fit( + landmarks_ref, + landmarks_query, + source_cs=cs_name_query, + target_cs=cs_name_ref, + ) + + if output_mode == "object": + return result + if data is None: + raise ValueError("`data` is required when `output_mode` is 'copy' or 'inplace'.") + + if isinstance(data, SpatialData): + return writeback_affine_sdata( + result, data, output_mode=output_mode, moving_cs=cs_name_query, target_cs=cs_name_ref + ) + if isinstance(data, AnnData): + return writeback_obs( + result, + output_mode=output_mode, + query_adata=data, + container=None, + element_key=None, + spatial_key=spatial_key, + key_added=key_added, + ) + raise TypeError(f"`data` must be AnnData or SpatialData, got {type(data).__name__}.") diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py new file mode 100644 index 000000000..ff3437065 --- /dev/null +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -0,0 +1,158 @@ +"""Input resolvers and output write-back for the public align functions. + +This is the *only* layer that knows about the ``AnnData | SpatialData`` argument +shapes and the ``output_mode`` write-back strategies. The fit estimators in +:mod:`squidpy.experimental._fit` operate on plain arrays and never see a +container. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +from anndata import AnnData +from spatialdata import SpatialData + +from squidpy._validators import assert_key_in_sdata + +if TYPE_CHECKING: + from squidpy.experimental._fit import FitResult + from squidpy.experimental._fit.align_landmarks import AffineFitResult + + +# --------------------------------------------------------------------------- +# Read side +# --------------------------------------------------------------------------- + + +def resolve_obs_pair( + data_ref: AnnData | SpatialData, + data_query: AnnData | SpatialData | None, + ref_key: str | None, + query_key: str | None, +) -> tuple[AnnData, AnnData, SpatialData | None, str | None]: + """Normalise ``align(on="obs")`` inputs. + + Returns ``(ref_adata, query_adata, query_container, query_key)`` where + ``query_container`` is the SpatialData to write back into (``None`` for plain + AnnData inputs). + """ + if isinstance(data_ref, AnnData): + if data_query is None: + raise ValueError("`data_query` is required when `data_ref` is an AnnData.") + if not isinstance(data_query, AnnData): + raise TypeError( + f"Mixed AnnData/SpatialData inputs are not supported; `data_query` is {type(data_query).__name__}." + ) + if ref_key is not None or query_key is not None: + raise ValueError("`ref_key`/`query_key` are only valid for SpatialData inputs.") + return data_ref, data_query, None, None + + if not isinstance(data_ref, SpatialData): + raise TypeError(f"`data_ref` must be AnnData or SpatialData, got {type(data_ref).__name__}.") + + sdata_query = data_ref if data_query is None else data_query + if not isinstance(sdata_query, SpatialData): + raise TypeError( + f"Mixed AnnData/SpatialData inputs are not supported; `data_query` is {type(data_query).__name__}." + ) + if ref_key is None or query_key is None: + raise ValueError("`ref_key` and `query_key` are required for SpatialData inputs.") + assert_key_in_sdata(data_ref, ref_key, attr="tables") + assert_key_in_sdata(sdata_query, query_key, attr="tables") + return data_ref.tables[ref_key], sdata_query.tables[query_key], sdata_query, query_key + + +def get_coords(adata: AnnData, spatial_key: str) -> np.ndarray: + """Return a validated ``(N, 2)`` ``(x, y)`` coordinate array from ``obsm``.""" + if spatial_key not in adata.obsm: + raise KeyError(f"`obsm[{spatial_key!r}]` not found; pass `spatial_key=` to select the coordinate key.") + arr = np.asarray(adata.obsm[spatial_key], dtype=float) + if arr.ndim != 2 or arr.shape[1] != 2: + raise ValueError(f"`obsm[{spatial_key!r}]` must be an (N, 2) array, found shape {arr.shape}.") + return arr + + +# --------------------------------------------------------------------------- +# Write side +# --------------------------------------------------------------------------- + + +def writeback_obs( + result: FitResult, + *, + output_mode: str, + query_adata: AnnData, + container: SpatialData | None, + element_key: str | None, + spatial_key: str, + key_added: str | None, +) -> FitResult | AnnData | SpatialData | None: + """Bake ``result.transform(coords)`` into the query ``obsm`` per ``output_mode``.""" + if output_mode == "object": + return result + + dest = _resolve_dest(query_adata, spatial_key=spatial_key, key_added=key_added) + new_coords = result.transform(get_coords(query_adata, spatial_key)) + + if container is None: + target = query_adata if output_mode == "inplace" else query_adata.copy() + target.obsm[dest] = new_coords + return None if output_mode == "inplace" else target + + sdata = container if output_mode == "inplace" else shallow_copy_sdata(container) + sdata.tables[element_key].obsm[dest] = new_coords + return None if output_mode == "inplace" else sdata + + +def writeback_affine_sdata( + result: AffineFitResult, + sdata: SpatialData, + *, + output_mode: str, + moving_cs: str | None, + target_cs: str | None, +) -> SpatialData | None: + """Register the fitted affine on every element living in ``moving_cs``. + + Non-destructive: it adds a transformation into ``target_cs`` so the whole + coordinate system inherits the alignment. Nothing is materialised. + """ + from spatialdata.transformations import Affine, get_transformation, set_transformation + + if moving_cs is None or target_cs is None: + raise ValueError("`cs_name_query` and `cs_name_ref` are required to register a transform on a SpatialData.") + + out = sdata if output_mode == "inplace" else shallow_copy_sdata(sdata) + sd_affine = Affine(np.asarray(result.matrix), input_axes=("x", "y"), output_axes=("x", "y")) + touched = False + for _etype, _name, element in out.gen_elements(): + if isinstance(element, AnnData): + continue + if moving_cs not in get_transformation(element, get_all=True): + continue + set_transformation(element, sd_affine, to_coordinate_system=target_cs) + touched = True + if not touched: + raise KeyError(f"No elements in the SpatialData are registered to coordinate system {moving_cs!r}.") + return None if output_mode == "inplace" else out + + +def _resolve_dest(adata: AnnData, *, spatial_key: str, key_added: str | None) -> str: + """Resolve the destination obsm key, guarding against silent overwrite.""" + if key_added is not None: + return key_added + dest = f"aligned_{spatial_key}" + if dest in adata.obsm: + raise ValueError( + f"`obsm[{dest!r}]` already exists. Pass `key_added` explicitly to choose the destination " + f"(or to overwrite it intentionally)." + ) + return dest + + +def shallow_copy_sdata(sdata: SpatialData) -> SpatialData: + """Shallow copy of a SpatialData for ``output_mode='copy'`` (via ``subset``).""" + names = [name for _, name, _ in sdata.gen_elements()] + return sdata.subset(names, filter_tables=False, include_orphan_tables=True) diff --git a/src/squidpy/experimental/tl/_align/_validation.py b/src/squidpy/experimental/tl/_align/_validation.py new file mode 100644 index 000000000..d501d8208 --- /dev/null +++ b/src/squidpy/experimental/tl/_align/_validation.py @@ -0,0 +1,16 @@ +"""Argument validation for the public ``align`` / ``align_by_landmarks`` functions.""" + +from __future__ import annotations + +from squidpy._validators import assert_one_of + +OUTPUT_MODES = ("object", "copy", "inplace") +ON_VALUES = ("obs", "image") + + +def validate_output_mode(value: str) -> None: + assert_one_of(value, OUTPUT_MODES, name="output_mode") + + +def validate_on(value: str) -> None: + assert_one_of(value, ON_VALUES, name="on") diff --git a/tests/experimental/fit/test_stalign.py b/tests/experimental/fit/test_stalign.py index 56769e729..f1d8f3bfb 100644 --- a/tests/experimental/fit/test_stalign.py +++ b/tests/experimental/fit/test_stalign.py @@ -11,8 +11,8 @@ pytest.importorskip("jax") -from squidpy.experimental._fit._methods import ALIGN, StalignEstimator, StalignFitResult -from squidpy.experimental._fit._methods._stalign._tools import ( +from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES, StalignEstimator, StalignFitResult +from squidpy.experimental._fit.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, @@ -40,8 +40,8 @@ def _tiny_config() -> STalignConfig: def test_stalign_registered_in_align_family() -> None: - assert "stalign" in ALIGN.keys() - assert ALIGN.get("stalign") is StalignEstimator + assert "stalign" in ALIGN_SAMPLES.keys() + assert ALIGN_SAMPLES.get("stalign") is StalignEstimator def test_stalign_fit_returns_displacement_result() -> None: diff --git a/tests/experimental/tl/__init__.py b/tests/experimental/tl/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py new file mode 100644 index 000000000..30a2051d5 --- /dev/null +++ b/tests/experimental/tl/test_align.py @@ -0,0 +1,83 @@ +"""Integration tests for the public ``align`` (STalign) function. + +Tiny synthetic fixtures with ``niter=1`` keep these fast; they verify wiring, +write-back modes, and the key guard -- not solver quality. +""" + +from __future__ import annotations + +import numpy as np +import pytest +from anndata import AnnData + +pytest.importorskip("jax") + +from squidpy.experimental._fit.align_samples import StalignFitResult +from squidpy.experimental._fit.align_samples._stalign_impl._tools import ( + STalignConfig, + STalignPreprocessConfig, + STalignRegistrationConfig, +) +from squidpy.experimental.tl import align + + +def _adata(*, key: str = "spatial") -> AnnData: + pts = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) + adata = AnnData(np.zeros((pts.shape[0], 1))) + adata.obsm[key] = pts + return adata + + +def _tiny() -> STalignConfig: + return STalignConfig( + preprocess=STalignPreprocessConfig(dx=0.5, blur=1.0), + registration=STalignRegistrationConfig(a=1.0, expand=1.0, nt=1, niter=1, epV=1.0), + ) + + +def test_object_mode_returns_result_and_touches_nothing() -> None: + ref, query = _adata(), _adata() + result = align(ref, query, method="stalign", output_mode="object", config=_tiny()) + assert isinstance(result, StalignFitResult) + assert result.deltas.shape == query.obsm["spatial"].shape + assert "aligned_spatial" not in query.obsm + + +def test_inplace_writes_explicit_key() -> None: + ref, query = _adata(), _adata() + out = align(ref, query, output_mode="inplace", key_added="spatial_aligned", config=_tiny()) + assert out is None + assert query.obsm["spatial_aligned"].shape == query.obsm["spatial"].shape + + +def test_inplace_default_key() -> None: + ref, query = _adata(), _adata() + align(ref, query, output_mode="inplace", config=_tiny()) + assert "aligned_spatial" in query.obsm + + +def test_copy_leaves_original_untouched() -> None: + ref, query = _adata(), _adata() + out = align(ref, query, output_mode="copy", key_added="aligned", config=_tiny()) + assert isinstance(out, AnnData) and out is not query + assert "aligned" in out.obsm + assert "aligned" not in query.obsm + + +def test_existing_default_key_requires_explicit_key_added() -> None: + ref, query = _adata(), _adata() + query.obsm["aligned_spatial"] = np.zeros_like(query.obsm["spatial"]) + with pytest.raises(ValueError, match="aligned_spatial.*already exists"): + align(ref, query, output_mode="inplace", config=_tiny()) + + +def test_image_not_implemented() -> None: + ref, query = _adata(), _adata() + with pytest.raises(NotImplementedError, match="on='image'"): + align(ref, query, on="image", config=_tiny()) + + +def test_missing_spatial_key() -> None: + ref, query = _adata(), _adata() + with pytest.raises(KeyError, match="missing.*not found"): + align(ref, query, spatial_key="missing", config=_tiny()) diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py new file mode 100644 index 000000000..05e3e472d --- /dev/null +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +import numpy as np +import pytest +from anndata import AnnData + +from squidpy.experimental._fit.align_landmarks import AffineFitResult +from squidpy.experimental.tl import align_by_landmarks + +# square corners; query = ref shifted by (5, 7) -> a pure translation both models recover +_REF = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) +_SHIFT = np.array([5.0, 7.0]) +_QUERY = _REF + _SHIFT + + +def _adata(coords: np.ndarray = _QUERY, *, key: str = "spatial") -> AnnData: + adata = AnnData(np.zeros((coords.shape[0], 1))) + adata.obsm[key] = coords.copy() + return adata + + +@pytest.mark.parametrize("method", ["similarity", "affine"]) +def test_object_mode_returns_affine_result(method: str) -> None: + result = align_by_landmarks(_REF, _QUERY, method=method, output_mode="object") + assert isinstance(result, AffineFitResult) + assert result.matrix.shape == (3, 3) + # affine maps query -> ref + np.testing.assert_allclose(result.transform(_QUERY), _REF, atol=1e-6) + assert result.metadata["method"] == method + + +def test_object_is_default() -> None: + assert isinstance(align_by_landmarks(_REF, _QUERY), AffineFitResult) + + +def test_anndata_inplace_writes_default_key() -> None: + adata = _adata() + out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace") + assert out is None + assert "aligned_spatial" in adata.obsm + np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) + # source coords untouched + np.testing.assert_allclose(adata.obsm["spatial"], _QUERY) + + +def test_anndata_copy_leaves_original_untouched() -> None: + adata = _adata() + out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="copy", key_added="xy_aligned") + assert isinstance(out, AnnData) and out is not adata + assert "xy_aligned" in out.obsm + assert "xy_aligned" not in adata.obsm + + +def test_custom_spatial_key() -> None: + adata = _adata(key="loc") + align_by_landmarks(_REF, _QUERY, method="affine", data=adata, spatial_key="loc", output_mode="inplace") + assert "aligned_loc" in adata.obsm + + +def test_existing_default_key_requires_explicit_key_added() -> None: + adata = _adata() + adata.obsm["aligned_spatial"] = np.zeros_like(_QUERY) + with pytest.raises(ValueError, match="aligned_spatial.*already exists"): + align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace") + # explicit key_added overwrites without error + align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace", key_added="aligned_spatial") + np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) + + +def test_write_mode_requires_data() -> None: + with pytest.raises(ValueError, match="`data` is required"): + align_by_landmarks(_REF, _QUERY, method="affine", output_mode="inplace") + + +def test_too_few_landmarks() -> None: + with pytest.raises(ValueError, match="at least 3 landmark pairs"): + align_by_landmarks(_REF[:2], _QUERY[:2], method="affine", output_mode="object") + + +def test_length_mismatch() -> None: + with pytest.raises(ValueError, match="same shape"): + align_by_landmarks(_REF, _QUERY[:3], method="affine", output_mode="object") + + +def test_unknown_method_lists_available() -> None: + with pytest.raises(ValueError, match=r"Unknown align_landmarks method 'nope'"): + align_by_landmarks(_REF, _QUERY, method="nope", output_mode="object") + + +def test_spatialdata_registers_transformation() -> None: + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Identity, get_transformation + + pts = PointsModel.parse(_QUERY, transformations={"query_cs": Identity()}) + sdata = sd.SpatialData(points={"pts": pts}) + + out = align_by_landmarks( + _REF, + _QUERY, + method="affine", + data=sdata, + cs_name_query="query_cs", + cs_name_ref="ref_cs", + output_mode="inplace", + ) + assert out is None + assert "ref_cs" in get_transformation(sdata.points["pts"], get_all=True) From 14d36b0d2b8795a5ba1b380906a062394e957a26 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 10:05:17 +0200 Subject: [PATCH 072/131] undo lock file --- uv.lock | 6445 ------------------------------------------------------- 1 file changed, 6445 deletions(-) delete mode 100644 uv.lock diff --git a/uv.lock b/uv.lock deleted file mode 100644 index a8b388cdf..000000000 --- a/uv.lock +++ /dev/null @@ -1,6445 +0,0 @@ -version = 1 -requires-python = ">=3.11" -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", - "python_full_version < '3.12'", -] - -[[package]] -name = "aiobotocore" -version = "3.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aioitertools" }, - { name = "botocore" }, - { name = "jmespath" }, - { name = "multidict" }, - { name = "python-dateutil" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/75/42cce839c2ec263ff74b10b650fe36b066fbb124cbee6f247eac0983e1ab/aiobotocore-3.7.0.tar.gz", hash = "sha256:c64d871ed5491a6571948dd48eabd185b46c6c23b64e3afd0c059fc7593ada30", size = 127054 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl", hash = "sha256:680bde7c64679a821a9312641b759d9497f790ba8b2e88c6959e6273ee765b8e", size = 89539 }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062 }, -] - -[[package]] -name = "aiohttp" -version = "3.13.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513 }, - { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748 }, - { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673 }, - { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757 }, - { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152 }, - { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010 }, - { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251 }, - { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969 }, - { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871 }, - { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844 }, - { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969 }, - { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193 }, - { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477 }, - { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198 }, - { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321 }, - { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069 }, - { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859 }, - { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876 }, - { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557 }, - { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258 }, - { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199 }, - { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013 }, - { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501 }, - { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981 }, - { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934 }, - { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671 }, - { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219 }, - { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049 }, - { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557 }, - { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931 }, - { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125 }, - { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427 }, - { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534 }, - { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446 }, - { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930 }, - { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927 }, - { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141 }, - { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476 }, - { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507 }, - { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465 }, - { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523 }, - { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113 }, - { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351 }, - { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205 }, - { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618 }, - { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185 }, - { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311 }, - { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147 }, - { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356 }, - { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637 }, - { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896 }, - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721 }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663 }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094 }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701 }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360 }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023 }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795 }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405 }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082 }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346 }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891 }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113 }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088 }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976 }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444 }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128 }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029 }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758 }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883 }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668 }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461 }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661 }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800 }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382 }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724 }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027 }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644 }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630 }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403 }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924 }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119 }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072 }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819 }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441 }, -] - -[[package]] -name = "aioitertools" -version = "0.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/3c/53c4a17a05fb9ea2313ee1777ff53f5e001aefd5cc85aa2f4c2d982e1e38/aioitertools-0.13.0.tar.gz", hash = "sha256:620bd241acc0bbb9ec819f1ab215866871b4bbd1f73836a55f799200ee86950c", size = 19322 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl", hash = "sha256:0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be", size = 24182 }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, -] - -[[package]] -name = "anndata" -version = "0.12.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "array-api-compat" }, - { name = "h5py" }, - { name = "legacy-api-wrap" }, - { name = "natsort" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "scipy" }, - { name = "scverse-misc", version = "0.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "scverse-misc", version = "0.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6c/0f/4c6b3a4c5dc57bd02f0656666aefae7e83b8158b839f60cfb1c5f2fbecab/anndata-0.12.16.tar.gz", hash = "sha256:26d557fd147728993aac99c2eef676e1e316a84bbeee3c176ca6c5d66e5fc443", size = 2255740 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/31/bb8da9d45d2b54b7d7ba31945b7024374028274d089828dfd15035411828/anndata-0.12.16-py3-none-any.whl", hash = "sha256:2661332ed8e757886aafddc2f76103846ae17a16e1c003d76c3001ad067d8bc6", size = 175291 }, -] - -[[package]] -name = "annsel" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anndata" }, - { name = "more-itertools" }, - { name = "narwhals", extra = ["pandas"] }, - { name = "pooch" }, - { name = "session-info2" }, - { name = "tqdm" }, - { name = "universal-pathlib" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/93/cdbe4903acb43fd743832e0c2933b88ec2dfd2a87b147992c45b90f647c8/annsel-0.1.2.tar.gz", hash = "sha256:a9ab14495f3e28c216a5cb74f918542710ac82e22bb04db57af233bbbf536da8", size = 341977 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/43/fff37380b704b60fad17c298b631044d98e3db8b9721df7897c967facf51/annsel-0.1.2-py3-none-any.whl", hash = "sha256:9659f60f4ee5545bc030341b6b8e932e0a615b95ac83d9cb6e2bd50951ae26da", size = 19120 }, -] - -[[package]] -name = "anyio" -version = "4.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, -] - -[[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, -] - -[[package]] -name = "argon2-cffi" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argon2-cffi-bindings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, -] - -[[package]] -name = "argon2-cffi-bindings" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, -] - -[[package]] -name = "array-api-compat" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/e5/9a12dd1c2b0ad61f3c3ad0fc14b888c65fd735dd9d26805f77317303cbe5/array_api_compat-1.14.0.tar.gz", hash = "sha256:c819ba707f5c507800cb545f7e6348ff1ecc46538381d9ad9b371ffc9cd6d784", size = 106369 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/d3/54cd560804a8c2b898824778e86c13c2a14600bc83532a9c4f69f2f469c3/array_api_compat-1.14.0-py3-none-any.whl", hash = "sha256:ed5af1f9b6595a199c942505f281ec994892556b6efc24679a0501e87a7d6279", size = 60124 }, -] - -[[package]] -name = "arrow" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, -] - -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, -] - -[[package]] -name = "async-lru" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403 }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 }, -] - -[[package]] -name = "backports-tarfile" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 }, -] - -[[package]] -name = "backports-zstd" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/05/480d439b482edf59b786bc19b474d990c61942e372f5de3dc14acac8154d/backports_zstd-1.5.0.tar.gz", hash = "sha256:a5e622a82eb183b4fbe18032755ce0a15fa9a82f2adb9b621620b91247aaedb7", size = 998556 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/bc/083c0ebee316f4863ed288c4a5eaa1e98be115e82deb8855da8bab1c7701/backports_zstd-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fbaa5502617dc4f04327c7a2951f0fcdca7aaef93ddf32c15dc8b620208174fa", size = 436838 }, - { url = "https://files.pythonhosted.org/packages/cd/e5/bf778667fff6598dbd0791745123ed964aee94753ae8e4e92aa1e07417b6/backports_zstd-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:204f00d62e95aab987c7c019452b2373bdefb17252443765f2ede7f15b6e669a", size = 363215 }, - { url = "https://files.pythonhosted.org/packages/63/a5/4fae78734dbefcb4b5386137c807e2107c4bc94e85c0d9eaae79206dde84/backports_zstd-1.5.0-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:2c77c0d4c330afd26d2a98f3d689ab922ec3f046014a1614ddcaad437666ac05", size = 507161 }, - { url = "https://files.pythonhosted.org/packages/d8/ec/b64409f0cf56fb65181d6f5d9130058f19d5c3c9f8c581a5e2bd62642630/backports_zstd-1.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bb2f2d2c07358edeaa251cf804b993e9f0d5d93af8a7ea2414d80ff3c105e95", size = 476728 }, - { url = "https://files.pythonhosted.org/packages/4d/10/4c1693cb4e129585a6e4cb565106cad7347e61c43c8375b9e9cadb00eb06/backports_zstd-1.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89f554abcebcb2c487024e63be8059083775c5fd351fec0cc2dc3e9f528714", size = 582388 }, - { url = "https://files.pythonhosted.org/packages/45/b9/dc748a0e7d21ce2228241f6e8af96d297c80ab69c4c49429309b8fa3beb8/backports_zstd-1.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea969758af743000d822fc3a69dc9de059bbbb8d07d2f13e06ff49ac63cce74f", size = 642091 }, - { url = "https://files.pythonhosted.org/packages/de/5f/02366ddae6e008d53df71605e4e3ca8dcea5d1dfcba29040b46883a23127/backports_zstd-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:775ad82d268923639bc924013fc61561df376c148506b241f0f80718b5bb3a2f", size = 492256 }, - { url = "https://files.pythonhosted.org/packages/c0/c7/c5e7824c17abc87dbb24c7c90dc43054d701533cf04d3531cb9b7105cdac/backports_zstd-1.5.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:663128370bbc2ebcc436b8977bc434a7bf29919d92d91fee05ed6fb0fa807646", size = 566214 }, - { url = "https://files.pythonhosted.org/packages/12/7b/ee7368c4ad8f5e00b3fd84fc566fb7714aa766c5672500793990e19efa00/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572c76832e9a24da4084befa52c23f4c03fede2aa250ae6250cbc5a11b980f69", size = 482666 }, - { url = "https://files.pythonhosted.org/packages/77/36/2826f9f04b6c91d5f707f49188ac6f5ec7487b36d73caedfa20db3307826/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9410bcbcd3afd787a15a276d68f954d1703788c780faa421183a61d39da8b862", size = 510594 }, - { url = "https://files.pythonhosted.org/packages/84/3b/95342baf0e301b7d06c6862389f8520a9d71f073a6c1a5b86182e7d89148/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0fab15e6895bef621041dd82d6306ffa24889257dd902c4b98b88e4260b3465d", size = 586713 }, - { url = "https://files.pythonhosted.org/packages/bc/32/73d2b8f572960307406b084bb8932f4ebd9fcedb05d1502e04fecf25970a/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ffde637b6d0082f1c3356657002469cf199c7c12d50d9822a55b13425c778d3", size = 564037 }, - { url = "https://files.pythonhosted.org/packages/8f/a4/6e319fa7fa5851c3ca9701cbded9522c16018432a01a33a95cc0fccb6b4a/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c01d377c1489cb2230bf6a9ff01c73c42863cc96ee648c49923d4f6d4ea4e2d5", size = 632626 }, - { url = "https://files.pythonhosted.org/packages/67/5c/10df0444db05f9276b286d230a3d6948ad47c593fc22925b8fe551d34b26/backports_zstd-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4080bb9c8a51bb2bf8caf8018d78278cd49eb924cb06a54f56a411095e2ac912", size = 496270 }, - { url = "https://files.pythonhosted.org/packages/f1/ad/6cd1de5cd858ac653833098f13a4643a4c9db484072350d3dbf299cc46f1/backports_zstd-1.5.0-cp311-cp311-win32.whl", hash = "sha256:9f4fe3fd82c8c6e8a9fdc5c71f92f9fe2442d02e7f59fddef25a955e189e3f38", size = 289754 }, - { url = "https://files.pythonhosted.org/packages/1d/1b/df94ad1cb79705d717f7e1063da642c538a6d7ce6443c8e60355fa507ea4/backports_zstd-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e7c0372fa036751109604c70a8c87e59faaacc195d519c8cb9e0e527ee2b5478", size = 314829 }, - { url = "https://files.pythonhosted.org/packages/e8/e7/24e60da7cc89b9ed1c5b474678e316dd0ddfe7cd1de39b23d04452ca5946/backports_zstd-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:264a66137555bb4648f7e64cfc514d820758072684f373269fcdd2e8d4a90306", size = 291497 }, - { url = "https://files.pythonhosted.org/packages/24/71/29ed213344f8f62b7520745d7df3752d88db456aff9d8b706bdf5eb99a3c/backports_zstd-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1858cacdb3e50105a1b60acdc3dd5b18650077d12dce243e19d5c88e8172bd71", size = 437170 }, - { url = "https://files.pythonhosted.org/packages/d0/e3/a58a3eb8fc54d4e3e4f684ed7b1f688da02e5bda5ae5e2809e94cf2ead2f/backports_zstd-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ccffc0a1974ecc2cc42afa4c15f56d036a4b2bae0abc46e6ba9b3358d9b1c037", size = 363265 }, - { url = "https://files.pythonhosted.org/packages/3f/03/9d13840d206dec1c4698c803f61c58379b3578cb9dc6140ba5fa4ce2f31d/backports_zstd-1.5.0-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:ab3430ab4d4ac3fb1bc1e4174d137731e51363b6abd5e51a1599690fe9c7d61d", size = 507527 }, - { url = "https://files.pythonhosted.org/packages/6a/8f/8dc4b5736dca218cbca9609549a8f6dc202990abdb49afdc6112442f5360/backports_zstd-1.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c737c1cb4a10c2d0f6cba9a347522858094f0a737b4558c67a777bcaa4a795cd", size = 477352 }, - { url = "https://files.pythonhosted.org/packages/96/2c/65a66976a761b5b62eacbaed5ed418c694b24b5c480399315d799751de62/backports_zstd-1.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0379c66510681a6b2780d3f3ef2cff54d01204b52448d64bde1855d40f856a04", size = 582799 }, - { url = "https://files.pythonhosted.org/packages/d3/e9/ee93a66cd28cb3ad7f3c04d1105325a5428671b18bd41ba9ed8b43bc44cf/backports_zstd-1.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c7474b291e264c9609358d3875cf539623f7a65339c2b533020992b1a4c095b", size = 641530 }, - { url = "https://files.pythonhosted.org/packages/e4/4b/2cecd4d6679f175f28ae02022bd2050ff4023e38902fae104dbe2e231911/backports_zstd-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb73c22444617bc5a3abf32dd27b3f2085898cfe3b95e6855300e9189898a3bd", size = 495324 }, - { url = "https://files.pythonhosted.org/packages/4d/20/ee21e4e791e31f38f7a70b3961eb64b350d9be802a335e7a04c02b41b197/backports_zstd-1.5.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6cd7f6c33afd89354f74469e315e72754e3040f91f7b685061e225d9e36e3e8e", size = 569796 }, - { url = "https://files.pythonhosted.org/packages/76/da/86c9a2ea384885b60638b3e47113198449568d0e36ef3834d1f969623092/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2106309071f279b38d3663c55c7fed192733b4f332b50eb3fa707e54bad6967a", size = 483367 }, - { url = "https://files.pythonhosted.org/packages/5d/f0/c95c6e4dd28fc314547782a482839e422283d62c2aaf45d30672109a4a1e/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:56fffa80be74cb11ac843333bbdc56e466c87967706886b3efd6b16d83830d90", size = 510976 }, - { url = "https://files.pythonhosted.org/packages/0e/a2/72777b7e1872228a13b09b0bf77ae6cf626008d462cc2e1a0ae64721fd55/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5e8b8251eec80e67e30ec79dfc5b3b1ada069b9ac48b56b102f3e2c6f8281062", size = 587190 }, - { url = "https://files.pythonhosted.org/packages/f5/a1/db5d1aee59da308eadeaa189764a4ec68e98495c309a13dcb8da5718fef1/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f334dd17ffead361aa9090e40151bd123507ce213a62733121b7145c6711cbde", size = 567395 }, - { url = "https://files.pythonhosted.org/packages/00/0f/39ca1a6e8c5c2dc81da9e06c44d1990cc464f4b16dae214e877afd7adfc0/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:78cbfd061255fef6de5070a54e0f9c00e8aabad5c99dd2ad884a3a7d1acc09ae", size = 632048 }, - { url = "https://files.pythonhosted.org/packages/73/fd/a438ee4fc615016dbe96112b709b6805ee19eb215f46e208c8fbce086d8d/backports_zstd-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f55d70df44f49d599e20033013bc1ae705202735c45d4bca8eb963b225e15fd", size = 499833 }, - { url = "https://files.pythonhosted.org/packages/f7/42/f544fde4de32687e28c514288ae3c11106ba644e9dd580992cbd704bbb49/backports_zstd-1.5.0-cp312-cp312-win32.whl", hash = "sha256:a8b096e0383a3bcab34f8c97b79e1a52051189d11258bbc2bc1145997a15dd1d", size = 289876 }, - { url = "https://files.pythonhosted.org/packages/ad/31/9c29cd3175892e5ee909f5e8d14707fa07815301ff24b5c697d1cea62a77/backports_zstd-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:e2802899ba4ef1a062ffe4bb1292c5df32011a54b4c3004c54f46ec975f39554", size = 314933 }, - { url = "https://files.pythonhosted.org/packages/11/ee/1a50acd6446c0d57c4f93ad6ce68e1a631ad920737a6b2d0bbbc47de7f42/backports_zstd-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:3c0353e66942afbd45518788cfbd1e9e117828ceb390fa50517f46f291850d8e", size = 291665 }, - { url = "https://files.pythonhosted.org/packages/c6/e6/252521e3a847eb200bc0a1d528542d651b9c8dc7953e231c39ed2890d5ff/backports_zstd-1.5.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:02a57ee8598dd863c0b11c7af00042ce6bc045bf6f4249fa4c322c62614ca1fd", size = 400134 }, - { url = "https://files.pythonhosted.org/packages/36/43/27ef105ffa2da3d52218d4a7b2e14037974283953b3ee790358af6e9b4df/backports_zstd-1.5.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:c56c11eb3173d540e1fb0216f7ab477cbd3a204eca41f5f329059ee8a5d2ad47", size = 454225 }, - { url = "https://files.pythonhosted.org/packages/0e/c9/cdcba1244347500d00567ce2cd6bf04c92d1b0fb6405fb8e13c07715eb46/backports_zstd-1.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ef98f632026aa8e6ce05d786977092798efbe78677aa71219f22d31787809c90", size = 357229 }, - { url = "https://files.pythonhosted.org/packages/df/da/cea04dab3ffb940bde9a59866bde6f2594a7b3ef2948a63fb3898f73d311/backports_zstd-1.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:c3712300b18f9d07f788b03594b2f34dfad89d77df96938a640c5007522a6b69", size = 365907 }, - { url = "https://files.pythonhosted.org/packages/da/c4/6a71df2e65033f9b7d8017d77ea2bb572fc2ebc814ea383fdcda4187597a/backports_zstd-1.5.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:bdbc75d1f54df70b65bcfbc8aa0cac21475f79665bb045960af606dc07b56090", size = 446453 }, - { url = "https://files.pythonhosted.org/packages/66/e7/f98ad1a6a249c27884df9d28cf6ebc3c368e0e3288a741c1d51a572bb3d7/backports_zstd-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93d306300d25e59f1cbe98cda494bf295be03a20e8b2c5602ee5ddc03ded29f2", size = 436634 }, - { url = "https://files.pythonhosted.org/packages/ba/42/d0393ecc64e2ab6ae1b5ca7edbe26e3fe5196885f15d6cc4bce7254e29cd/backports_zstd-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:305d2e4ae9a595d0fd9d5bea5a7a2163306c6c4dcc5eec35ecd5008219d4580e", size = 362867 }, - { url = "https://files.pythonhosted.org/packages/41/fe/87aa9404763bada695d06e5cb9d0575bae033cbf3a2e4e3bd648760178f7/backports_zstd-1.5.0-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:c8f0967bf8d806b250fb1e905a6b8190e7ae83656d5308989243f84e01fa3774", size = 506844 }, - { url = "https://files.pythonhosted.org/packages/56/94/3af7ce637d148e0b0acb1298b61afe9a934ed425bad9ff05e87afbf6766d/backports_zstd-1.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76b7314ca9a253171e3e9524960e9e6411997323cf10aecbbc330faa7a90278d", size = 476975 }, - { url = "https://files.pythonhosted.org/packages/aa/6c/dc2aa1b48296ac6effc3bacb5a3061d40ed74bf73082dfe38eed2ba8362b/backports_zstd-1.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b1d0bf16bba86b1071731ced389f184e8de61c1afcafa584244f7f726632f92f", size = 582496 }, - { url = "https://files.pythonhosted.org/packages/f6/38/dd49d3dd27eda9b165ccd63d70538fea016a3e9e42923bbbc1d89fae8a43/backports_zstd-1.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:96709d27d406008575ef759405169d538040156704b457d8c0ac035127a46b67", size = 643257 }, - { url = "https://files.pythonhosted.org/packages/59/75/78e819272450aec2462f97a1bceb90bde481f9dba435bf9e76d580b4dec4/backports_zstd-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5737402c29b2bd5bc661d4cde08aed531ed326f2b59a7ad98dc07650dc99a2c9", size = 491958 }, - { url = "https://files.pythonhosted.org/packages/62/ae/d860f9cf21cb59d583a12166353bf71a439538e2b669f4a7736e400ca596/backports_zstd-1.5.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b65f37ddd375114dbf84658e7dd168e10f5a93394940bfefa7fafc2d3234450", size = 567198 }, - { url = "https://files.pythonhosted.org/packages/38/7c/b175d4c9ff60f964c8f6dd43211de905227cfde5a41eb5f654df58483025/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fae7825dde4f81c28b4c66b1e997f893e296c3f1668351952b3ed085eb9f8cd", size = 482792 }, - { url = "https://files.pythonhosted.org/packages/eb/e3/f7b50cf891a10da5f9c412ed4a9c4a772df4d4186d98a41e75c9b462f148/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3aa10e77c0e712d2dfb950910b50591c2fb11f0f1328814e23acc0b4950766df", size = 510363 }, - { url = "https://files.pythonhosted.org/packages/be/50/e7841fd4a65661d527697a0e2dab97295868965ccd4e3e12474472719a60/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:518b2ef54ce0fee6d29379cfd64ef66e639456f1b18943466e929b19677f135f", size = 586917 }, - { url = "https://files.pythonhosted.org/packages/c6/7c/57e985dbd621f0307b8c57cabb258eb976793f2aeaf8a5bc020e15b4a793/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:673a1e5fdaa6cb0c7a967eb33066b6dd564871b3498a93e11e2972998047d11f", size = 565004 }, - { url = "https://files.pythonhosted.org/packages/2f/8f/855ffcd1ee0fcf44c3fe62e36db8e7362292d450cc7c4b3f43edccbcd37a/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1277c07ff2d731586aa05aebd946a1b30184620d886a735dd5d5bf94a4a1061e", size = 633737 }, - { url = "https://files.pythonhosted.org/packages/20/39/c4129a03d268699200dfebe1ccab97c7c332d2794571afb372a62e4ed098/backports_zstd-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aff334c7c38b4aea2a899f3138a99c1d58f0686ad7815c74bff506ecf4333296", size = 496309 }, - { url = "https://files.pythonhosted.org/packages/8e/33/34152316dd244dcd43d5300ded3cf6e1b46d343e4e92620c23e533fa91df/backports_zstd-1.5.0-cp313-cp313-win32.whl", hash = "sha256:b932834c4d85360f46d1e7fbf3eee1e26ba594e0eb5c3ee1281e89bc1d48d06f", size = 289560 }, - { url = "https://files.pythonhosted.org/packages/71/c5/f759bc87fd77c88f4fdad2d878535fb7e9537c6a05876d206e6690bf33c6/backports_zstd-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:c71dfbeced720326a8917a6edf921c568dc2396228c6432205c6d7e7fe7f3707", size = 314812 }, - { url = "https://files.pythonhosted.org/packages/47/96/d7970dbb2fef34b549b34146090f48f41903cc7268b1ed1c7542eaa1852e/backports_zstd-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:7b5798b20ffff71ee4620a01f56fe0b50271724b4251db08c90a069446cc4752", size = 291411 }, - { url = "https://files.pythonhosted.org/packages/89/92/8e8769e1e3ebec16d39f455e317a0f137a191b1f122853d0377c660666ce/backports_zstd-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0ca2d4ac4901eada2cfb86fda692e5d4a1e09485d9f2ec5777dc6cd3154b3b46", size = 410809 }, - { url = "https://files.pythonhosted.org/packages/63/5c/741a2923020c45b85cad4dffffcb86dbfa2d4aaed27f18ee793428ef4c24/backports_zstd-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:20796211a623ec6e0061cef4d7cca760e9e0a0a951bb30dc9ba89ed4a3fea5e4", size = 340342 }, - { url = "https://files.pythonhosted.org/packages/c8/3b/68c4fe8a551d3f47ed75ddcf15dc7c777bb9d869fc0e0f5b7cacc9f158f5/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:5232cd2a58c60da4ceb0e09e42dbc579b92dda4a9301a756af0c738223a23487", size = 421476 }, - { url = "https://files.pythonhosted.org/packages/a8/4d/ab5dcd6ab9a7ac02ec42c4507211da7dadb9498abb655115c296077e2b8b/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:012d88a9ae08f331e1adc03dfbda4ff2ae7f76ea62455975827b215677a11aec", size = 395020 }, - { url = "https://files.pythonhosted.org/packages/55/aa/ec512a0d14552bbb4e75693f7065434b865956abd045ceb67f0574146241/backports_zstd-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbb7d79f8e43b6e0e17616961e425b9f8b32d9933e1db69242baa6e21f44a978", size = 414985 }, - { url = "https://files.pythonhosted.org/packages/aa/31/759d077aa680555e17c9d2bb09edf4c3428d895fe5d35a8df67684401b84/backports_zstd-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6172dcdd664ef243e55a35e6b45f1c866767c61043f0ddcd908abd14df662065", size = 300853 }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721 }, -] - -[[package]] -name = "bleach" -version = "6.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437 }, -] - -[package.optional-dependencies] -css = [ - { name = "tinycss2" }, -] - -[[package]] -name = "botocore" -version = "1.43.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/79/2f4be1896db3db7ccf44504253a175d56b6bd6b669619edc5147d1aa21ea/botocore-1.43.0.tar.gz", hash = "sha256:e933b31a2d644253e1d029d7d39e99ba41b87e29300534f189744cc438cdf928", size = 15286817 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl", hash = "sha256:cc5b15eaec3c6eac05d8012cb5ef17ebe891beb88a16ca13c374bfaece1241e6", size = 14970102 }, -] - -[[package]] -name = "certifi" -version = "2026.5.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134 }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344 }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560 }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613 }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476 }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374 }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597 }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574 }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971 }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972 }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078 }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076 }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820 }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635 }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271 }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048 }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529 }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097 }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983 }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519 }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572 }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963 }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361 }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, -] - -[[package]] -name = "cfgv" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445 }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705 }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419 }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901 }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742 }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061 }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239 }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173 }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841 }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304 }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455 }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036 }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739 }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277 }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819 }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281 }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843 }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328 }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061 }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031 }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239 }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589 }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733 }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652 }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229 }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552 }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806 }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316 }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274 }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468 }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460 }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330 }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828 }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627 }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008 }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303 }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282 }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595 }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986 }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711 }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036 }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998 }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056 }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537 }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176 }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723 }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085 }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819 }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915 }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234 }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042 }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706 }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727 }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882 }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860 }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564 }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276 }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238 }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189 }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352 }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024 }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869 }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541 }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634 }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384 }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133 }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257 }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851 }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393 }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251 }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609 }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014 }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979 }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238 }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110 }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824 }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103 }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194 }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827 }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168 }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018 }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958 }, -] - -[[package]] -name = "click" -version = "8.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639 }, -] - -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "colorcet" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/af/b969f541242b84cbbacabdf20862e487689e352bf0f02f90df2795d29da5/colorcet-3.2.1.tar.gz", hash = "sha256:48d9a67e6e59dc5c0a965aa1b46fe5d59cdc95cc36a95949f29313f950ac59f7", size = 2202958 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/24/e95471ae93c08d3606c9c7343cf65d490f154daa88b50581957a0aa780f4/colorcet-3.2.1-py3-none-any.whl", hash = "sha256:3f6fde13cef2169222dd5fe2a2bf847c02d644470fdf167ed566f6421df470f7", size = 262291 }, -] - -[[package]] -name = "comm" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773 }, - { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149 }, - { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222 }, - { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234 }, - { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555 }, - { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238 }, - { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218 }, - { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867 }, - { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677 }, - { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234 }, - { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123 }, - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419 }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979 }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653 }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536 }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397 }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601 }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288 }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386 }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018 }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567 }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655 }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257 }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034 }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672 }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234 }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169 }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859 }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062 }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932 }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024 }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578 }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524 }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730 }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897 }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751 }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486 }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106 }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548 }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297 }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023 }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157 }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570 }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713 }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189 }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251 }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810 }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871 }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264 }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819 }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650 }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833 }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692 }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424 }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300 }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769 }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892 }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748 }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554 }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118 }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555 }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295 }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027 }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428 }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331 }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831 }, - { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809 }, - { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593 }, - { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202 }, - { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207 }, - { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315 }, -] - -[[package]] -name = "coverage" -version = "7.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/d7/477ad149490e6cb849f28abea1dabb9c823cea72e7500c81b4240ce619c0/coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f", size = 219848 }, - { url = "https://files.pythonhosted.org/packages/91/82/a5eb47257c50601bb7b9a9d2857c67b7a3a85ad74180eb2c98bb1fbe0ce5/coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4", size = 220354 }, - { url = "https://files.pythonhosted.org/packages/43/8b/78419b5391a5cb706b6544390507e469d83ffc9a8248b02c4011aceb9365/coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1", size = 250771 }, - { url = "https://files.pythonhosted.org/packages/77/63/e77aaacd491182210d639636b7a8bba23ffffa9b82aa3762da9431855fa9/coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f", size = 252683 }, - { url = "https://files.pythonhosted.org/packages/65/1c/a022e3cfbec2ac241640003cb3a817e161d9c7f5aa9b49173756cdc03204/coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129", size = 254791 }, - { url = "https://files.pythonhosted.org/packages/61/d6/967e408aca4c1ceb88cb0cc677169110ae7f5995fb5eaf5fb1f5a1bb8f5d/coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860", size = 256748 }, - { url = "https://files.pythonhosted.org/packages/b8/be/869188f7fe28638078ec479331ace6dc5f7b40b7153eb616f47ab79404d8/coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c", size = 250907 }, - { url = "https://files.pythonhosted.org/packages/07/aa/adb7d3b4278d690e68703abcd76ab1b948242e3668d921711551b78f9ddb/coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7", size = 252483 }, - { url = "https://files.pythonhosted.org/packages/43/61/331c74103c62dcb0c4b9b3a0de9a61aca016208b0a90f109592a9f9ecc28/coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec", size = 250545 }, - { url = "https://files.pythonhosted.org/packages/f6/b6/c5dae3c104d89be04828f61810e6b3473825482e4c288cc4ed04553e08ae/coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef", size = 254310 }, - { url = "https://files.pythonhosted.org/packages/ad/a1/2b9d5863e3b83c01ad8199e3c597802fbb3a9dc90b058885804c20296d31/coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df", size = 250266 }, - { url = "https://files.pythonhosted.org/packages/7f/5e/0e511fbdb269359be26fe678a1c3fa1f2aa2a01573cc3f54268c8d6d4797/coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9", size = 251174 }, - { url = "https://files.pythonhosted.org/packages/85/10/e55307b622b3dd9671cb321824502dc10f93e72f2802b9946159a8edadeb/coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548", size = 222354 }, - { url = "https://files.pythonhosted.org/packages/71/cf/107421693cfb71e4f1ca5bf70443f64d4161878068d07a3e51c7ad21d17b/coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e", size = 223290 }, - { url = "https://files.pythonhosted.org/packages/b8/1d/3e3644585eb29e9dafefb19555078529a4d7cce12bd21929664eea989277/coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3", size = 221953 }, - { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022 }, - { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379 }, - { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888 }, - { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624 }, - { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739 }, - { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998 }, - { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296 }, - { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658 }, - { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803 }, - { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873 }, - { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372 }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245 }, - { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567 }, - { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372 }, - { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989 }, - { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044 }, - { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412 }, - { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412 }, - { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008 }, - { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241 }, - { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373 }, - { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635 }, - { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373 }, - { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341 }, - { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497 }, - { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159 }, - { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934 }, - { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584 }, - { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394 }, - { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015 }, - { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733 }, - { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086 }, - { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381 }, - { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458 }, - { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884 }, - { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022 }, - { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631 }, - { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443 }, - { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069 }, - { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780 }, - { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970 }, - { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157 }, - { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259 }, - { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320 }, - { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577 }, - { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091 }, - { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421 }, - { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466 }, - { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973 }, - { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318 }, - { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633 }, - { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488 }, - { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329 }, - { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291 }, - { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564 }, - { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107 }, - { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764 }, - { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837 }, - { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650 }, - { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218 }, - { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822 }, - { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084 }, - { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454 }, - { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578 }, - { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981 }, - { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112 }, - { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558 }, - { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447 }, - { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048 }, - { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781 }, - { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896 }, - { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214 }, - { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624 }, - { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728 }, - { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752 }, - { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815 }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "cryptography" -version = "48.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433 }, - { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620 }, - { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283 }, - { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573 }, - { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677 }, - { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808 }, - { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941 }, - { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579 }, - { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326 }, - { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672 }, - { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574 }, - { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776 }, - { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121 }, - { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042 }, - { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526 }, - { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116 }, - { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030 }, - { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640 }, - { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657 }, - { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362 }, - { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580 }, - { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283 }, - { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266 }, - { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228 }, - { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097 }, - { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582 }, - { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479 }, - { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481 }, - { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713 }, - { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165 }, - { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947 }, - { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059 }, - { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575 }, - { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126 }, - { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270 }, - { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797 }, - { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800 }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, -] - -[[package]] -name = "dask" -version = "2026.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "cloudpickle" }, - { name = "fsspec" }, - { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, - { name = "packaging" }, - { name = "partd" }, - { name = "pyyaml" }, - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/46/61ecde57bac647ca7eb6ffef8dcd90af6c1c649020874cd7fd8738003d62/dask-2026.1.1.tar.gz", hash = "sha256:12b1dbb0d6e92f287feb4076871600b2fba3a843d35ff214776ada5e9e7a1529", size = 10994732 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/4b/9cc373120658a2516aa5f6dcdde631c95d714b876d29ad8f8e009d793f3f/dask-2026.1.1-py3-none-any.whl", hash = "sha256:146b0ef2918eb581e06139183a88801b4a8c52d7c37758a91f8c3b75c54b0e15", size = 1481492 }, -] - -[package.optional-dependencies] -array = [ - { name = "numpy" }, -] - -[[package]] -name = "dask-image" -version = "2026.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dask", extra = ["array"] }, - { name = "numpy" }, - { name = "pims" }, - { name = "scipy" }, - { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/49/e592a13a5e1efdcdb8f1faab7c4e309c61648792e276f9ced5fb79381b33/dask_image-2026.5.0.tar.gz", hash = "sha256:ed6b462277e691b2c12b0890ba801a0f9a00cc1894b0aa71b195a7a1419b2b00", size = 80457 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/5b/15d6d6ff8697b188787609be059fe4f07f99fc00f43f68e9e1540fa8733e/dask_image-2026.5.0-py3-none-any.whl", hash = "sha256:acf86cd7f0f1e97804198d30b7cc931efd29f9dec86c65ec004b50405c3f5227", size = 43814 }, -] - -[[package]] -name = "datashader" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorcet" }, - { name = "multipledispatch" }, - { name = "numba" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "param" }, - { name = "pyct" }, - { name = "requests" }, - { name = "scipy" }, - { name = "toolz" }, - { name = "xarray" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/4b/a9141f286f0c01685e9715ba3404769a6138e74575c4d5ccbaa95a22c3bd/datashader-0.19.1.tar.gz", hash = "sha256:f62d880a4a431813f9bb3959e565feda79c1634f889aaadcf948cb0d0c114cdd", size = 10581968 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/41/247627c8b9fef5c605d00546b85771a8fe42975b9616a557cead5468789b/datashader-0.19.1-py3-none-any.whl", hash = "sha256:7ce7154ff3ed070607f429355f57002fee5a17964e47c0b6447eeafe8cef9c82", size = 10715897 }, -] - -[[package]] -name = "debugpy" -version = "1.8.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/56/c3baf5cbe4dd77427fd9aef99fcdade259ad128feeb8a786c246adb838e5/debugpy-1.8.20-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:eada6042ad88fa1571b74bd5402ee8b86eded7a8f7b827849761700aff171f1b", size = 2208318 }, - { url = "https://files.pythonhosted.org/packages/9a/7d/4fa79a57a8e69fe0d9763e98d1110320f9ecd7f1f362572e3aafd7417c9d/debugpy-1.8.20-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:7de0b7dfeedc504421032afba845ae2a7bcc32ddfb07dae2c3ca5442f821c344", size = 3171493 }, - { url = "https://files.pythonhosted.org/packages/7d/f2/1e8f8affe51e12a26f3a8a8a4277d6e60aa89d0a66512f63b1e799d424a4/debugpy-1.8.20-cp311-cp311-win32.whl", hash = "sha256:773e839380cf459caf73cc533ea45ec2737a5cc184cf1b3b796cd4fd98504fec", size = 5209240 }, - { url = "https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl", hash = "sha256:1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb", size = 5233481 }, - { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686 }, - { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588 }, - { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372 }, - { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835 }, - { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560 }, - { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272 }, - { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208 }, - { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930 }, - { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066 }, - { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425 }, - { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407 }, - { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521 }, - { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658 }, -] - -[[package]] -name = "decorator" -version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365 }, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, -] - -[[package]] -name = "deprecated" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298 }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, -] - -[[package]] -name = "distributed" -version = "2026.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "cloudpickle" }, - { name = "dask" }, - { name = "jinja2" }, - { name = "locket" }, - { name = "msgpack" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyyaml" }, - { name = "sortedcontainers" }, - { name = "tblib" }, - { name = "toolz" }, - { name = "tornado" }, - { name = "urllib3" }, - { name = "zict" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/19/0c13efdffc55cb311594f66c1c8d36a3c4711e427c820155fb9c59138b5e/distributed-2026.1.1.tar.gz", hash = "sha256:3d2709a43912797df3c345af3bb333bbf1a386ec1e9e6a134e5f050521373dbd", size = 2102870 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/dc/6d709bcf8fed7611d8a510aeed23b0436cf6db5b61e63c8eb8451eb0d4d8/distributed-2026.1.1-py3-none-any.whl", hash = "sha256:506759b1ed88e45e12ba65e2a429de9911862db55d27dd8bb293c6268430374e", size = 1008417 }, -] - -[[package]] -name = "docrep" -version = "0.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/8e/250fab1cafeea43f4eb11f1d64cd6313f639965ff62cb0d9da3883655781/docrep-0.3.2.tar.gz", hash = "sha256:ed8a17e201abd829ef8da78a0b6f4d51fb99a4cbd0554adbed3309297f964314", size = 33197 } - -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, -] - -[[package]] -name = "donfig" -version = "0.8.1.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/25/71/80cc718ff6d7abfbabacb1f57aaa42e9c1552bfdd01e64ddd704e4a03638/donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52", size = 19506 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl", hash = "sha256:2a3175ce74a06109ff9307d90a230f81215cbac9a751f4d1c6194644b8204f9d", size = 21592 }, -] - -[[package]] -name = "execnet" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708 }, -] - -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, -] - -[[package]] -name = "fast-array-utils" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "numpy", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1e/ff/93edc904b05a3260d5a690ac6dcdcd3cce10065b6fb56cdc683f80969456/fast_array_utils-1.3.1.tar.gz", hash = "sha256:34d175a63e9208c6fcbcb3cc18f75480ecdeaeed248759da0e74ab8fbcf55808", size = 330367 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/cb/ddcf4ad63ba88df95025837e35faf6ea6862bab1327f836801ba02140a22/fast_array_utils-1.3.1-py3-none-any.whl", hash = "sha256:7617322b29c9f3a8e4c046355ecf653bbee581245787243ea06212a1a56fa611", size = 36518 }, -] - -[[package]] -name = "fast-array-utils" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "numpy", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/86/7345025275bd19b1303bb6267133982f4e95309f85a39529036566cf3b29/fast_array_utils-1.4.1.tar.gz", hash = "sha256:466512aa0e19ebfb2f8d7ae8736fc81b5724e9bf1a7958474206c17b8e991e9c", size = 336422 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/28/aad6d6b63085766c994cf50f2e1f7bd514bd20eb149ad15477db8339420a/fast_array_utils-1.4.1-py3-none-any.whl", hash = "sha256:cad55a58359cac4084445d801b50904357093a94863aca7ff7118bd4bd38ce7c", size = 39859 }, -] - -[package.optional-dependencies] -accel = [ - { name = "numba", marker = "python_full_version >= '3.12'" }, -] -sparse = [ - { name = "scipy", marker = "python_full_version >= '3.12'" }, -] - -[[package]] -name = "fastjsonschema" -version = "2.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, -] - -[[package]] -name = "filelock" -version = "3.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812 }, -] - -[[package]] -name = "fonttools" -version = "4.63.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793 }, - { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130 }, - { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952 }, - { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308 }, - { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932 }, - { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271 }, - { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473 }, - { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389 }, - { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131 }, - { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704 }, - { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298 }, - { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800 }, - { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666 }, - { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598 }, - { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575 }, - { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211 }, - { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062 }, - { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594 }, - { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840 }, - { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801 }, - { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009 }, - { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892 }, - { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313 }, - { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299 }, - { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338 }, - { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661 }, - { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526 }, - { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946 }, - { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489 }, - { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870 }, - { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026 }, - { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454 }, - { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152 }, - { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809 }, - { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649 }, - { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147 }, - { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237 }, - { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933 }, - { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326 }, - { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829 }, - { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562 }, -] - -[[package]] -name = "fqdn" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912 }, - { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046 }, - { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119 }, - { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067 }, - { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160 }, - { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544 }, - { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797 }, - { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923 }, - { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886 }, - { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731 }, - { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544 }, - { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806 }, - { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382 }, - { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647 }, - { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064 }, - { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937 }, - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782 }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594 }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448 }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411 }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014 }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909 }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049 }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485 }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619 }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320 }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820 }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518 }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096 }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985 }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591 }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102 }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717 }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651 }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417 }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391 }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048 }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549 }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833 }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363 }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314 }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365 }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763 }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110 }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717 }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235 }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742 }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725 }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506 }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161 }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676 }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638 }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067 }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101 }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901 }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395 }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659 }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127 }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698 }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749 }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298 }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015 }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038 }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130 }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845 }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131 }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542 }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308 }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210 }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972 }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536 }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330 }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627 }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238 }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738 }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739 }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186 }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196 }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830 }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289 }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318 }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814 }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762 }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470 }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042 }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148 }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676 }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451 }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507 }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, -] - -[[package]] -name = "fsspec" -version = "2026.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d5/8d/1c51c094345df128ca4a990d633fe1a0ff28726c9e6b3c41ba65087bba1d/fsspec-2026.4.0.tar.gz", hash = "sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4", size = 312760 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402 }, -] - -[package.optional-dependencies] -http = [ - { name = "aiohttp" }, -] -s3 = [ - { name = "s3fs" }, -] - -[[package]] -name = "geopandas" -version = "1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "pyogrio" }, - { name = "pyproj" }, - { name = "shapely" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/ba/8e6b2091878e99e86a36a814dcaeff652ed48bdb03d53e78e15aaa63a914/geopandas-1.1.3.tar.gz", hash = "sha256:91a31989b6f566012838d21d5f8033f37dce882079ccb7cfdc40d5ccce7f284f", size = 336718 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/78/6a04792ace63a93e162f1305392d500ae8ddcb620e7eb88a22fd622b35bb/geopandas-1.1.3-py3-none-any.whl", hash = "sha256:90d62a64f95eaa3be2ccc115c5f3d6e24208bb11983b390fdc0621a3eccd0230", size = 342514 }, -] - -[[package]] -name = "google-crc32c" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/ef/21ccfaab3d5078d41efe8612e0ed0bfc9ce22475de074162a91a25f7980d/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8", size = 31298 }, - { url = "https://files.pythonhosted.org/packages/c5/b8/f8413d3f4b676136e965e764ceedec904fe38ae8de0cdc52a12d8eb1096e/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7", size = 30872 }, - { url = "https://files.pythonhosted.org/packages/f6/fd/33aa4ec62b290477181c55bb1c9302c9698c58c0ce9a6ab4874abc8b0d60/google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15", size = 33243 }, - { url = "https://files.pythonhosted.org/packages/71/03/4820b3bd99c9653d1a5210cb32f9ba4da9681619b4d35b6a052432df4773/google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a", size = 33608 }, - { url = "https://files.pythonhosted.org/packages/7c/43/acf61476a11437bf9733fb2f70599b1ced11ec7ed9ea760fdd9a77d0c619/google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2", size = 34439 }, - { url = "https://files.pythonhosted.org/packages/e9/5f/7307325b1198b59324c0fa9807cafb551afb65e831699f2ce211ad5c8240/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113", size = 31300 }, - { url = "https://files.pythonhosted.org/packages/21/8e/58c0d5d86e2220e6a37befe7e6a94dd2f6006044b1a33edf1ff6d9f7e319/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb", size = 30867 }, - { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364 }, - { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740 }, - { url = "https://files.pythonhosted.org/packages/df/c0/87c2073e0c72515bb8733d4eef7b21548e8d189f094b5dad20b0ecaf64f6/google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962", size = 34437 }, - { url = "https://files.pythonhosted.org/packages/d1/db/000f15b41724589b0e7bc24bc7a8967898d8d3bc8caf64c513d91ef1f6c0/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b", size = 31297 }, - { url = "https://files.pythonhosted.org/packages/d7/0d/8ebed0c39c53a7e838e2a486da8abb0e52de135f1b376ae2f0b160eb4c1a/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27", size = 30867 }, - { url = "https://files.pythonhosted.org/packages/ce/42/b468aec74a0354b34c8cbf748db20d6e350a68a2b0912e128cabee49806c/google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa", size = 33344 }, - { url = "https://files.pythonhosted.org/packages/1c/e8/b33784d6fc77fb5062a8a7854e43e1e618b87d5ddf610a88025e4de6226e/google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8", size = 33694 }, - { url = "https://files.pythonhosted.org/packages/92/b1/d3cbd4d988afb3d8e4db94ca953df429ed6db7282ed0e700d25e6c7bfc8d/google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f", size = 34435 }, - { url = "https://files.pythonhosted.org/packages/21/88/8ecf3c2b864a490b9e7010c84fd203ec8cf3b280651106a3a74dd1b0ca72/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697", size = 31301 }, - { url = "https://files.pythonhosted.org/packages/36/c6/f7ff6c11f5ca215d9f43d3629163727a272eabc356e5c9b2853df2bfe965/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651", size = 30868 }, - { url = "https://files.pythonhosted.org/packages/56/15/c25671c7aad70f8179d858c55a6ae8404902abe0cdcf32a29d581792b491/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2", size = 33381 }, - { url = "https://files.pythonhosted.org/packages/42/fa/f50f51260d7b0ef5d4898af122d8a7ec5a84e2984f676f746445f783705f/google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21", size = 33734 }, - { url = "https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2", size = 34878 }, - { url = "https://files.pythonhosted.org/packages/52/c5/c171e4d8c44fec1422d801a6d2e5d7ddabd733eeda505c79730ee9607f07/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93", size = 28615 }, - { url = "https://files.pythonhosted.org/packages/9c/97/7d75fe37a7a6ed171a2cf17117177e7aab7e6e0d115858741b41e9dd4254/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c", size = 28800 }, -] - -[[package]] -name = "greenlet" -version = "3.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/3c/ff890b466eaba2b0f5e6bdfff025f8c75f41b8ffdc3dbc3d24ad261e764a/greenlet-3.5.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:73f78f9b9f0a5c06e5c946ba1e8e36f5114923b6be109ee618c54f079c3ea14f", size = 284764 }, - { url = "https://files.pythonhosted.org/packages/81/0e/5e5457be3d256918f6a4756f073548a3f0190836e2cc94aa6d0d617a940b/greenlet-3.5.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0cbed8bb44e23c5b199f888f4e4ce096b45ad9f25ff74a7ad0213875e936bb2", size = 603479 }, - { url = "https://files.pythonhosted.org/packages/6d/e1/f89a21d58d308298e6f275f13a1b472ed96c680b601a371b08be6a725989/greenlet-3.5.1-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a203a8bd0acb0701653d3bbb26e404854a68674139ed5cbb778830f42b09bb33", size = 615495 }, - { url = "https://files.pythonhosted.org/packages/2c/f2/8fd452fd81adb9ec79c8275c1375702ab0fd6bee4952da12eaa09b9508d8/greenlet-3.5.1-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ebeb75c81211f5c702576cf81f315e77e23cfdb2c7c6fcb9dd143e6de35c360", size = 623515 }, - { url = "https://files.pythonhosted.org/packages/75/de/af6cef182862d2ccd6975440d21c9058a77c3f9b469abf94e322dfd2e0e3/greenlet-3.5.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a271fcd66c74615cda6a964fda3f304267a12e50a084472218a39bb0376f563", size = 614754 }, - { url = "https://files.pythonhosted.org/packages/ec/bc/c318aa9f3ffc77320fddcee3d892be957b42e2ff947198d9450b004f3a38/greenlet-3.5.1-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:017a544f0385d441e88714160d089d6900ef46c9eff9d99b6715a5ef2d127747", size = 418439 }, - { url = "https://files.pythonhosted.org/packages/1a/c6/50e520283a9f19388a7326b05f9e8637e566003475eacaadad04f558c68d/greenlet-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ded7b068c7c31c1a8657d4fd42d886b3e051ae29f88b80c5ff9d502257b0f071", size = 1574097 }, - { url = "https://files.pythonhosted.org/packages/21/1c/13abd1f4860d987fa5e1170a01930d6e6cd40d328de487a3c9fdaff0ffd0/greenlet-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0932b81d72f552ded9d810d00021b64d89f2195a91ce115b893f943b7a4ab3c", size = 1641058 }, - { url = "https://files.pythonhosted.org/packages/f5/56/5f332b7705545eac2dc01b4e9254d24a793f2656d55d5cc6b94ee59d22ae/greenlet-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:88e300d136eac057b2397aa1cfd7328b4c87c7eb66a09c7bc6a1292234db474e", size = 238089 }, - { url = "https://files.pythonhosted.org/packages/d9/a9/a3c2fa886c5b94863fb0e61b3bc14610b7aa94cf4f17f8741b11708305fc/greenlet-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:cc6ab7e555c8a112ad3a76e368e86e12a2754bcae1652a5602e133ec7b635523", size = 234989 }, - { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220 }, - { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585 }, - { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215 }, - { url = "https://files.pythonhosted.org/packages/7c/6c/de5b1b388cd2d9fbdfeab324863daba37d54e6e233ddbefd70b385a8c591/greenlet-3.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89101bfd5011e069be974903cb3a4e4523845e4ece2d62dcd8d358933c0ef249", size = 620094 }, - { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358 }, - { url = "https://files.pythonhosted.org/packages/4a/43/1204baffab8a6476464795a7ccf394a3248d4f22c9f87173a15b36b6d971/greenlet-3.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e6cd99ea59dd5d89f0c956606571d79bfe6f68c9eb7f4a4083a41a7f1587edee", size = 422782 }, - { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475 }, - { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625 }, - { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791 }, - { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580 }, - { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060 }, - { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370 }, - { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987 }, - { url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911 }, - { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911 }, - { url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228 }, - { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689 }, - { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602 }, - { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683 }, - { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499 }, - { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579 }, - { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106 }, - { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478 }, - { url = "https://files.pythonhosted.org/packages/c6/2d/2d80842910da44f78c286532d084b8a5c3717c844ae80ceb3858738ae89a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c09df69dc1712d131332054a858a3e5cca400967fa3a672e2324fbb0971448c", size = 667767 }, - { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800 }, - { url = "https://files.pythonhosted.org/packages/e9/d3/dad2eecedfbb1ed7050a20dcfae40c1442b74bc7423608be2c7e03ee7133/greenlet-3.5.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:a4764e0bfc6a4d114c865b32520805c16a990ef5f286a514413b05d5ecd6a23d", size = 470786 }, - { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518 }, - { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593 }, - { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800 }, - { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862 }, - { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637 }, - { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840 }, - { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416 }, - { url = "https://files.pythonhosted.org/packages/8c/46/5987dcd1a2570ba84f3b187536b2ca3ae97613387e57f5cfa99df068fe5e/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea37d5a157eb9493820d3792ac4ece28619a394391d2b9f2f78057d396ff0f0f", size = 656607 }, - { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397 }, - { url = "https://files.pythonhosted.org/packages/2c/c1/6da0a9ddcc29d7e51ef14883fa3dc1e53b3f4ffba00582106c7bf55da1d8/greenlet-3.5.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:8d8a23250ea3ec7b36de8fa4b541e9e2db3ee82915cc060ab0631609ad8b28de", size = 488287 }, - { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469 }, - { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115 }, - { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246 }, - { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575 }, - { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428 }, - { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064 }, - { url = "https://files.pythonhosted.org/packages/dc/74/807a047255bf1e09303627c46dc043dca596b6958a354d904f32ab382005/greenlet-3.5.1-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:10a9a1c0bfbc93d41156ffcb90c75fbc05544054faf15dcc1fdf9765f8b607f0", size = 672962 }, - { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697 }, - { url = "https://files.pythonhosted.org/packages/76/32/19d4e13225193c29b13e308015223f7d75fd3d8623d49dd19040d2ce8ec1/greenlet-3.5.1-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:ef08c1567c78074b22d1a200183d52d04a14df447bf70bcbb6a3507a48e776fc", size = 476047 }, - { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256 }, - { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956 }, - { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802 }, - { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853 }, - { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877 }, - { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333 }, - { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443 }, - { url = "https://files.pythonhosted.org/packages/c9/9d/1dcdf7b95ab3cf8c7b6d7277c18a5e167312f2b362ddfcc5d5e6d8d84b43/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a57b0d05a0448eed231d59c0ceb287dde984551e54cbc51ac2d4865712838e9c", size = 659998 }, - { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356 }, - { url = "https://files.pythonhosted.org/packages/05/7e/c4959664fc231d587d66d8e81f2095e98056ba1954beafdcbe635e251052/greenlet-3.5.1-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:b0703c2cef53e01baec47f7a3868009913ad71ec678bbecb42a6f40895e4ce62", size = 494470 }, - { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216 }, - { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427 }, - { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225 }, - { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590 }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, -] - -[[package]] -name = "h5py" -version = "3.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663 }, - { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630 }, - { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472 }, - { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150 }, - { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544 }, - { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013 }, - { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673 }, - { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834 }, - { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604 }, - { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940 }, - { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852 }, - { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250 }, - { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108 }, - { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216 }, - { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868 }, - { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286 }, - { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808 }, - { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837 }, - { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860 }, - { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417 }, - { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214 }, - { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598 }, - { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509 }, - { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362 }, - { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608 }, - { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773 }, - { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886 }, - { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883 }, - { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039 }, - { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526 }, - { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263 }, - { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450 }, - { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693 }, - { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305 }, - { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061 }, - { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216 }, - { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068 }, - { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253 }, - { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671 }, - { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706 }, -] - -[[package]] -name = "hatch" -version = "1.16.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-zstd", marker = "python_full_version < '3.14'" }, - { name = "click" }, - { name = "hatchling" }, - { name = "httpx" }, - { name = "hyperlink" }, - { name = "keyring" }, - { name = "packaging" }, - { name = "pexpect" }, - { name = "platformdirs" }, - { name = "pyproject-hooks" }, - { name = "python-discovery" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "tomli-w" }, - { name = "tomlkit" }, - { name = "userpath" }, - { name = "uv" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/02/ce9c4c439fa3f195b21b4b5bb18b44d1076297c86477ef7e3d2de6064ec3/hatch-1.16.5.tar.gz", hash = "sha256:57bdeeaa72577859ce37091a5449583875331c06f9cb6af9077947ad40b3a1de", size = 5220741 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/8a/11ae7e271870f0ad8fa0012e4265982bebe0fdc21766b161fb8b8fc3aefc/hatch-1.16.5-py3-none-any.whl", hash = "sha256:d9b8047f2cd10d3349eb6e8f278ad728a04f91495aace305c257d5c2747188fb", size = 141269 }, -] - -[[package]] -name = "hatchling" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "pathspec" }, - { name = "pluggy" }, - { name = "trove-classifiers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/b4cfe330cd4f49cff17fd771154730555fa4123beb7f292cf0098b4e6c20/hatchling-1.29.0.tar.gz", hash = "sha256:793c31816d952cee405b83488ce001c719f325d9cda69f1fc4cd750527640ea6", size = 55656 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/8a/44032265776062a89171285ede55a0bdaadc8ac00f27f0512a71a9e3e1c8/hatchling-1.29.0-py3-none-any.whl", hash = "sha256:50af9343281f34785fab12da82e445ed987a6efb34fd8c2fc0f6e6630dbcc1b0", size = 76356 }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, -] - -[[package]] -name = "hyperlink" -version = "21.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/51/1947bd81d75af87e3bb9e34593a4cf118115a8feb451ce7a69044ef1412e/hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b", size = 140743 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/aa/8caf6a0a3e62863cbb9dab27135660acba46903b703e224f14f447e57934/hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4", size = 74638 }, -] - -[[package]] -name = "identify" -version = "2.6.19" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397 }, -] - -[[package]] -name = "idna" -version = "3.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316 }, -] - -[[package]] -name = "igraph" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "texttable" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/be/56bef1919005b4caf1f71522b300d359f7faeb7ae93a3b0baa9b4f146a87/igraph-1.0.0.tar.gz", hash = "sha256:2414d0be2e4d77ee5357807d100974b40f6082bb1bb71988ec46cfb6728651ee", size = 5077105 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/03/3278ad0ceb3ea0e84d8ae3a85bdded4d0e57853aeb802a200feb43847b93/igraph-1.0.0-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:c2cbc415e02523e5a241eecee82319080bf928a70b1ba299f3b3e25bf029b6d4", size = 2257415 }, - { url = "https://files.pythonhosted.org/packages/0d/bc/6281ec7f9baaf71ee57c3b1748da2d3148d15d253e1a03006f204aa68ca5/igraph-1.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a27753cd80680a8f676c2d5a467aaa4a95e510b30748398ec4e4aeb982130e8", size = 2048555 }, - { url = "https://files.pythonhosted.org/packages/2a/38/3cd6428a4ed4c09a56df05998438e7774fd1d799ee4fb8fc481674f5f7fc/igraph-1.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a55dc3a2a4e3fc3eba42479910c1511bfc3ecb33cdf5f0406891fd85f14b5aee", size = 5314141 }, - { url = "https://files.pythonhosted.org/packages/7d/da/dd2867c25adbb41563720f14b5fc895c98bf88be682a3faff4f7b3118d2a/igraph-1.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d04c2c76f686fb1f554ee35dfd3085f5e73b7965ba6b4cf06d53e66b1955522", size = 5683134 }, - { url = "https://files.pythonhosted.org/packages/e5/40/243c118d34ab80382d7009c4dcb99b887384c3d2ce84d29eeac19e2a007a/igraph-1.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2b52dc1757fff0fed29a9f7a276d971a11db4211569ed78b9eab36288dfcc9d", size = 6211583 }, - { url = "https://files.pythonhosted.org/packages/1d/b7/88f433819c54b496cb0315fce28e658970cb20ff5dbd52a5a605ce2888de/igraph-1.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:05c79a2a8fca695b2f217a6fa7f2549f896f757d4db41be32a055400cb19cc30", size = 6594509 }, - { url = "https://files.pythonhosted.org/packages/7b/5d/8f7f6f619d374e959aa3664ebc4b24c10abc90c2e8efbed97f2623fadaf5/igraph-1.0.0-cp39-abi3-win32.whl", hash = "sha256:c2bce3cd472fec3dd9c4d8a3ea5b6b9be65fb30edf760beb4850760dd4f2d479", size = 2725406 }, - { url = "https://files.pythonhosted.org/packages/af/77/a85b3745cf40a0572bae2de8cd9c2a2a8af78e5cf3e880fc0a249114e609/igraph-1.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:faeff8ede0cf15eb4ded44b0fcea6e1886740146e60504c24ad2da14e0939563", size = 3221663 }, - { url = "https://files.pythonhosted.org/packages/ef/7e/5df541c37bdf6493035e89c22bd53f30d99b291bcda6c78e9a8afeecec2b/igraph-1.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:b607cafc24b10a615e713ee96e58208ef27e0764af80140c7cc45d4724a3f2df", size = 2785701 }, - { url = "https://files.pythonhosted.org/packages/b9/73/bf1d4dbbc9123435b3ca14bb608b243a50a4f158ecea564bf196715248d9/igraph-1.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3189c1a8e8a8f58009f3f729040eb3701254d074ed37245691d529869ec940c5", size = 2246636 }, - { url = "https://files.pythonhosted.org/packages/59/ac/28482f2af45cc0a0ca88a69d17a6ea694f58bdbd22cc876e7273a0379282/igraph-1.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ebe9502689b946301584b3cfacdbc70c58c4d664d804e39b6daa31be5c20bf46", size = 2036101 }, - { url = "https://files.pythonhosted.org/packages/56/80/806a093df1d1ddc3b30d0418b1ee56388ae7018f8ae288677ee2b3a1abaf/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f117683108c54330d6dc67a708e3724c13c9989885122a29781296872989a222", size = 3053403 }, - { url = "https://files.pythonhosted.org/packages/56/bf/cf7aeff230a4368c0b8bc6b02f3ea27db41db33714b51e1e8a7c1458f31b/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:077dbff0edb8b4ce0f9fefdf325200346d9d5db02de31872b41743de08e67a16", size = 3262472 }, - { url = "https://files.pythonhosted.org/packages/d8/ca/dbc06072d5eea402a6dc81f387afb1b7e0c415f1d8a75232943fc4d1bfdb/igraph-1.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fe7c693b2a84a4e03ca31e65aa05a2ecd8728137fa9909ccbf6453b4200b856d", size = 3218861 }, -] - -[[package]] -name = "imagecodecs" -version = "2025.11.11" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/7f/5842fd3e5bb8195d3b3af12af82a5c1d3280611cb01ffd02edec57e55b9e/imagecodecs-2025.11.11.tar.gz", hash = "sha256:a836d329f91c890d17b9dc3f3a2bba944a198ae741592c3cb0c4b9b097d10d34", size = 9505589 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/61/4017d04559736e903cb972bfaac0dc464b2d7bd945d9070cfa2526b7596e/imagecodecs-2025.11.11-cp311-abi3-macosx_10_15_x86_64.whl", hash = "sha256:f5d01cc85ca2fe354302b3650f4848e8f1d3174200277b7cb2624abed4d02ea5", size = 12148485 }, - { url = "https://files.pythonhosted.org/packages/8f/27/0494b31c65a4dbb09206962f7a056ff9df5388ab13e1dddc27ecf47238df/imagecodecs-2025.11.11-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:45a865690275a3a8893c4c54377c678a7b361ebe13dd3a9e22c66443a96b1c2a", size = 10151325 }, - { url = "https://files.pythonhosted.org/packages/ca/5e/d98ce5608a48efff8f07139b11e4e76f7a05471dc327fb28e696afa7504c/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be6e2f4b6601b57cfc9fba2382da7e02ad43c44e3b037adbf275a11f688b231f", size = 22366018 }, - { url = "https://files.pythonhosted.org/packages/a8/ac/fde1f9f99ebe7d8b989bb0005883b69f35abd6ac2d677e0a0caf23f29961/imagecodecs-2025.11.11-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4a3894b62ca6ad0c4d75cb0a0aac2a525c4a5c4a2a60defc5d7fb716a4bb355", size = 23200222 }, - { url = "https://files.pythonhosted.org/packages/30/fe/ea18b0514128a60830c4909e23add385f8e799ff9719bc0b5de82673baa1/imagecodecs-2025.11.11-cp311-abi3-win32.whl", hash = "sha256:3735ea088321b0685e593c5d44fc1d146a84fcf6645c52ed136fa576e16c3572", size = 14847479 }, - { url = "https://files.pythonhosted.org/packages/01/b8/287a2d64f746e3bdf44693d2013b5fb7440eafe1dca08a35a0fd30458d33/imagecodecs-2025.11.11-cp311-abi3-win_amd64.whl", hash = "sha256:4252b15f5ba55bc7d7aacf621f6bb6e73fd70e66d56277a1f26563198b2cd576", size = 19052271 }, - { url = "https://files.pythonhosted.org/packages/a3/c8/413154b6014d9e1ee2b717bfdfa6617e81557b44487e2fb2a88ca3da125b/imagecodecs-2025.11.11-cp311-abi3-win_arm64.whl", hash = "sha256:f4480973fcf2d826ba6806b7ac6f4d9bd72eeb557cfd3687ac885939d130ec47", size = 14529653 }, - { url = "https://files.pythonhosted.org/packages/8c/2e/6a7117f2fe8e83c6ed58ad237625e4e82e139f67ce4695c457789e43cdc5/imagecodecs-2025.11.11-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bb0f4714e60dd94b94ca778b872fb90b885b83381228743903035d79b7e91a88", size = 12448096 }, - { url = "https://files.pythonhosted.org/packages/24/96/0c479746198723c53326c65edf56331d8e910afaa2fa132358ca8d5a6177/imagecodecs-2025.11.11-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:faf4ab8f2f11694aa2d6ed7548ce1d1598a14e326a6bb3d50669ed5c7da764c6", size = 10433730 }, - { url = "https://files.pythonhosted.org/packages/b4/19/de48587d37aa2bc355313abdfdda801c7a26235ec66bfad19ff3ee0b7b23/imagecodecs-2025.11.11-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdb475f54d0abac3cfcde42ec47f606eea1d4afa52173420577317102d361ae5", size = 25835500 }, - { url = "https://files.pythonhosted.org/packages/17/47/26edea4799c1250ad34305544fb69a65d6151dbe623508ed71537a678e7e/imagecodecs-2025.11.11-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e3d4cb2dc507c0bab057321333927575d1d611efe368e76b752f5ad3c55856ad", size = 25952929 }, - { url = "https://files.pythonhosted.org/packages/31/75/d720db7e0d654d3fd0db253ed8b8a83fad6d87735132fb01326c4f720fd7/imagecodecs-2025.11.11-cp314-cp314t-win32.whl", hash = "sha256:7bcc214d6f6ecd2bd7b648a6ac5589edcde946bcb738b6c763ce1c864934c58c", size = 15467675 }, - { url = "https://files.pythonhosted.org/packages/c0/fd/1a1eb89f9d1f9256d0fbb9f328607a1215710e19d4086bc602e8543423e4/imagecodecs-2025.11.11-cp314-cp314t-win_amd64.whl", hash = "sha256:58dbc0095142dcef55aaac392ee5bd8fb7c3656753e3c37424c3612209c656a4", size = 19915854 }, - { url = "https://files.pythonhosted.org/packages/59/27/2fa20c89149eed89949c34654bb3643f2aa229492e05fbd2ffadb5e4dc41/imagecodecs-2025.11.11-cp314-cp314t-win_arm64.whl", hash = "sha256:dd61871455ae6c3486051e68e0d576d54e53c45d157ce233c4a09269f132bf48", size = 15098090 }, -] - -[[package]] -name = "imageio" -version = "2.37.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "pillow" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646 }, -] - -[[package]] -name = "imagesize" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441 }, -] - -[[package]] -name = "importlib-metadata" -version = "9.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789 }, -] - -[[package]] -name = "inflect" -version = "7.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, - { name = "typeguard" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/c6/943357d44a21fd995723d07ccaddd78023eace03c1846049a2645d4324a3/inflect-7.5.0.tar.gz", hash = "sha256:faf19801c3742ed5a05a8ce388e0d8fe1a07f8d095c82201eb904f5d27ad571f", size = 73751 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197 }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, -] - -[[package]] -name = "ipykernel" -version = "7.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "comm" }, - { name = "debugpy" }, - { name = "ipython" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "matplotlib-inline" }, - { name = "nest-asyncio" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788 }, -] - -[[package]] -name = "ipython" -version = "9.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "psutil" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274 }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, -] - -[[package]] -name = "ipywidgets" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "comm" }, - { name = "ipython" }, - { name = "jupyterlab-widgets" }, - { name = "traitlets" }, - { name = "widgetsnbextension" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808 }, -] - -[[package]] -name = "isoduration" -version = "20.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "arrow" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777 }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871 }, -] - -[[package]] -name = "jaraco-functools" -version = "4.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/9a/982e48afcffcd727a9144506720ffd4224b6b7e355c98641866f38b7c043/jaraco_functools-4.5.0-py3-none-any.whl", hash = "sha256:79ce39246eddbde4b3a03b77ea5f0f7878dc669b166a66cf3fa8e266aa3fa2f4", size = 10594 }, -] - -[[package]] -name = "jax" -version = "0.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaxlib" }, - { name = "ml-dtypes" }, - { name = "numpy" }, - { name = "opt-einsum" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/24/49/b082387119c4a6bc7596296bbdc6bce034628cdd2845ebb27304cbca3624/jax-0.10.1.tar.gz", hash = "sha256:11672410faf8752429eb9a131de203dc488a2a3a012d509baa2b39878008810d", size = 2718178 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/6e/5087e0347188f6970aba1ffbd0018754d23c3f3461e9f21785f2f27a02c2/jax-0.10.1-py3-none-any.whl", hash = "sha256:47f3192c76e9e3358de1b106a8af5e943fccb10510903f25d96ea53652729134", size = 3150973 }, -] - -[[package]] -name = "jaxlib" -version = "0.10.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ml-dtypes" }, - { name = "numpy" }, - { name = "scipy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/e9/43253041a0b28f64a99dfa8a6823de415b1b9723dc8eb8718945bb957b1c/jaxlib-0.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff0c82cb958d180d26687459962e262b6e1c90f239903c8253cc190067124db4", size = 60802893 }, - { url = "https://files.pythonhosted.org/packages/f2/dd/fe5e06087d79ef014a10fc4d5acba76f585278eb54cdbeb880347600a41c/jaxlib-0.10.1-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:349b131c8b393fe5de2aabec1539f18c6a6ce85680b145c78b068fc56d498472", size = 80280956 }, - { url = "https://files.pythonhosted.org/packages/33/89/8c6dff28eaf86a917622e427caf045ecb85772eaa1679937915e4e5ac8d6/jaxlib-0.10.1-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9c2f0d6b910e93dd47bbfc3aa54c83950ff6a05955913892d31b034337d89420", size = 85810989 }, - { url = "https://files.pythonhosted.org/packages/83/fd/6b705478e19af3dd384351c91f4b469fef83bb1fa070c45a937ae7dcc335/jaxlib-0.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:f49375e63c4486295e31d3f93b4abeb0a69183cb78484299b135c74bc036ec4c", size = 64802918 }, - { url = "https://files.pythonhosted.org/packages/53/b4/fdb6e989b142d8a8d2093f342cbc5323fe0d4a7217fd899c8ddf9e108a5a/jaxlib-0.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7a4399c7429c87ee6f7ab1e5712c5548ecfabde974ee9f4a12957fea4e35efb8", size = 60816137 }, - { url = "https://files.pythonhosted.org/packages/a7/29/0b4eaaca005708751ff301903a2ca760dcc34175dadb7536498c57a7de85/jaxlib-0.10.1-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:12126603ba472300c62480f86d20972d563143041039d9dea349ad510aa6123c", size = 80294034 }, - { url = "https://files.pythonhosted.org/packages/38/69/2912ab63036e21c72748019e1d8e09e8a1fc3368b3e83fc27898a1858575/jaxlib-0.10.1-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:f3cdf5b7f48470ab5455ab79aab746419694ccb6b52651cc2ce5fb27def03588", size = 85828774 }, - { url = "https://files.pythonhosted.org/packages/ec/8f/993ea419eca6f34fe12613e22a03b93f40e5b1e8e0df18d4060e1313a1fc/jaxlib-0.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:0acf3f8e7dca9074c0327f0f61502845792ca9f82fab23b841b00daa78e85488", size = 64830187 }, - { url = "https://files.pythonhosted.org/packages/cf/76/3b637d4def229015a3035a7b44fac0dcf2536ae337540cdbffc651334d4e/jaxlib-0.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4167213aa00f14bb0d8fbd90f9ded75e976f71ce8baf8c3c44e04c8fb80ea0c1", size = 60815855 }, - { url = "https://files.pythonhosted.org/packages/5b/76/9a1971bc9edb8728a7ba86d2693127ee46add9811230b8452321415fd4e9/jaxlib-0.10.1-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:e53223d8f6861d33c02dd02343fa464700401ff5363784d37c33407218e328b8", size = 80293947 }, - { url = "https://files.pythonhosted.org/packages/20/1d/69a0ba52fb546261e71a7209378ee6059950e9c088a2a18355e01509f474/jaxlib-0.10.1-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:bb073a1224e659e01e8d32d47c000edb52ec2aa8ba97ec22b2228b3a46e5c167", size = 85829861 }, - { url = "https://files.pythonhosted.org/packages/a9/df/48659e2ee57705c63a51525f810fe3e0c87af4ca9f89d4738281a872d58e/jaxlib-0.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:6449f1d4a22324f5f02c843360475783f9fd1d353fe711806cbf4e927d1360ae", size = 64828863 }, - { url = "https://files.pythonhosted.org/packages/c7/34/3f7c95ee1b2555d611f836988a49b522c04b8d186e0528f91d45118089bf/jaxlib-0.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:72a7db1242d6b7773340e430c244e73a8d13f82ce83933c0529a8b4b1520dcf3", size = 60934063 }, - { url = "https://files.pythonhosted.org/packages/3a/84/855a2395d299e00e1d9ffd0aecd516b52b81780f3e4ef537527be6d8c1fc/jaxlib-0.10.1-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:649c26ca92e9bbffb3c35226b37893916f20e6b89fb3911ce79b39e5cfb27b46", size = 80402500 }, - { url = "https://files.pythonhosted.org/packages/be/35/153e91a9c770a981d525d845b3f4cdb71a1e119681594a33908e9536bdff/jaxlib-0.10.1-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:cfe75d8a17e0d33a7bed27f32d7a5344a66e8d4af7073973f396e14ff4a9c503", size = 85941210 }, - { url = "https://files.pythonhosted.org/packages/0b/a1/c4d4c0530313c50dd1ba07fff480cfd0c5f18c5ec49742f4a52a6edfd95f/jaxlib-0.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9a657554dbe56f3691d377cb99d00b29df14e5638e579e57965f9f69c32c9315", size = 60826189 }, - { url = "https://files.pythonhosted.org/packages/98/71/529b2439b88491e0806ee9fa6191ecf90f4447dfe092e80bd19577c85260/jaxlib-0.10.1-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:93cd9989404f86a21b50c7ff4e850a31f55509664312080978bde97a664d92a9", size = 80300654 }, - { url = "https://files.pythonhosted.org/packages/be/08/0bc4132fb2fe224ee9d83fd60e3650fd4d893b4e5148707a98df8f0333a4/jaxlib-0.10.1-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:26b94e9640b01968cc14b8353dd6b6540d723f30579c78b1f46a477fc4aa196d", size = 85841241 }, - { url = "https://files.pythonhosted.org/packages/5c/de/423d748ce3367bd5ea20d8cc34a7ceb6420da4d41c20b247f54194700d04/jaxlib-0.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:375820799bbf7d515dd4e4d40f3334566b73d3fe64d340afbd6aa897d5d7c486", size = 67301520 }, - { url = "https://files.pythonhosted.org/packages/2d/bf/3f7ce089d62f7ac85ea678925471f7ec88038899e67ab02079c1e7d8ad4e/jaxlib-0.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bc52f16bdd61b299efeed7ea8e743e91a118059a463da2ab97196fc05b69ddb7", size = 60935393 }, - { url = "https://files.pythonhosted.org/packages/65/6a/38cf1d4ff8c8f74ec8d567e0aa6e3d2082ab6fc580545f6bb51368da20a9/jaxlib-0.10.1-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:55b0a473fbd57d31dc3935c4cd5c0c38af5f7c1f41300df27923cf46676972ca", size = 80405741 }, - { url = "https://files.pythonhosted.org/packages/16/9e/d3cff171aaf13a09aab26a44d1a27dcbf0d6311e4d855f5d99685965ace3/jaxlib-0.10.1-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:4b0cb8ef960a3723037db63f28ffa20083d90fff6d30085e99d7c63cfa08e4c0", size = 85942183 }, -] - -[[package]] -name = "jedi" -version = "0.20.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812 }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010 }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, -] - -[[package]] -name = "jmespath" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419 }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071 }, -] - -[[package]] -name = "json5" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271 }, -] - -[[package]] -name = "jsonpointer" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659 }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, -] - -[package.optional-dependencies] -format-nongpl = [ - { name = "fqdn" }, - { name = "idna" }, - { name = "isoduration" }, - { name = "jsonpointer" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "rfc3987-syntax" }, - { name = "uri-template" }, - { name = "webcolors" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, -] - -[[package]] -name = "jupyter-cache" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "click" }, - { name = "importlib-metadata" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "pyyaml" }, - { name = "sqlalchemy" }, - { name = "tabulate" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/f7/3627358075f183956e8c4974603232b03afd4ddc7baf72c2bc9fff522291/jupyter_cache-1.0.1.tar.gz", hash = "sha256:16e808eb19e3fb67a223db906e131ea6e01f03aa27f49a7214ce6a5fec186fb9", size = 32048 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/6b/67b87da9d36bff9df7d0efbd1a325fa372a43be7158effaf43ed7b22341d/jupyter_cache-1.0.1-py3-none-any.whl", hash = "sha256:9c3cafd825ba7da8b5830485343091143dff903e4d8c69db9349b728b140abf6", size = 33907 }, -] - -[[package]] -name = "jupyter-client" -version = "8.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371 }, -] - -[[package]] -name = "jupyter-core" -version = "5.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, -] - -[[package]] -name = "jupyter-events" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema", extra = ["format-nongpl"] }, - { name = "packaging" }, - { name = "python-json-logger" }, - { name = "pyyaml" }, - { name = "referencing" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/6c/6fcde0c8f616ed360ffd3587f7db9e225a7e62b583a04494d2f069cf64ea/jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf", size = 19512 }, -] - -[[package]] -name = "jupyter-lsp" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/e8/9d61dcbd1dce8ef418f06befd4ac084b4720429c26b0b1222bc218685eff/jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81", size = 77513 }, -] - -[[package]] -name = "jupyter-server" -version = "2.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "argon2-cffi" }, - { name = "jinja2" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "jupyter-events" }, - { name = "jupyter-server-terminals" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "overrides", marker = "python_full_version < '3.12'" }, - { name = "packaging" }, - { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "pyzmq" }, - { name = "send2trash" }, - { name = "terminado" }, - { name = "tornado" }, - { name = "traitlets" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/a0/eb3c511f54df7b54ca5fc7bff3f4d2277d69052d6a7f521643dfed5279d6/jupyter_server-2.19.0.tar.gz", hash = "sha256:1731236bc32b680223e1ceb9d68209a845203475012ef68773a81434b46a31a7", size = 754561 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/78/d2881e68894cecdcd05912a9c585cfb776ef1fb38b62c8dba98f12ab3adc/jupyter_server-2.19.0-py3-none-any.whl", hash = "sha256:cb76591b76d7093584c2ad2ae72ac3d58614a4b597507a1bb04e1f9f683cf9ea", size = 392244 }, -] - -[[package]] -name = "jupyter-server-terminals" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "terminado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, -] - -[[package]] -name = "jupyterlab" -version = "4.5.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-lru" }, - { name = "httpx" }, - { name = "ipykernel" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyter-lsp" }, - { name = "jupyter-server" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "packaging" }, - { name = "setuptools" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/22/8440ec827762146e7cdecf04335bd348795899d29dc6ae82238707353a2c/jupyterlab-4.5.7.tar.gz", hash = "sha256:55a9822c4754da305f41e113452c68383e214dcf96de760146af89ce5d5117b0", size = 23992763 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/aa/537b8f7d80e799af19af35fb3ddfc970b951088a13c57dd9387dcfbb7f61/jupyterlab-4.5.7-py3-none-any.whl", hash = "sha256:fba4cb0e2c44a52859669d8c98b45de029d5e515f8407bf8534d2a8fc5f0964d", size = 12450123 }, -] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, -] - -[[package]] -name = "jupyterlab-server" -version = "2.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "jinja2" }, - { name = "json5" }, - { name = "jsonschema" }, - { name = "jupyter-server" }, - { name = "packaging" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, -] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.16" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, -] - -[[package]] -name = "jupytext" -version = "1.19.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/2d/15624c3d9440d85a280ff13d2d23afd989802f25470ac59932f4fef6f0c6/jupytext-1.19.3.tar.gz", hash = "sha256:713c3ed4441afe0f31474d28ea2e6b61a268c04c40fd78e5ccfd7f7ac9e9f766", size = 4305350 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/ec/d9be3bd1db141e76b2f525c265f70e66edd30a51a3307d8edf0ef1909c54/jupytext-1.19.3-py3-none-any.whl", hash = "sha256:acf75492f80895ad8e664fd8db1708b617008dd0e71c341a1abc3d0d07310ed0", size = 170579 }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160 }, -] - -[[package]] -name = "kiwisolver" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798 }, - { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216 }, - { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911 }, - { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209 }, - { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888 }, - { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304 }, - { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650 }, - { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949 }, - { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125 }, - { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783 }, - { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726 }, - { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738 }, - { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718 }, - { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480 }, - { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930 }, - { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158 }, - { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388 }, - { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068 }, - { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934 }, - { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537 }, - { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685 }, - { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024 }, - { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241 }, - { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742 }, - { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966 }, - { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417 }, - { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238 }, - { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947 }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569 }, - { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997 }, - { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166 }, - { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395 }, - { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065 }, - { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903 }, - { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751 }, - { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793 }, - { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041 }, - { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292 }, - { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865 }, - { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369 }, - { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989 }, - { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645 }, - { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237 }, - { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573 }, - { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998 }, - { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700 }, - { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537 }, - { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514 }, - { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848 }, - { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542 }, - { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447 }, - { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918 }, - { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856 }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580 }, - { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018 }, - { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804 }, - { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482 }, - { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328 }, - { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410 }, - { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231 }, - { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489 }, - { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063 }, - { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913 }, - { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782 }, - { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815 }, - { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925 }, - { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322 }, - { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857 }, - { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376 }, - { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549 }, - { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680 }, - { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905 }, - { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086 }, - { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577 }, - { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794 }, - { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646 }, - { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511 }, - { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858 }, - { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539 }, - { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310 }, - { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244 }, - { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154 }, - { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377 }, - { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288 }, - { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158 }, - { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260 }, - { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403 }, - { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687 }, - { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032 }, - { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532 }, - { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420 }, - { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892 }, - { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603 }, - { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558 }, -] - -[[package]] -name = "lark" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, -] - -[[package]] -name = "latexcodec" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/dd/4270b2c5e2ee49316c3859e62293bd2ea8e382339d63ab7bbe9f39c0ec3b/latexcodec-3.0.1.tar.gz", hash = "sha256:e78a6911cd72f9dec35031c6ec23584de6842bfbc4610a9678868d14cdfb0357", size = 31222 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/40/23569737873cc9637fd488606347e9dd92b9fa37ba4fcda1f98ee5219a97/latexcodec-3.0.1-py3-none-any.whl", hash = "sha256:a9eb8200bff693f0437a69581f7579eb6bca25c4193515c09900ce76451e452e", size = 18532 }, -] - -[[package]] -name = "lazy-loader" -version = "0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/ac/21a1f8aa3777f5658576777ea76bfb124b702c520bbe90edf4ae9915eafa/lazy_loader-0.5.tar.gz", hash = "sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3", size = 15294 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl", hash = "sha256:ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005", size = 8044 }, -] - -[[package]] -name = "legacy-api-wrap" -version = "1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/49/f06f94048c8974205730d40beca879e43b6eee08efb0101cfb8623e60f41/legacy_api_wrap-1.5.tar.gz", hash = "sha256:b41ba6532f3ebfe3a897a35a7f97dec3be04b92a450f6c2bcf89f1b91c9cadf2", size = 11610 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/5b/058db09c45ba58a7321bdf2294cae651b37d6fec68117265af90cde043b0/legacy_api_wrap-1.5-py3-none-any.whl", hash = "sha256:5a8ea50e3e3bcbcdec3447b77034fd0d32cb2cf4089db799238708e4d7e0098d", size = 10182 }, -] - -[[package]] -name = "leidenalg" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "igraph" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/a5/853e93441aed7f82b0389f86f37e19413e817ba0c54cc790895935256968/leidenalg-0.11.0.tar.gz", hash = "sha256:f454be96bbc8089ea2a90ca853d8d389ab646de964a03bd58417f8b29ff8ef5d", size = 452850 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/a9/4ab4e244215db0c8b626e4bed0d3e0fbd191c52d2d5f5cb9d160139ecc7e/leidenalg-0.11.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5607589050bfc1926e657b4d8a3b5341fe1eb81018c22cf4a3d3a39e368d1fcb", size = 2256514 }, - { url = "https://files.pythonhosted.org/packages/98/f4/98db342d603671ae0a233f0a624939a47161044a2716cbd62a50440a1132/leidenalg-0.11.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b5781876b1f1faed72a4f9926ff52de286843556b9d6791fe25a2acb33b7a5c", size = 1926003 }, - { url = "https://files.pythonhosted.org/packages/9b/38/fd6ac21af10b12828b472eada4fce0edf2a212581238ad0c8d1afebc6f98/leidenalg-0.11.0-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a80f49477f8e793f27d8e08949177f19e3834cd878af50a662b4f87335d06549", size = 2545535 }, - { url = "https://files.pythonhosted.org/packages/77/87/b087584750a788535b4a8d56ddeb82a175d32b472aa5338a4e2cc593a42c/leidenalg-0.11.0-cp38-abi3-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:2143be3e80485584ccbdf927323fce65345da17facd0f8b438f11015f5dc6c27", size = 2845029 }, - { url = "https://files.pythonhosted.org/packages/b0/a4/a89e2ce16a580f7bea066ed49364f0b3e04a6412f0c3692975bee8515141/leidenalg-0.11.0-cp38-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:571a0934f831a69442d82889d319bdba93de924bd9e09b720cd8cbe6fdc08c17", size = 2738084 }, - { url = "https://files.pythonhosted.org/packages/e8/fe/8923cac6cd7c9e0ac5f38aaa69a4744c93d025575763d05f7a3baae8020d/leidenalg-0.11.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec03e7178b19102dd271b453a39b9865cf283b4113151ba60514e5681046294", size = 4070307 }, - { url = "https://files.pythonhosted.org/packages/fe/94/beaab5ee9968f9389f705532c31ffb868bad8a5ce68fb699ddde5ddc5409/leidenalg-0.11.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:310b9269a11fd1e960590c1a2b6ff685a2cc42aa3234ce67bc2a623ab61f26a9", size = 3797863 }, - { url = "https://files.pythonhosted.org/packages/6e/8e/8caf4ba38fd7d8e6197348b348a4ab666b1b3117225ea2f0934a98a93176/leidenalg-0.11.0-cp38-abi3-win32.whl", hash = "sha256:5ea4cd7ee054540112b28f7e2d64658dcccd59f61a5d6a08a41df808645f96e9", size = 1643351 }, - { url = "https://files.pythonhosted.org/packages/47/15/7d459a8e2a43f17c1db129b997b7bb7aa7f000a0967bab87c28b8c5cf448/leidenalg-0.11.0-cp38-abi3-win_amd64.whl", hash = "sha256:5e789c0960008d185413344a402d0587580c441644d4d20bf57c96f25d4d1710", size = 1990321 }, - { url = "https://files.pythonhosted.org/packages/b2/01/f6bdfb489ce86cb11f7b5428bb8c3cff751436fff4e12c99f3b5f59d622e/leidenalg-0.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:aa9b3bb5e75c5120b57cb8a3ba176610853359a9f5e787ecd6ba7e85c61f4fdc", size = 2254690 }, - { url = "https://files.pythonhosted.org/packages/9b/50/ffad5f00e995ea064d1345867f6aa3534ebfb58e361309e088a12ef278d1/leidenalg-0.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:bed72262f181774de8425d6951c0e9c1d9c3d9bc64e030993e4215d6ab67933b", size = 1924396 }, - { url = "https://files.pythonhosted.org/packages/9d/7c/7683c87e9d0bf70519a0d6cdbb27363b3520a4d5edd9c5f59181e393e2ff/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1672eb4b24c2c484c0f4d2225267a456751f4624ac99619fed4fb9e4a31049", size = 2379529 }, - { url = "https://files.pythonhosted.org/packages/b8/0d/0560646fe75f6d161006cb6f164ef7d25096b9d59d68e836bce931ec8724/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:3d9c74f9c3d97d1daec5cc9f43b56a62bb3f40d5fef9f2b5defd8d715bfb5cdd", size = 2683317 }, - { url = "https://files.pythonhosted.org/packages/79/b9/d84d6225d79fed4fee19faef49e8b8fa58b70fcf55de5061bb1adf602eaa/leidenalg-0.11.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:023e97e4c96573e89fdea876da663e38694f6fa91e2c85e98455101409381d88", size = 2570109 }, -] - -[[package]] -name = "llvmlite" -version = "0.47.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/0b/b9d1911cfefa61399821dfb37f486d83e0f42630a8d12f7194270c417002/llvmlite-0.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07", size = 37232770 }, - { url = "https://files.pythonhosted.org/packages/46/27/5799b020e4cdfb25a7c951c06a96397c135efcdc21b78d853bbd9c814c7d/llvmlite-0.47.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb", size = 56275177 }, - { url = "https://files.pythonhosted.org/packages/7e/51/48a53fedf01cb1f3f43ef200be17ebf83c8d9a04018d3783c1a226c342c2/llvmlite-0.47.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8", size = 55128631 }, - { url = "https://files.pythonhosted.org/packages/a2/50/59227d06bdc96e23322713c381af4e77420949d8cd8a042c79e0043096cc/llvmlite-0.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327", size = 38138400 }, - { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769 }, - { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178 }, - { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632 }, - { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692 }, - { url = "https://files.pythonhosted.org/packages/77/6f/4615353e016799f80fa52ccb270a843c413b22361fadda2589b2922fb9b0/llvmlite-0.47.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec", size = 37232771 }, - { url = "https://files.pythonhosted.org/packages/31/b8/69f5565f1a280d032525878a86511eebed0645818492feeb169dfb20ae8e/llvmlite-0.47.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb", size = 56275178 }, - { url = "https://files.pythonhosted.org/packages/d6/da/b32cafcb926fb0ce2aa25553bf32cb8764af31438f40e2481df08884c947/llvmlite-0.47.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40", size = 55128632 }, - { url = "https://files.pythonhosted.org/packages/46/9f/4898b44e4042c60fafcb1162dfb7014f6f15b1ec19bf29cfea6bf26df90d/llvmlite-0.47.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e", size = 38138695 }, - { url = "https://files.pythonhosted.org/packages/1c/d4/33c8af00f0bf6f552d74f3a054f648af2c5bc6bece97972f3bfadce4f5ec/llvmlite-0.47.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14", size = 37232773 }, - { url = "https://files.pythonhosted.org/packages/64/1d/a760e993e0c0ba6db38d46b9f48f6c7dceb8ac838824997fb9e25f97bc04/llvmlite-0.47.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa", size = 56275176 }, - { url = "https://files.pythonhosted.org/packages/84/3b/e679bc3b29127182a7f4aa2d2e9e5bea42adb93fb840484147d59c236299/llvmlite-0.47.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca", size = 55128631 }, - { url = "https://files.pythonhosted.org/packages/be/f7/19e2a09c62809c9e63bbd14ce71fb92c6ff7b7b3045741bb00c781efc3c9/llvmlite-0.47.0-cp314-cp314-win_amd64.whl", hash = "sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94", size = 39153826 }, - { url = "https://files.pythonhosted.org/packages/40/a1/581a8c707b5e80efdbbe1dd94527404d33fe50bceb71f39d5a7e11bd57b7/llvmlite-0.47.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c", size = 37232772 }, - { url = "https://files.pythonhosted.org/packages/11/03/16090dd6f74ba2b8b922276047f15962fbeea0a75d5601607edb301ba945/llvmlite-0.47.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b", size = 56275178 }, - { url = "https://files.pythonhosted.org/packages/f5/cb/0abf1dd4c5286a95ffe0c1d8c67aec06b515894a0dd2ac97f5e27b82ab0b/llvmlite-0.47.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8", size = 55128632 }, - { url = "https://files.pythonhosted.org/packages/4f/79/d3bbab197e86e0ff4f9c07122895b66a3e0d024247fcff7f12c473cb36d9/llvmlite-0.47.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453", size = 39153839 }, -] - -[[package]] -name = "locket" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/83/97b29fe05cb6ae28d2dbd30b81e2e402a3eed5f460c26e9eaa5895ceacf5/locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632", size = 4350 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3", size = 4398 }, -] - -[[package]] -name = "markdown-it-py" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687 }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, - { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, - { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, - { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, - { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, - { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, - { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, - { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, - { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, - { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, - { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, -] - -[[package]] -name = "matplotlib" -version = "3.10.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860 }, - { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254 }, - { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092 }, - { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691 }, - { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771 }, - { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112 }, - { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310 }, - { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908 }, - { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016 }, - { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336 }, - { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602 }, - { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966 }, - { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462 }, - { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688 }, - { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331 }, - { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461 }, - { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091 }, - { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027 }, - { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269 }, - { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588 }, - { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913 }, - { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019 }, - { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645 }, - { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194 }, - { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684 }, - { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790 }, - { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571 }, - { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292 }, - { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276 }, - { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218 }, - { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145 }, - { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085 }, - { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358 }, - { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970 }, - { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785 }, - { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999 }, - { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543 }, - { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800 }, - { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561 }, - { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884 }, - { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333 }, - { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785 }, - { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420 }, - { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981 }, - { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002 }, -] - -[[package]] -name = "matplotlib-inline" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534 }, -] - -[[package]] -name = "matplotlib-scalebar" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "matplotlib" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/3d/e76e0e48c96e43b656446c35990824f20136de21940a7436020ab757a54d/matplotlib_scalebar-0.9.0.tar.gz", hash = "sha256:f91adc5e4e67ac920a8fa98a55f904d56e5c2da4d56286748c31ec62146362be", size = 1180588 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/c0/2dfab7b319dabe23f5a7b515a797c74b501d15c72e7a03837cf0cf779b9e/matplotlib_scalebar-0.9.0-py3-none-any.whl", hash = "sha256:5140525cd4e0c60bcade541b86571dabaf446fa69192530bd82d60b54601aa79", size = 16411 }, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - -[[package]] -name = "mistune" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/84/620cc3f7e3adf6f5067e10f4dbae71295d8f9e16d5d3f9ef97c40f2f592c/mistune-3.2.1.tar.gz", hash = "sha256:7c8e5501d38bac1582e067e46c8343f17d57ea1aaa735823f3aba1fd59c88a28", size = 98003 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/7f/a946aa4f8752b37102b41e64dca18a1976ac705c3a0d1dfe74d820a02552/mistune-3.2.1-py3-none-any.whl", hash = "sha256:78cdb0ba5e938053ccf63651b352508d2efa9411dc8810bfb05f2dc5140c0048", size = 53749 }, -] - -[[package]] -name = "ml-dtypes" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/5e/712092cfe7e5eb667b8ad9ca7c54442f21ed7ca8979745f1000e24cf8737/ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90", size = 679734 }, - { url = "https://files.pythonhosted.org/packages/4f/cf/912146dfd4b5c0eea956836c01dcd2fce6c9c844b2691f5152aca196ce4f/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040", size = 5056165 }, - { url = "https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483", size = 5034975 }, - { url = "https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb", size = 210742 }, - { url = "https://files.pythonhosted.org/packages/a0/c9/64230ef14e40aa3f1cb254ef623bf812735e6bec7772848d19131111ac0d/ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de", size = 160709 }, - { url = "https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac", size = 676927 }, - { url = "https://files.pythonhosted.org/packages/54/0f/428ef6881782e5ebb7eca459689448c0394fa0a80bea3aa9262cba5445ea/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900", size = 5028464 }, - { url = "https://files.pythonhosted.org/packages/3a/cb/28ce52eb94390dda42599c98ea0204d74799e4d8047a0eb559b6fd648056/ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff", size = 5009002 }, - { url = "https://files.pythonhosted.org/packages/f5/f0/0cfadd537c5470378b1b32bd859cf2824972174b51b873c9d95cfd7475a5/ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7", size = 212222 }, - { url = "https://files.pythonhosted.org/packages/16/2e/9acc86985bfad8f2c2d30291b27cd2bb4c74cea08695bd540906ed744249/ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460", size = 160793 }, - { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888 }, - { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993 }, - { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956 }, - { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224 }, - { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798 }, - { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083 }, - { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111 }, - { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453 }, - { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612 }, - { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145 }, - { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781 }, - { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145 }, - { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230 }, - { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032 }, - { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353 }, - { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085 }, - { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358 }, - { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332 }, - { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612 }, - { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825 }, -] - -[[package]] -name = "more-itertools" -version = "11.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226 }, -] - -[[package]] -name = "msgpack" -version = "1.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271 }, - { url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914 }, - { url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962 }, - { url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183 }, - { url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454 }, - { url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341 }, - { url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747 }, - { url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633 }, - { url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755 }, - { url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939 }, - { url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064 }, - { url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131 }, - { url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556 }, - { url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920 }, - { url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013 }, - { url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096 }, - { url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708 }, - { url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119 }, - { url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212 }, - { url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315 }, - { url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721 }, - { url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657 }, - { url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668 }, - { url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040 }, - { url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037 }, - { url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631 }, - { url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118 }, - { url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127 }, - { url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981 }, - { url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885 }, - { url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658 }, - { url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290 }, - { url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234 }, - { url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391 }, - { url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787 }, - { url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453 }, - { url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264 }, - { url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076 }, - { url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242 }, - { url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509 }, - { url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957 }, - { url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910 }, - { url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197 }, - { url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772 }, - { url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868 }, -] - -[[package]] -name = "multidict" -version = "6.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626 }, - { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706 }, - { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356 }, - { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355 }, - { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433 }, - { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376 }, - { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365 }, - { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747 }, - { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293 }, - { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962 }, - { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360 }, - { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940 }, - { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502 }, - { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065 }, - { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870 }, - { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302 }, - { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981 }, - { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159 }, - { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893 }, - { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456 }, - { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872 }, - { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018 }, - { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883 }, - { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413 }, - { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404 }, - { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456 }, - { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322 }, - { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955 }, - { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254 }, - { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059 }, - { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588 }, - { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642 }, - { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377 }, - { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887 }, - { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053 }, - { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307 }, - { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174 }, - { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116 }, - { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524 }, - { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368 }, - { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952 }, - { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317 }, - { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132 }, - { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140 }, - { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277 }, - { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291 }, - { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156 }, - { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742 }, - { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221 }, - { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664 }, - { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490 }, - { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695 }, - { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884 }, - { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122 }, - { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175 }, - { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460 }, - { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930 }, - { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582 }, - { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031 }, - { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596 }, - { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492 }, - { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899 }, - { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970 }, - { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060 }, - { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888 }, - { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554 }, - { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341 }, - { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391 }, - { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422 }, - { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770 }, - { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109 }, - { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573 }, - { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190 }, - { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486 }, - { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219 }, - { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132 }, - { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420 }, - { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510 }, - { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094 }, - { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786 }, - { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483 }, - { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403 }, - { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315 }, - { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528 }, - { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784 }, - { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980 }, - { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602 }, - { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930 }, - { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074 }, - { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471 }, - { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401 }, - { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143 }, - { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507 }, - { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358 }, - { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884 }, - { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878 }, - { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542 }, - { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403 }, - { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889 }, - { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982 }, - { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415 }, - { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337 }, - { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788 }, - { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842 }, - { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237 }, - { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008 }, - { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542 }, - { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719 }, - { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, -] - -[[package]] -name = "multipledispatch" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/3e/a62c3b824c7dec33c4a1578bcc842e6c30300051033a4e5975ed86cc2536/multipledispatch-1.0.0.tar.gz", hash = "sha256:5c839915465c68206c3e9c473357908216c28383b425361e5d144594bf85a7e0", size = 12385 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/c0/00c9809d8b9346eb238a6bbd5f83e846a4ce4503da94a4c08cb7284c325b/multipledispatch-1.0.0-py3-none-any.whl", hash = "sha256:0c53cd8b077546da4e48869f49b13164bebafd0c2a5afceb6bb6a316e7fb46e4", size = 12818 }, -] - -[[package]] -name = "multiscale-spatial-image" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dask" }, - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "spatial-image" }, - { name = "xarray" }, - { name = "xarray-dataclass" }, - { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/93/8cab41d5f2d11834e4d077d7a9dd7cdf74596fdc68290e31e77422d4edf2/multiscale_spatial_image-2.0.3.tar.gz", hash = "sha256:0b72b20785c5c8bbae7aea0608ca53e02d61fd76720b10473aeafb97e1616cab", size = 1310669 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/a7/fec56dbac873a18930b2127d400794a91dd53898bff811aa4802ddbbfac9/multiscale_spatial_image-2.0.3-py3-none-any.whl", hash = "sha256:8b20d36e90a104083fe14f5466d582eea453c213d1a368d7e7435b3e13445f61", size = 29169 }, -] - -[[package]] -name = "myst-nb" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata" }, - { name = "ipykernel" }, - { name = "ipython" }, - { name = "jupyter-cache" }, - { name = "myst-parser" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "pyyaml" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/b4/ff1abeea67e8cfe0a8c033389f6d1d8b0bfecfd611befb5cbdeab884fce6/myst_nb-1.4.0.tar.gz", hash = "sha256:c145598de62446a6fd009773dd071a40d3b76106ace780de1abdfc6961f614c2", size = 82285 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/93/0a378b48488879a1d925b42a804edfc6e0cd0ef854220f2dce738a46e7e9/myst_nb-1.4.0-py3-none-any.whl", hash = "sha256:0e2c86e7d3b82c3aa51383f82d6268f7714f3b772c23a796ab09538a8e68b4e4", size = 82555 }, -] - -[[package]] -name = "myst-parser" -version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/21/dc/603751677fff302f34396e206b610f556a59d7fe58b9a2145f54e96b48e8/myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02", size = 101182 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/dc/f3dfb7488b770f3f67e6545085bf2abea5172e88f57b8ad25ef860ca704c/myst_parser-5.1.0-py3-none-any.whl", hash = "sha256:9c91c52b3cdb4d94a6506e4fab4e2f296c7623a0da0dcbe6de1565c3dad67a8a", size = 85817 }, -] - -[[package]] -name = "narwhals" -version = "2.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/a0/6198c56d42ef2f3c6ed0c42ba30dbcefdc86a91262d7d449010770ae085b/narwhals-2.21.2.tar.gz", hash = "sha256:5c5b2d0b47aef7c73ea412cfcbcd467f2f2d5be73e3c2ab19d78f4a97718790a", size = 632176 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/77/928ea2e70641ca177a11140062cc5840d421795f2e82749d408d0cce900a/narwhals-2.21.2-py3-none-any.whl", hash = "sha256:7bb57c3700486039215455b9bf2d64261915cc0fd845cc30272d631df696b251", size = 451201 }, -] - -[package.optional-dependencies] -pandas = [ - { name = "pandas" }, -] - -[[package]] -name = "natsort" -version = "8.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, -] - -[[package]] -name = "nbclient" -version = "0.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "nbformat" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465 }, -] - -[[package]] -name = "nbconvert" -version = "7.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "bleach", extra = ["css"] }, - { name = "defusedxml" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyterlab-pygments" }, - { name = "markupsafe" }, - { name = "mistune" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "pandocfilters" }, - { name = "pygments" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/f8/bb0a9d5f46819c821dc1f004aa2cc29b1d91453297dbf5ff20470f00f193/nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8", size = 261927 }, -] - -[[package]] -name = "nbformat" -version = "5.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastjsonschema" }, - { name = "jsonschema" }, - { name = "jupyter-core" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, -] - -[[package]] -name = "nbsphinx" -version = "0.9.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/d1/82081750f8a78ad0399c6ed831d42623b891904e8e7b8a75878225cf1dce/nbsphinx-0.9.8.tar.gz", hash = "sha256:d0765908399a8ee2b57be7ae881cf2ea58d66db3af7bbf33e6eb48f83bea5495", size = 417469 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/78/843bcf0cf31f88d2f8a9a063d2d80817b1901657d83d65b89b3aa835732e/nbsphinx-0.9.8-py3-none-any.whl", hash = "sha256:92d95ee91784e56bc633b60b767a6b6f23a0445f891e24641ce3c3f004759ccf", size = 31961 }, -] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, -] - -[[package]] -name = "networkx" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504 }, -] - -[[package]] -name = "nodeenv" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438 }, -] - -[[package]] -name = "notebook" -version = "7.5.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, - { name = "jupyterlab" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/c2/cf59bd2e6f2c8b976b52477e3e53bf6f97bc714ed046a51821afb428eaee/notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1", size = 14170814 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/d6/1fd0646b9bbd9efbb0b8ae21b2325fbef515769a5621c03e31d8eb8da587/notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0", size = 14581730 }, -] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, -] - -[[package]] -name = "numba" -version = "0.65.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llvmlite" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/c5/db2ac3685833d626c0dcae6bd2330cd68433e1fd248d15f70998160d3ad7/numba-0.65.1.tar.gz", hash = "sha256:19357146c32fe9ed25059ab915e8465fb13951cf6b0aace3826b76886373ab23", size = 2765600 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/b3/650500c2eab4534d98e9166f4298e0f3c69c742afdf24e6eabccd1f16ad8/numba-0.65.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:7020d74b19cdb8cff16506542fdd510756e28c5e7f3bd0b7f574f0f42272fcd9", size = 2680563 }, - { url = "https://files.pythonhosted.org/packages/44/0b/0615dbedb98f5b32a35a53290fbdc6e22306968109278d7e58df82d7a9f6/numba-0.65.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f80ed83774b5173abd6581cd8d2165d1d38e13d2e5c8155c0c0b421784745420", size = 3745018 }, - { url = "https://files.pythonhosted.org/packages/49/aa/4361698f35bf63bff67dfe6c90493731177f48ede954f77b0588731537bc/numba-0.65.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ed425a43b0a5f9772f2f4e2dd0bbd12eabecae1af0b24efcfd4e053f012aac6", size = 3450962 }, - { url = "https://files.pythonhosted.org/packages/bd/9a/af61ec03b3116c161fd7a06b9e8a265729a8718458333e8ffbb06d9a3978/numba-0.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:df40a5028a975b9ea66f6a2a3f7abbdbd541a863070e34ed367aff21141248e4", size = 2747417 }, - { url = "https://files.pythonhosted.org/packages/57/bc/76f8f8c5cf9adee47fdb7bbb03be8900f76f902d451d7477cf12b845e1de/numba-0.65.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac3f1e77c352dd0ea9712732c2d8f9ca507717435eec5b5013bf138ac33c4a08", size = 2681371 }, - { url = "https://files.pythonhosted.org/packages/69/47/a415af0283e4db0398104c6d1c11c9861a98dc67a7aa442a7769ed5d6196/numba-0.65.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:52bc6f3ceb8fcaff9b2ae26b4c6b1e9fee39db8d355534c0fe4f39a901246b84", size = 3802467 }, - { url = "https://files.pythonhosted.org/packages/46/36/246f73ec99cfeab2f2cb2ce7d4218766cc36a2da418901223f4f4da9c813/numba-0.65.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90ca10b3463bae0bd70589726fe3c77d01d6b5fc86bee54bcdf9fb6b47c28977", size = 3502628 }, - { url = "https://files.pythonhosted.org/packages/db/9e/3c679b2ee078425b9e99a91e44f8d132a6830d8ccce5227bc5e9181aeed8/numba-0.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:5971c632be2a2351500431f46213821dba8d02b18a9f7d02fd36bd2743e41a6a", size = 2750611 }, - { url = "https://files.pythonhosted.org/packages/79/37/14a4579049c1eb673afd0de0cb4842982acd55b9ce2643e763db858bcea0/numba-0.65.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1735c15c1134a5108b4d6a5c77fc0947924ea066a738dc09a52008c13df9cad3", size = 2681344 }, - { url = "https://files.pythonhosted.org/packages/a0/22/b8d873f6466b20aa563fc9b33acd48dec89a07803ddaa2f1c8ca1cd33126/numba-0.65.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c09f49117ef255e1f1c6dad0c7a1ed39868243862a73be5706793241a3755f1b", size = 3810619 }, - { url = "https://files.pythonhosted.org/packages/62/08/e16a8b5d9a018962ebb5c66be662317cde32b9f5dab08441f90bed5522fb/numba-0.65.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:594a8680b3fadac99e97e489b1fd89007177e5336713745c3b769528c635a464", size = 3509783 }, - { url = "https://files.pythonhosted.org/packages/fd/a5/03c970d57f4c1741354837353ce39fb5206952ae1dba8922d29c86f64805/numba-0.65.1-cp313-cp313-win_amd64.whl", hash = "sha256:85be74c0d036842699a30058f82fb88fc5ffdc59f7615cab5792ea92914c9b62", size = 2750534 }, - { url = "https://files.pythonhosted.org/packages/4f/2e/8aed9b726d9ba5f11ad287645fd479e88278db3060a25cb1225d730eb2b7/numba-0.65.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:33f5eb68eb1c843511615d14663ce60258525d6a4c65ab040e2c2b0c4cf17450", size = 2681554 }, - { url = "https://files.pythonhosted.org/packages/87/96/f3eb235fafa82a34e2ab5dd7dc9ffff998ebf5f0bbc23fa56a96aeb44da6/numba-0.65.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71e73029bf53a62cc6afcf96be4bd942290d8b4c55f0a454fb536158115790f7", size = 3779602 }, - { url = "https://files.pythonhosted.org/packages/09/90/b0f09b48752d23640b8284f22aa597737e8adaddc7fbfacc4708b7f73a4c/numba-0.65.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a07635e0be926b9bdbffb09137c230fb13f6ec0e564914ba937cee12ce3eb35", size = 3479532 }, - { url = "https://files.pythonhosted.org/packages/56/46/3f7fc04fb853559e74b210e0b62c19974ec844cefec611f9e535f4da3761/numba-0.65.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a20fcdabdefbdacf88d85caf70c3b18c4bcb7ebb8f82e6a19486383dd26ab63", size = 2752637 }, - { url = "https://files.pythonhosted.org/packages/81/7b/c1a341a9067367778f4152a5f01061cf281fb09582c92c510ec4918cabf6/numba-0.65.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:548dd4b3a4508d5062768d1514b2cd7b015f9a25ec7af651c50dee243965e652", size = 2684600 }, - { url = "https://files.pythonhosted.org/packages/03/36/98ddbcf3e4f04a6dd07e1c67249955920579ba4af6bb6868e3088f4ed282/numba-0.65.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:78abc28feff2c2ff8307fff3975b6438352759c9acb797ecd6b1fb6e7e39e31d", size = 3817198 }, - { url = "https://files.pythonhosted.org/packages/a3/83/0dad21057ece5a835599f5d24099b091703995e23dbbf894f259e91c010b/numba-0.65.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7676cb389555805f9b9a1840cbcd1ea6c8bd5376ab6918e3a29c5ea1dbda20", size = 3533862 }, - { url = "https://files.pythonhosted.org/packages/32/36/8be7118ffd4c8440881046eac3d0982cc5ab42909508cf5d67024d62a2e4/numba-0.65.1-cp314-cp314t-win_amd64.whl", hash = "sha256:20609346e3bd75204950dcbbfe383a8d7dbf4902f442aedbf00f97fef4aa8f38", size = 2758237 }, -] - -[[package]] -name = "numcodecs" -version = "0.16.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/44/bd/8a391e7c356366224734efd24da929cc4796fff468bfb179fe1af6548535/numcodecs-0.16.5.tar.gz", hash = "sha256:0d0fb60852f84c0bd9543cc4d2ab9eefd37fc8efcc410acd4777e62a1d300318", size = 6276387 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/85/1ac101a40ead81eaa1c7dc49a8827a30e2e436211b43ebdc63c590eb1347/numcodecs-0.16.5-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:78382dcea50622f2ef1e6e7a71dbe7f861d8fe376b27b7c297c26907304fef1e", size = 1621795 }, - { url = "https://files.pythonhosted.org/packages/0e/cc/0d97ef55dda48cb0f93d7b92d761208e7a99bd2eea6b0e859426e6a99a21/numcodecs-0.16.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2d04a19cb57a3c519b4127ac377cca6471aee1990d7c18f5b1e3a4fe1306689", size = 1153030 }, - { url = "https://files.pythonhosted.org/packages/5e/41/e120ee1b390730ac5987cde2afd82e2b8442cec315ab40b94b0373e93e73/numcodecs-0.16.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c043af648eb280cd61785c99c22ff5c3c3460f906eb51a8511327c4f5111b283", size = 8510503 }, - { url = "https://files.pythonhosted.org/packages/54/4b/195ac84cc8f6077b4f0f421e8daee21b7f1bd88cb7716414234379fe68ec/numcodecs-0.16.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c398919ef2eb0e56b8e97456f622640bfd3deed06de3acc976989cbcb22628a3", size = 9123428 }, - { url = "https://files.pythonhosted.org/packages/0f/5b/af02c417954f46e5c7bd5163ac251f535877d909fce54861c99ae197f6f6/numcodecs-0.16.5-cp311-cp311-win_amd64.whl", hash = "sha256:3820860ed302d4d84a1c66e70981ff959d5eb712555be4e7d8ced49888594773", size = 801542 }, - { url = "https://files.pythonhosted.org/packages/75/cc/55420f3641a67f78392dc0bc5d02cb9eb0a9dcebf2848d1ac77253ca61fa/numcodecs-0.16.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:24e675dc8d1550cd976a99479b87d872cb142632c75cc402fea04c08c4898523", size = 1656287 }, - { url = "https://files.pythonhosted.org/packages/f5/6c/86644987505dcb90ba6d627d6989c27bafb0699f9fd00187e06d05ea8594/numcodecs-0.16.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:94ddfa4341d1a3ab99989d13b01b5134abb687d3dab2ead54b450aefe4ad5bd6", size = 1148899 }, - { url = "https://files.pythonhosted.org/packages/97/1e/98aaddf272552d9fef1f0296a9939d1487914a239e98678f6b20f8b0a5c8/numcodecs-0.16.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b554ab9ecf69de7ca2b6b5e8bc696bd9747559cb4dd5127bd08d7a28bec59c3a", size = 8534814 }, - { url = "https://files.pythonhosted.org/packages/fb/53/78c98ef5c8b2b784453487f3e4d6c017b20747c58b470393e230c78d18e8/numcodecs-0.16.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad1a379a45bd3491deab8ae6548313946744f868c21d5340116977ea3be5b1d6", size = 9173471 }, - { url = "https://files.pythonhosted.org/packages/1c/20/2fdec87fc7f8cec950d2b0bea603c12dc9f05b4966dc5924ba5a36a61bf6/numcodecs-0.16.5-cp312-cp312-win_amd64.whl", hash = "sha256:845a9857886ffe4a3172ba1c537ae5bcc01e65068c31cf1fce1a844bd1da050f", size = 801412 }, - { url = "https://files.pythonhosted.org/packages/38/38/071ced5a5fd1c85ba0e14ba721b66b053823e5176298c2f707e50bed11d9/numcodecs-0.16.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25be3a516ab677dad890760d357cfe081a371d9c0a2e9a204562318ac5969de3", size = 1654359 }, - { url = "https://files.pythonhosted.org/packages/d1/c0/5f84ba7525577c1b9909fc2d06ef11314825fc4ad4378f61d0e4c9883b4a/numcodecs-0.16.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0107e839ef75b854e969cb577e140b1aadb9847893937636582d23a2a4c6ce50", size = 1144237 }, - { url = "https://files.pythonhosted.org/packages/0b/00/787ea5f237b8ea7bc67140c99155f9c00b5baf11c49afc5f3bfefa298f95/numcodecs-0.16.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:015a7c859ecc2a06e2a548f64008c0ec3aaecabc26456c2c62f4278d8fc20597", size = 8483064 }, - { url = "https://files.pythonhosted.org/packages/c4/e6/d359fdd37498e74d26a167f7a51e54542e642ea47181eb4e643a69a066c3/numcodecs-0.16.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84230b4b9dad2392f2a84242bd6e3e659ac137b5a1ce3571d6965fca673e0903", size = 9126063 }, - { url = "https://files.pythonhosted.org/packages/27/72/6663cc0382ddbb866136c255c837bcb96cc7ce5e83562efec55e1b995941/numcodecs-0.16.5-cp313-cp313-win_amd64.whl", hash = "sha256:5088145502ad1ebf677ec47d00eb6f0fd600658217db3e0c070c321c85d6cf3d", size = 799275 }, - { url = "https://files.pythonhosted.org/packages/3c/9e/38e7ca8184c958b51f45d56a4aeceb1134ecde2d8bd157efadc98502cc42/numcodecs-0.16.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b05647b8b769e6bc8016e9fd4843c823ce5c9f2337c089fb5c9c4da05e5275de", size = 1654721 }, - { url = "https://files.pythonhosted.org/packages/a1/37/260fa42e7b2b08e6e00ad632f8dd620961a60a459426c26cea390f8c68d0/numcodecs-0.16.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3832bd1b5af8bb3e413076b7d93318c8e7d7b68935006b9fa36ca057d1725a8f", size = 1146887 }, - { url = "https://files.pythonhosted.org/packages/4e/15/e2e1151b5a8b14a15dfd4bb4abccce7fff7580f39bc34092780088835f3a/numcodecs-0.16.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49f7b7d24f103187f53135bed28bb9f0ed6b2e14c604664726487bb6d7c882e1", size = 8476987 }, - { url = "https://files.pythonhosted.org/packages/6d/30/16a57fc4d9fb0ba06c600408bd6634f2f1753c54a7a351c99c5e09b51ee2/numcodecs-0.16.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aec9736d81b70f337d89c4070ee3ffeff113f386fd789492fa152d26a15043e4", size = 9102377 }, - { url = "https://files.pythonhosted.org/packages/31/a5/a0425af36c20d55a3ea884db4b4efca25a43bea9214ba69ca7932dd997b4/numcodecs-0.16.5-cp314-cp314-win_amd64.whl", hash = "sha256:b16a14303800e9fb88abc39463ab4706c037647ac17e49e297faa5f7d7dbbf1d", size = 819022 }, -] - -[[package]] -name = "numpy" -version = "2.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194 }, - { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111 }, - { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159 }, - { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936 }, - { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692 }, - { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164 }, - { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877 }, - { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487 }, - { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945 }, - { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406 }, - { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528 }, - { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119 }, - { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246 }, - { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410 }, - { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240 }, - { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012 }, - { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538 }, - { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706 }, - { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541 }, - { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825 }, - { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687 }, - { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482 }, - { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648 }, - { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902 }, - { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992 }, - { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944 }, - { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392 }, - { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220 }, - { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800 }, - { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600 }, - { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134 }, - { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598 }, - { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272 }, - { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197 }, - { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287 }, - { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763 }, - { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070 }, - { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752 }, - { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024 }, - { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398 }, - { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971 }, - { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532 }, - { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881 }, - { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458 }, - { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559 }, - { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716 }, - { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947 }, - { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197 }, - { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245 }, - { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587 }, - { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226 }, - { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196 }, - { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334 }, - { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678 }, - { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672 }, - { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731 }, - { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805 }, - { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496 }, - { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616 }, - { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145 }, - { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813 }, - { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982 }, - { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908 }, - { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867 }, - { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511 }, - { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064 }, - { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157 }, - { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728 }, - { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374 }, - { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286 }, - { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263 }, -] - -[[package]] -name = "ome-zarr" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "dask" }, - { name = "deprecated" }, - { name = "fsspec", extra = ["s3"] }, - { name = "numpy" }, - { name = "rangehttpserver" }, - { name = "requests" }, - { name = "scikit-image" }, - { name = "toolz" }, - { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/ee/0e99c62dd3d453c9fb84d8c8cc9f64e255c2d7638546836516282fae2be5/ome_zarr-0.15.0.tar.gz", hash = "sha256:e9479d3a9849c194a616355290f6ae574a7d4402559c5f7005be20cf81945435", size = 81497 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/71/159de9929fe98326ada41393d2133fda3f9869ea10bf94996c48846ff68f/ome_zarr-0.15.0-py3-none-any.whl", hash = "sha256:99789b0c4c27fbd197ab1a8bb3ae58b1c7b4e72d0d6b95e9dda6e6416623d3eb", size = 45200 }, -] - -[[package]] -name = "omnipath" -version = "1.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "docrep" }, - { name = "inflect" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "typing-extensions" }, - { name = "urllib3" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/b8/f504706093c149092925c1679aa96aced48af4bde1f73620cc2a6edc7f79/omnipath-1.0.12.tar.gz", hash = "sha256:37e33fd0ab4924a5345deab46868fdc472955c2be136c97ccc8dc5b5ea0ecfb8", size = 2058935 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ba/704f9e56ae80ef66cf0534e23dac22ada34845f86b5e5b8b3294649d96b6/omnipath-1.0.12-py3-none-any.whl", hash = "sha256:28b85360f2f566e3c1e146560f8d3a5c133e221ce93c63ef62b3c71be9a6b003", size = 51621 }, -] - -[[package]] -name = "opt-einsum" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932 }, -] - -[[package]] -name = "overrides" -version = "7.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, -] - -[[package]] -name = "pandas" -version = "2.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790 }, - { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831 }, - { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267 }, - { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281 }, - { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453 }, - { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361 }, - { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702 }, - { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846 }, - { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618 }, - { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212 }, - { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693 }, - { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002 }, - { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971 }, - { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722 }, - { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 }, - { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 }, - { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 }, - { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 }, - { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 }, - { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 }, - { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 }, - { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 }, - { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 }, - { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 }, - { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 }, - { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 }, - { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 }, - { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 }, - { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 }, - { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 }, - { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 }, - { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 }, - { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 }, - { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 }, - { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 }, - { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 }, - { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 }, - { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 }, - { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 }, - { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 }, -] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, -] - -[[package]] -name = "param" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/b5/e64cb3d5767dd10ed69c5e3abfab3a94debc1bcf8a56e35b7e720960eb30/param-2.4.0.tar.gz", hash = "sha256:d2c09da9fe80084cb5a8732761f26786317133995344df03db5f09d63ac35233", size = 216559 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/84/8b09d8bd4acf852266374ee53c257be11348b214b6febdc4affbf33fa091/param-2.4.0-py3-none-any.whl", hash = "sha256:770ecebb1bb8ffaeb8722a00976ba5092af287344471c513ba51597b4257339b", size = 150655 }, -] - -[[package]] -name = "parso" -version = "0.8.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025 }, -] - -[[package]] -name = "partd" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "locket" }, - { name = "toolz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f", size = 18905 }, -] - -[[package]] -name = "pathlib-abc" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/cb/448649d7f25d228bf0be3a04590ab7afa77f15e056f8fa976ed05ec9a78f/pathlib_abc-0.5.2.tar.gz", hash = "sha256:fcd56f147234645e2c59c7ae22808b34c364bb231f685ddd9f96885aed78a94c", size = 33342 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/29/c028a0731e202035f0e2e0bfbf1a3e46ad6c628cbb17f6f1cc9eea5d9ff1/pathlib_abc-0.5.2-py3-none-any.whl", hash = "sha256:4c9d94cf1b23af417ce7c0417b43333b06a106c01000b286c99de230d95eefbb", size = 19070 }, -] - -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328 }, -] - -[[package]] -name = "patsy" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/be/44/ed13eccdd0519eff265f44b670d46fbb0ec813e2274932dc1c0e48520f7d/patsy-1.0.2.tar.gz", hash = "sha256:cdc995455f6233e90e22de72c37fcadb344e7586fb83f06696f54d92f8ce74c0", size = 399942 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/70/ba4b949bdc0490ab78d545459acd7702b211dfccf7eb89bbc1060f52818d/patsy-1.0.2-py2.py3-none-any.whl", hash = "sha256:37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a", size = 233301 }, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, -] - -[[package]] -name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347 }, - { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873 }, - { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168 }, - { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188 }, - { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401 }, - { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655 }, - { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105 }, - { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402 }, - { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149 }, - { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626 }, - { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531 }, - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279 }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490 }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462 }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744 }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371 }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215 }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783 }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112 }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489 }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129 }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612 }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837 }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528 }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401 }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094 }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402 }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005 }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669 }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194 }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423 }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667 }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580 }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896 }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266 }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508 }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927 }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624 }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252 }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550 }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114 }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667 }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966 }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241 }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592 }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542 }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765 }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848 }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515 }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159 }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185 }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386 }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384 }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599 }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021 }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360 }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628 }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321 }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723 }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400 }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835 }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225 }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541 }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251 }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807 }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935 }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720 }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498 }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413 }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084 }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152 }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579 }, - { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969 }, - { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674 }, - { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479 }, - { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230 }, - { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404 }, - { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215 }, - { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946 }, -] - -[[package]] -name = "pims" -version = "0.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "imageio" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "slicerator" }, - { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz", hash = "sha256:55907a4c301256086d2aa4e34a5361b9109f24e375c2071e1117b9491e82946b", size = 87779 } - -[[package]] -name = "platformdirs" -version = "4.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743 }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, -] - -[[package]] -name = "pooch" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "platformdirs" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/43/85ef45e8b36c6a48546af7b266592dc32d7f67837a6514d111bced6d7d75/pooch-1.9.0.tar.gz", hash = "sha256:de46729579b9857ffd3e741987a2f6d5e0e03219892c167c6578c0091fb511ed", size = 61788 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl", hash = "sha256:f265597baa9f760d25ceb29d0beb8186c243d6607b0f60b83ecf14078dbc703b", size = 67175 }, -] - -[[package]] -name = "pre-commit" -version = "4.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472 }, -] - -[[package]] -name = "prometheus-client" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154 }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, -] - -[[package]] -name = "propcache" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744 }, - { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033 }, - { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754 }, - { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573 }, - { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645 }, - { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563 }, - { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888 }, - { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253 }, - { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558 }, - { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007 }, - { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355 }, - { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057 }, - { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938 }, - { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731 }, - { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966 }, - { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135 }, - { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381 }, - { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887 }, - { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654 }, - { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190 }, - { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995 }, - { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422 }, - { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342 }, - { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639 }, - { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588 }, - { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029 }, - { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774 }, - { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532 }, - { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592 }, - { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788 }, - { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514 }, - { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018 }, - { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322 }, - { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172 }, - { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457 }, - { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835 }, - { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545 }, - { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886 }, - { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261 }, - { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184 }, - { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534 }, - { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500 }, - { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994 }, - { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884 }, - { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464 }, - { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588 }, - { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667 }, - { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463 }, - { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621 }, - { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649 }, - { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636 }, - { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872 }, - { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257 }, - { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696 }, - { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378 }, - { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283 }, - { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616 }, - { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773 }, - { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664 }, - { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643 }, - { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595 }, - { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711 }, - { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247 }, - { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102 }, - { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964 }, - { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546 }, - { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330 }, - { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521 }, - { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662 }, - { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928 }, - { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650 }, - { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912 }, - { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300 }, - { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208 }, - { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633 }, - { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724 }, - { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069 }, - { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099 }, - { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391 }, - { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626 }, - { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781 }, - { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570 }, - { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436 }, - { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373 }, - { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554 }, - { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395 }, - { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653 }, - { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914 }, - { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567 }, - { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542 }, - { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845 }, - { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985 }, - { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999 }, - { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779 }, - { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796 }, - { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023 }, - { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448 }, - { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329 }, - { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172 }, - { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813 }, - { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764 }, - { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140 }, - { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036 }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664 }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087 }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383 }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210 }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228 }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284 }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, -] - -[[package]] -name = "pyarrow" -version = "24.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898 }, - { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915 }, - { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931 }, - { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449 }, - { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949 }, - { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986 }, - { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371 }, - { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559 }, - { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654 }, - { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394 }, - { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122 }, - { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032 }, - { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490 }, - { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660 }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759 }, - { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471 }, - { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981 }, - { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172 }, - { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733 }, - { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335 }, - { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748 }, - { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554 }, - { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301 }, - { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929 }, - { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365 }, - { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819 }, - { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252 }, - { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127 }, - { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997 }, - { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720 }, - { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852 }, - { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852 }, - { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207 }, - { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117 }, - { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155 }, - { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387 }, - { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102 }, - { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118 }, - { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765 }, - { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890 }, - { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250 }, - { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282 }, -] - -[[package]] -name = "pybtex" -version = "0.26.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "latexcodec" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f5/f30da9c93f0fa6d619332b2f69597219b625f35780473a05164a9981fd9a/pybtex-0.26.1.tar.gz", hash = "sha256:2e5543bea424e60e9e42eef70bff597be48649d8f68ba061a7a092b2477d5464", size = 692991 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/f6/775eb92e865b28cdb4ad1f2bed7a5446197516f76b58a950faa3be3fd08d/pybtex-0.26.1-py3-none-any.whl", hash = "sha256:e26c0412cc54f5f21b2a6d9d175762a2d2af9ccf3a8f651cdb89ec035db77aa1", size = 126134 }, -] - -[[package]] -name = "pybtex-docutils" -version = "1.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "pybtex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7e/84/796ea94d26188a853660f81bded39f8de4cfe595130aef0dea1088705a11/pybtex-docutils-1.0.3.tar.gz", hash = "sha256:3a7ebdf92b593e00e8c1c538aa9a20bca5d92d84231124715acc964d51d93c6b", size = 18348 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/b1/ce1f4596211efb5410e178a803f08e59b20bedb66837dcf41e21c54f9ec1/pybtex_docutils-1.0.3-py3-none-any.whl", hash = "sha256:8fd290d2ae48e32fcb54d86b0efb8d573198653c7e2447d5bec5847095f430b9", size = 6385 }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, -] - -[[package]] -name = "pyct" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "param" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/c3/78eeacf7cbf478db6bd41de0ee2221cc6769d81e0c10f6c1616c82a25e06/pyct-0.6.0.tar.gz", hash = "sha256:d4e513b2cf35b6165605ae5fce5f2a49985bc67473c579de85134b8c71d374a8", size = 14586 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/b2/23f4032cd1c9744aa8e9ecda43cd4d755fcb209f7f40fae035248f31a679/pyct-0.6.0-py3-none-any.whl", hash = "sha256:cfaded7289fca72ddf6579b81459e3ec8db323a508e61c49aa318ee3cd6ff160", size = 16630 }, -] - -[[package]] -name = "pyenchant" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/ad/64925c937e41be75c7067c85757b3d45b148e9111187b37693269f583156/pyenchant-3.3.0.tar.gz", hash = "sha256:825288246b5debc9436f91967650974ef0d5636458502619e322c476f1283891", size = 60696 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/b0/35926bad6885fb7bc24aa7e1b45e6d86540c6c57ee4abc4fed1ef58d4ec0/pyenchant-3.3.0-py3-none-any.whl", hash = "sha256:3da00b1d01314d85aac733bb997415d7a3e875666dc81735ddcf320aa36b7a70", size = 58363 }, - { url = "https://files.pythonhosted.org/packages/d6/7f/1d7b8ad86c2a841d940df7b965fa727e052b95d539e4c563da685c25d0d2/pyenchant-3.3.0-py3-none-win32.whl", hash = "sha256:1d55e075645a6edbb3c590fb42f9e02b4d455e4affe28a2227d5cb6d4868e626", size = 37787278 }, - { url = "https://files.pythonhosted.org/packages/ad/ae/5624803b62ecb0a20248f0d28ed3f78c78746a032582a016d4b2890c7899/pyenchant-3.3.0-py3-none-win_amd64.whl", hash = "sha256:04a5bd0e022ebe2e8c6d9e498ec3d650602e264ec5486e9c6a1b7f99c9507c49", size = 37427576 }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, -] - -[[package]] -name = "pynndescent" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "llvmlite" }, - { name = "numba" }, - { name = "scikit-learn" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4a/fb/7f58c397fb31666756457ee2ac4c0289ef2daad57f4ae4be8dec12f80b03/pynndescent-0.6.0.tar.gz", hash = "sha256:7ffde0fb5b400741e055a9f7d377e3702e02250616834231f6c209e39aac24f5", size = 2992987 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/e6/94145d714402fd5ade00b5661f2d0ab981219e07f7db9bfa16786cdb9c04/pynndescent-0.6.0-py3-none-any.whl", hash = "sha256:dc8c74844e4c7f5cbd1e0cd6909da86fdc789e6ff4997336e344779c3d5538ef", size = 73511 }, -] - -[[package]] -name = "pyogrio" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "numpy" }, - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/d4/12f86b1ed09721363da4c09622464b604c851a9223fc0c6b393fb2012208/pyogrio-0.12.1.tar.gz", hash = "sha256:e548ab705bb3e5383693717de1e6c76da97f3762ab92522cb310f93128a75ff1", size = 303289 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/46/b2c2dcdfd88759b56f103365905fffb85e8b08c1db1ec7c8f8b4c4c26016/pyogrio-0.12.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:01b322dac2a258d24b024d1028dcaa03c9bb6d9c3988b86d298a64873d10dc65", size = 23670744 }, - { url = "https://files.pythonhosted.org/packages/d9/21/b69f1bc51d805c00dd7c484a18e1fd2e75b41da1d9f5b8591d7d9d4a7d2f/pyogrio-0.12.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:e10087abcbd6b7e8212560a7002984e5078ac7b3a969ddc2c9929044dbb0d403", size = 25246184 }, - { url = "https://files.pythonhosted.org/packages/19/8c/b6aae08e8fcc4f2a903da5f6bd8f888d2b6d7290e54dde5abe15b4cca8df/pyogrio-0.12.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f6c621972b09fd81a32317e742c69ff4a7763a803da211361a78317f9577765", size = 31434449 }, - { url = "https://files.pythonhosted.org/packages/70/f9/9538fa893c29a3fdfeddf3b4c9f8db77f2d4134bc766587929fec8405ebf/pyogrio-0.12.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c38253427b688464caad5316d4ebcec116b5e13f1f02cc4e3588502f136ca1b4", size = 30987586 }, - { url = "https://files.pythonhosted.org/packages/89/a4/0aef5837b4e11840f501e48e01c31242838476c4f4aff9c05e228a083982/pyogrio-0.12.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5f47787251de7ce13cc06038da93a1214dc283cbccf816be6e03c080358226c8", size = 32534386 }, - { url = "https://files.pythonhosted.org/packages/34/97/e8f2ed8a339152b86f8403c258ae5d5f23ab32d690eeb0545bb3473d0c69/pyogrio-0.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:c1d756cf2da4cdf5609779f260d1e1e89be023184225855d6f3dcd33bbe17cb0", size = 22941718 }, - { url = "https://files.pythonhosted.org/packages/ad/e0/656b6536549d41b5aec57e0deca1f269b4f17532f0636836f587e581603a/pyogrio-0.12.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:7a0d5ca39184030aec4cde30f4258f75b227a854530d2659babc8189d76e657d", size = 23661857 }, - { url = "https://files.pythonhosted.org/packages/14/78/313259e40da728bdb60106ffdc7ea8224d164498cb838ecb79b634aab967/pyogrio-0.12.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:feaff42bbe8087ca0b30e33b09d1ce049ca55fe83ad83db1139ef37d1d04f30c", size = 25237106 }, - { url = "https://files.pythonhosted.org/packages/8f/ca/5368571a8b00b941ccfbe6ea29a5566aaffd45d4eb1553b956f7755af43e/pyogrio-0.12.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81096a5139532de5a8003ef02b41d5d2444cb382a9aecd1165b447eb549180d3", size = 31417048 }, - { url = "https://files.pythonhosted.org/packages/ef/85/6eeb875f27bf498d657eb5dab9f58e4c48b36c9037122787abee9a1ba4ba/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:41b78863f782f7a113ed0d36a5dc74d59735bd3a82af53510899bb02a18b06bb", size = 30952115 }, - { url = "https://files.pythonhosted.org/packages/36/f7/cf8bec9024625947e1a71441906f60a5fa6f9e4c441c4428037e73b1fcc8/pyogrio-0.12.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8b65be8c4258b27cc8f919b21929cecdadda4c353e3637fa30850339ef4d15c5", size = 32537246 }, - { url = "https://files.pythonhosted.org/packages/ab/10/7c9f5e428273574e69f217eba3a6c0c42936188ad4dcd9e2c41ebb711188/pyogrio-0.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:1291b866c2c81d991bda15021b08b3621709b40ee3a85689229929e9465788bf", size = 22933980 }, - { url = "https://files.pythonhosted.org/packages/be/56/f56e79f71b84aa9bea25fdde39fab3846841bd7926be96f623eb7253b7e1/pyogrio-0.12.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:ec0e47a5a704e575092b2fd5c83fa0472a1d421e590f94093eb837bb0a11125d", size = 23658483 }, - { url = "https://files.pythonhosted.org/packages/66/ac/5559f8a35d58a16cbb2dd7602dd11936ff8796d8c9bf789f14da88764ec3/pyogrio-0.12.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:b4c888fc08f388be4dd99dfca5e84a5cdc5994deeec0230cc45144d3460e2b21", size = 25232737 }, - { url = "https://files.pythonhosted.org/packages/59/58/925f1c129ddd7cbba8dea4e7609797cea7a76dbc863ac9afd318a679c4b9/pyogrio-0.12.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:73a88436f9962750d782853727897ac2722cac5900d920e39fab3e56d7a6a7f1", size = 31377986 }, - { url = "https://files.pythonhosted.org/packages/18/5f/c87034e92847b1844d0e8492a6a8e3301147d32c5e57909397ce64dbedf5/pyogrio-0.12.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b5d248a0d59fe9bbf9a35690b70004c67830ee0ebe7d4f7bb8ffd8659f684b3a", size = 30915791 }, - { url = "https://files.pythonhosted.org/packages/46/35/b874f79d03e9f900012cf609f7fff97b77164f2e14ee5aac282f8a999c1b/pyogrio-0.12.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0622bc1a186421547660271083079b38d42e6f868802936d8538c0b379f1ab6b", size = 32499754 }, - { url = "https://files.pythonhosted.org/packages/c3/c4/705678c9c4200130290b3a104b45c0cc10aaa48fcef3b2585b34e34ab3e1/pyogrio-0.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:207bd60c7ffbcea84584596e3637653aa7095e9ee20fa408f90c7f9460392613", size = 22933945 }, - { url = "https://files.pythonhosted.org/packages/f9/e0/d92d4944001330bc87742d43f112d63d12fc89378b6187e62ff3fc1e8e85/pyogrio-0.12.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1511b39a283fa27cda906cd187a791578942a87a40b6a06697d9b43bb8ac80b0", size = 23692697 }, - { url = "https://files.pythonhosted.org/packages/e5/d7/40acbe06d1b1140e3bb27b79e9163776469c1dc785f1be7d9a7fc7b95c87/pyogrio-0.12.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:e486cd6aa9ea8a15394a5f84e019d61ec18f257eeeb642348bd68c3d1e57280b", size = 25258083 }, - { url = "https://files.pythonhosted.org/packages/87/a1/39fefd9cddd95986700524f43d3093b4350f6e4fc200623c3838424a5080/pyogrio-0.12.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3f1a19f63bfd1d3042e45f37ad1d6598123a5a604b6c4ba3f38b419273486cd", size = 31368995 }, - { url = "https://files.pythonhosted.org/packages/18/d7/da88c566e67d741a03851eb8d01358949d52e0b0fc2cd953582dc6d89ff8/pyogrio-0.12.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:f3dcc59b3316b8a0f59346bcc638a4d69997864a4d21da839192f50c4c92369a", size = 31035589 }, - { url = "https://files.pythonhosted.org/packages/11/ac/8f0199f0d31b8ddbc4b4ea1918df8070fdf3e0a63100b898633ec9396224/pyogrio-0.12.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:a0643e041dee3e8e038fce69f52a915ecb486e6d7b674c0f9919f3c9e9629689", size = 32487973 }, - { url = "https://files.pythonhosted.org/packages/bd/64/8541a27e9635a335835d234dfaeb19d6c26097fd88224eda7791f83ca98d/pyogrio-0.12.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5881017f29e110d3613819667657844d8e961b747f2d35cf92f273c27af6d068", size = 22987374 }, - { url = "https://files.pythonhosted.org/packages/f4/6f/b4d5e285e08c0c60bcc23b50d73038ddc7335d8de79cc25678cd486a3db0/pyogrio-0.12.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5a1b0453d1c9e7b03715dd57296c8f3790acb8b50d7e3b5844b3074a18f50709", size = 23660673 }, - { url = "https://files.pythonhosted.org/packages/8d/75/4b29e71489c5551aa1a1c5ca8c5160a60203c94f2f68c87c0e3614d58965/pyogrio-0.12.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e7ee560422239dd09ca7f8284cc8483a8919c30d25f3049bb0249bff4c38dec4", size = 25232194 }, - { url = "https://files.pythonhosted.org/packages/89/6e/e9929d2261a07c36301983de2767bcde90d441ab5bf1d767ce56dd07f8b4/pyogrio-0.12.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:648c6f7f5f214d30e6cf493b4af1d59782907ac068af9119ca35f18153d6865a", size = 31336936 }, - { url = "https://files.pythonhosted.org/packages/1d/9e/c59941d734ed936d4e5c89b4b99cb5541307cc42b3fd466ee78a1850c177/pyogrio-0.12.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:58042584f3fd4cabb0f55d26c1405053f656be8a5c266c38140316a1e981aca0", size = 30902210 }, - { url = "https://files.pythonhosted.org/packages/d1/68/cc07320a63f9c2586e60bf11d148b00e12d0e707673bffe609bbdcb7e754/pyogrio-0.12.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b438e38e4ccbaedaa5cb5824ff5de5539315d9b2fde6547c1e816576924ee8ca", size = 32461674 }, - { url = "https://files.pythonhosted.org/packages/13/bc/e4522f429c45a3b6ad28185849dd76e5c8718b780883c4795e7ee41841ae/pyogrio-0.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:f1d8d8a2fea3781dc2a05982c050259261ebc0f6c5e03732d6d79d582adf9363", size = 23550575 }, - { url = "https://files.pythonhosted.org/packages/bd/ac/34f0664d0e391994a7b68529ae07a96432b2b4926dbac173ddc4ec94d310/pyogrio-0.12.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9fe7286946f35a73e6370dc5855bc7a5e8e7babf9e4a8bad7a3279a1d94c7ea9", size = 23694285 }, - { url = "https://files.pythonhosted.org/packages/8a/93/873255529faff1da09d0b27287e85ec805a318c60c0c74fd7df77f94e557/pyogrio-0.12.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2c50345b382f1be801d654ec22c70ee974d6057d4ba7afe984b55f2192bc94ee", size = 25259825 }, - { url = "https://files.pythonhosted.org/packages/27/95/4d4c3644695d99c6fa0b0b42f0d6266ae9dfaf64478a3371eaac950bdd02/pyogrio-0.12.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0db95765ac0ca935c7fe579e29451294e3ab19c317b0c59c31fbe92a69155e0", size = 31371995 }, - { url = "https://files.pythonhosted.org/packages/4c/6f/71f6bcca8754c8bf55a4b7153c61c91f8ac5ba992568e9fa3e54a0ee76fd/pyogrio-0.12.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fc882779075982b93064b3bf3d8642514a6df00d9dd752493b104817072cfb01", size = 31035498 }, - { url = "https://files.pythonhosted.org/packages/fd/47/75c1aa165a988347317afab9b938a01ad25dbca559b582ea34473703dc38/pyogrio-0.12.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:806f620e0c54b54dbdd65e9b6368d24f344cda84c9343364b40a57eb3e1c4dca", size = 32496390 }, - { url = "https://files.pythonhosted.org/packages/31/93/4641dc5d952f6bdb71dabad2c50e3f8a5d58396cdea6ff8f8a08bfd4f4a6/pyogrio-0.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5399f66730978d8852ef5f44dbafa0f738e7f28f4f784349f36830b69a9d2134", size = 23620996 }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 }, -] - -[[package]] -name = "pyproj" -version = "3.7.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/90/67bd7260b4ea9b8b20b4f58afef6c223ecb3abf368eb4ec5bc2cdef81b49/pyproj-3.7.2.tar.gz", hash = "sha256:39a0cf1ecc7e282d1d30f36594ebd55c9fae1fda8a2622cee5d100430628f88c", size = 226279 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/bd/f205552cd1713b08f93b09e39a3ec99edef0b3ebbbca67b486fdf1abe2de/pyproj-3.7.2-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:2514d61f24c4e0bb9913e2c51487ecdaeca5f8748d8313c933693416ca41d4d5", size = 6227022 }, - { url = "https://files.pythonhosted.org/packages/75/4c/9a937e659b8b418ab573c6d340d27e68716928953273e0837e7922fcac34/pyproj-3.7.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:8693ca3892d82e70de077701ee76dd13d7bca4ae1c9d1e739d72004df015923a", size = 4625810 }, - { url = "https://files.pythonhosted.org/packages/c0/7d/a9f41e814dc4d1dc54e95b2ccaf0b3ebe3eb18b1740df05fe334724c3d89/pyproj-3.7.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5e26484d80fea56273ed1555abaea161e9661d81a6c07815d54b8e883d4ceb25", size = 9638694 }, - { url = "https://files.pythonhosted.org/packages/ad/ab/9bdb4a6216b712a1f9aab1c0fcbee5d3726f34a366f29c3e8c08a78d6b70/pyproj-3.7.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:281cb92847814e8018010c48b4069ff858a30236638631c1a91dd7bfa68f8a8a", size = 9493977 }, - { url = "https://files.pythonhosted.org/packages/c9/db/2db75b1b6190f1137b1c4e8ef6a22e1c338e46320f6329bfac819143e063/pyproj-3.7.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9c8577f0b7bb09118ec2e57e3babdc977127dd66326d6c5d755c76b063e6d9dc", size = 10841151 }, - { url = "https://files.pythonhosted.org/packages/89/f7/989643394ba23a286e9b7b3f09981496172f9e0d4512457ffea7dc47ffc7/pyproj-3.7.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a23f59904fac3a5e7364b3aa44d288234af267ca041adb2c2b14a903cd5d3ac5", size = 10751585 }, - { url = "https://files.pythonhosted.org/packages/53/6d/ad928fe975a6c14a093c92e6a319ca18f479f3336bb353a740bdba335681/pyproj-3.7.2-cp311-cp311-win32.whl", hash = "sha256:f2af4ed34b2cf3e031a2d85b067a3ecbd38df073c567e04b52fa7a0202afde8a", size = 5908533 }, - { url = "https://files.pythonhosted.org/packages/79/e0/b95584605cec9ed50b7ebaf7975d1c4ddeec5a86b7a20554ed8b60042bd7/pyproj-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b7cb633565129677b2a183c4d807c727d1c736fcb0568a12299383056e67433", size = 6320742 }, - { url = "https://files.pythonhosted.org/packages/b7/4d/536e8f93bca808175c2d0a5ac9fdf69b960d8ab6b14f25030dccb07464d7/pyproj-3.7.2-cp311-cp311-win_arm64.whl", hash = "sha256:38b08d85e3a38e455625b80e9eb9f78027c8e2649a21dec4df1f9c3525460c71", size = 6245772 }, - { url = "https://files.pythonhosted.org/packages/8d/ab/9893ea9fb066be70ed9074ae543914a618c131ed8dff2da1e08b3a4df4db/pyproj-3.7.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:0a9bb26a6356fb5b033433a6d1b4542158fb71e3c51de49b4c318a1dff3aeaab", size = 6219832 }, - { url = "https://files.pythonhosted.org/packages/53/78/4c64199146eed7184eb0e85bedec60a4aa8853b6ffe1ab1f3a8b962e70a0/pyproj-3.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:567caa03021178861fad27fabde87500ec6d2ee173dd32f3e2d9871e40eebd68", size = 4620650 }, - { url = "https://files.pythonhosted.org/packages/b6/ac/14a78d17943898a93ef4f8c6a9d4169911c994e3161e54a7cedeba9d8dde/pyproj-3.7.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c203101d1dc3c038a56cff0447acc515dd29d6e14811406ac539c21eed422b2a", size = 9667087 }, - { url = "https://files.pythonhosted.org/packages/b8/be/212882c450bba74fc8d7d35cbd57e4af84792f0a56194819d98106b075af/pyproj-3.7.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1edc34266c0c23ced85f95a1ee8b47c9035eae6aca5b6b340327250e8e281630", size = 9552797 }, - { url = "https://files.pythonhosted.org/packages/ba/c0/c0f25c87b5d2a8686341c53c1792a222a480d6c9caf60311fec12c99ec26/pyproj-3.7.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa9f26c21bc0e2dc3d224cb1eb4020cf23e76af179a7c66fea49b828611e4260", size = 10837036 }, - { url = "https://files.pythonhosted.org/packages/5d/37/5cbd6772addde2090c91113332623a86e8c7d583eccb2ad02ea634c4a89f/pyproj-3.7.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9428b318530625cb389b9ddc9c51251e172808a4af79b82809376daaeabe5e9", size = 10775952 }, - { url = "https://files.pythonhosted.org/packages/69/a1/dc250e3cf83eb4b3b9a2cf86fdb5e25288bd40037ae449695550f9e96b2f/pyproj-3.7.2-cp312-cp312-win32.whl", hash = "sha256:b3d99ed57d319da042f175f4554fc7038aa4bcecc4ac89e217e350346b742c9d", size = 5898872 }, - { url = "https://files.pythonhosted.org/packages/4a/a6/6fe724b72b70f2b00152d77282e14964d60ab092ec225e67c196c9b463e5/pyproj-3.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:11614a054cd86a2ed968a657d00987a86eeb91fdcbd9ad3310478685dc14a128", size = 6312176 }, - { url = "https://files.pythonhosted.org/packages/5d/68/915cc32c02a91e76d02c8f55d5a138d6ef9e47a0d96d259df98f4842e558/pyproj-3.7.2-cp312-cp312-win_arm64.whl", hash = "sha256:509a146d1398bafe4f53273398c3bb0b4732535065fa995270e52a9d3676bca3", size = 6233452 }, - { url = "https://files.pythonhosted.org/packages/be/14/faf1b90d267cea68d7e70662e7f88cefdb1bc890bd596c74b959e0517a72/pyproj-3.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:19466e529b1b15eeefdf8ff26b06fa745856c044f2f77bf0edbae94078c1dfa1", size = 6214580 }, - { url = "https://files.pythonhosted.org/packages/35/48/da9a45b184d375f62667f62eba0ca68569b0bd980a0bb7ffcc1d50440520/pyproj-3.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c79b9b84c4a626c5dc324c0d666be0bfcebd99f7538d66e8898c2444221b3da7", size = 4615388 }, - { url = "https://files.pythonhosted.org/packages/5e/e7/d2b459a4a64bca328b712c1b544e109df88e5c800f7c143cfbc404d39bfb/pyproj-3.7.2-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ceecf374cacca317bc09e165db38ac548ee3cad07c3609442bd70311c59c21aa", size = 9628455 }, - { url = "https://files.pythonhosted.org/packages/f8/85/c2b1706e51942de19076eff082f8495e57d5151364e78b5bef4af4a1d94a/pyproj-3.7.2-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5141a538ffdbe4bfd157421828bb2e07123a90a7a2d6f30fa1462abcfb5ce681", size = 9514269 }, - { url = "https://files.pythonhosted.org/packages/34/38/07a9b89ae7467872f9a476883a5bad9e4f4d1219d31060f0f2b282276cbe/pyproj-3.7.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f000841e98ea99acbb7b8ca168d67773b0191de95187228a16110245c5d954d5", size = 10808437 }, - { url = "https://files.pythonhosted.org/packages/12/56/fda1daeabbd39dec5b07f67233d09f31facb762587b498e6fc4572be9837/pyproj-3.7.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8115faf2597f281a42ab608ceac346b4eb1383d3b45ab474fd37341c4bf82a67", size = 10745540 }, - { url = "https://files.pythonhosted.org/packages/0d/90/c793182cbba65a39a11db2ac6b479fe76c59e6509ae75e5744c344a0da9d/pyproj-3.7.2-cp313-cp313-win32.whl", hash = "sha256:f18c0579dd6be00b970cb1a6719197fceecc407515bab37da0066f0184aafdf3", size = 5896506 }, - { url = "https://files.pythonhosted.org/packages/be/0f/747974129cf0d800906f81cd25efd098c96509026e454d4b66868779ab04/pyproj-3.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:bb41c29d5f60854b1075853fe80c58950b398d4ebb404eb532536ac8d2834ed7", size = 6310195 }, - { url = "https://files.pythonhosted.org/packages/82/64/fc7598a53172c4931ec6edf5228280663063150625d3f6423b4c20f9daff/pyproj-3.7.2-cp313-cp313-win_arm64.whl", hash = "sha256:2b617d573be4118c11cd96b8891a0b7f65778fa7733ed8ecdb297a447d439100", size = 6230748 }, - { url = "https://files.pythonhosted.org/packages/aa/f0/611dd5cddb0d277f94b7af12981f56e1441bf8d22695065d4f0df5218498/pyproj-3.7.2-cp313-cp313t-macosx_13_0_x86_64.whl", hash = "sha256:d27b48f0e81beeaa2b4d60c516c3a1cfbb0c7ff6ef71256d8e9c07792f735279", size = 6241729 }, - { url = "https://files.pythonhosted.org/packages/15/93/40bd4a6c523ff9965e480870611aed7eda5aa2c6128c6537345a2b77b542/pyproj-3.7.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:55a3610d75023c7b1c6e583e48ef8f62918e85a2ae81300569d9f104d6684bb6", size = 4652497 }, - { url = "https://files.pythonhosted.org/packages/1b/ae/7150ead53c117880b35e0d37960d3138fe640a235feb9605cb9386f50bb0/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8d7349182fa622696787cc9e195508d2a41a64765da9b8a6bee846702b9e6220", size = 9942610 }, - { url = "https://files.pythonhosted.org/packages/d8/17/7a4a7eafecf2b46ab64e5c08176c20ceb5844b503eaa551bf12ccac77322/pyproj-3.7.2-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:d230b186eb876ed4f29a7c5ee310144c3a0e44e89e55f65fb3607e13f6db337c", size = 9692390 }, - { url = "https://files.pythonhosted.org/packages/c3/55/ae18f040f6410f0ea547a21ada7ef3e26e6c82befa125b303b02759c0e9d/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:237499c7862c578d0369e2b8ac56eec550e391a025ff70e2af8417139dabb41c", size = 11047596 }, - { url = "https://files.pythonhosted.org/packages/e6/2e/d3fff4d2909473f26ae799f9dda04caa322c417a51ff3b25763f7d03b233/pyproj-3.7.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8c225f5978abd506fd9a78eaaf794435e823c9156091cabaab5374efb29d7f69", size = 10896975 }, - { url = "https://files.pythonhosted.org/packages/f2/bc/8fc7d3963d87057b7b51ebe68c1e7c51c23129eee5072ba6b86558544a46/pyproj-3.7.2-cp313-cp313t-win32.whl", hash = "sha256:2da731876d27639ff9d2d81c151f6ab90a1546455fabd93368e753047be344a2", size = 5953057 }, - { url = "https://files.pythonhosted.org/packages/cc/27/ea9809966cc47d2d51e6d5ae631ea895f7c7c7b9b3c29718f900a8f7d197/pyproj-3.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f54d91ae18dd23b6c0ab48126d446820e725419da10617d86a1b69ada6d881d3", size = 6375414 }, - { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501 }, - { url = "https://files.pythonhosted.org/packages/42/17/c2b050d3f5b71b6edd0d96ae16c990fdc42a5f1366464a5c2772146de33a/pyproj-3.7.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:2aaa328605ace41db050d06bac1adc11f01b71fe95c18661497763116c3a0f02", size = 6214541 }, - { url = "https://files.pythonhosted.org/packages/03/68/68ada9c8aea96ded09a66cfd9bf87aa6db8c2edebe93f5bf9b66b0143fbc/pyproj-3.7.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:35dccbce8201313c596a970fde90e33605248b66272595c061b511c8100ccc08", size = 4617456 }, - { url = "https://files.pythonhosted.org/packages/81/e4/4c50ceca7d0e937977866b02cb64e6ccf4df979a5871e521f9e255df6073/pyproj-3.7.2-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:25b0b7cb0042444c29a164b993c45c1b8013d6c48baa61dc1160d834a277e83b", size = 9615590 }, - { url = "https://files.pythonhosted.org/packages/05/1e/ada6fb15a1d75b5bd9b554355a69a798c55a7dcc93b8d41596265c1772e3/pyproj-3.7.2-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:85def3a6388e9ba51f964619aa002a9d2098e77c6454ff47773bb68871024281", size = 9474960 }, - { url = "https://files.pythonhosted.org/packages/51/07/9d48ad0a8db36e16f842f2c8a694c1d9d7dcf9137264846bef77585a71f3/pyproj-3.7.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b1bccefec3875ab81eabf49059e2b2ea77362c178b66fd3528c3e4df242f1516", size = 10799478 }, - { url = "https://files.pythonhosted.org/packages/85/cf/2f812b529079f72f51ff2d6456b7fef06c01735e5cfd62d54ffb2b548028/pyproj-3.7.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d5371ca114d6990b675247355a801925814eca53e6c4b2f1b5c0a956336ee36e", size = 10710030 }, - { url = "https://files.pythonhosted.org/packages/99/9b/4626a19e1f03eba4c0e77b91a6cf0f73aa9cb5d51a22ee385c22812bcc2c/pyproj-3.7.2-cp314-cp314-win32.whl", hash = "sha256:77f066626030f41be543274f5ac79f2a511fe89860ecd0914f22131b40a0ec25", size = 5991181 }, - { url = "https://files.pythonhosted.org/packages/04/b2/5a6610554306a83a563080c2cf2c57565563eadd280e15388efa00fb5b33/pyproj-3.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a964da1696b8522806f4276ab04ccfff8f9eb95133a92a25900697609d40112", size = 6434721 }, - { url = "https://files.pythonhosted.org/packages/ae/ce/6c910ea2e1c74ef673c5d48c482564b8a7824a44c4e35cca2e765b68cfcc/pyproj-3.7.2-cp314-cp314-win_arm64.whl", hash = "sha256:e258ab4dbd3cf627809067c0ba8f9884ea76c8e5999d039fb37a1619c6c3e1f6", size = 6363821 }, - { url = "https://files.pythonhosted.org/packages/e4/e4/5532f6f7491812ba782a2177fe9de73fd8e2912b59f46a1d056b84b9b8f2/pyproj-3.7.2-cp314-cp314t-macosx_13_0_x86_64.whl", hash = "sha256:bbbac2f930c6d266f70ec75df35ef851d96fdb3701c674f42fd23a9314573b37", size = 6241773 }, - { url = "https://files.pythonhosted.org/packages/20/1f/0938c3f2bbbef1789132d1726d9b0e662f10cfc22522743937f421ad664e/pyproj-3.7.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:b7544e0a3d6339dc9151e9c8f3ea62a936ab7cc446a806ec448bbe86aebb979b", size = 4652537 }, - { url = "https://files.pythonhosted.org/packages/c7/a8/488b1ed47d25972f33874f91f09ca8f2227902f05f63a2b80dc73e7b1c97/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:f7f5133dca4c703e8acadf6f30bc567d39a42c6af321e7f81975c2518f3ed357", size = 9940864 }, - { url = "https://files.pythonhosted.org/packages/c7/cc/7f4c895d0cb98e47b6a85a6d79eaca03eb266129eed2f845125c09cf31ff/pyproj-3.7.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5aff3343038d7426aa5076f07feb88065f50e0502d1b0d7c22ddfdd2c75a3f81", size = 9688868 }, - { url = "https://files.pythonhosted.org/packages/b2/b7/c7e306b8bb0f071d9825b753ee4920f066c40fbfcce9372c4f3cfb2fc4ed/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b0552178c61f2ac1c820d087e8ba6e62b29442debddbb09d51c4bf8acc84d888", size = 11045910 }, - { url = "https://files.pythonhosted.org/packages/42/fb/538a4d2df695980e2dde5c04d965fbdd1fe8c20a3194dc4aaa3952a4d1be/pyproj-3.7.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:47d87db2d2c436c5fd0409b34d70bb6cdb875cca2ebe7a9d1c442367b0ab8d59", size = 10895724 }, - { url = "https://files.pythonhosted.org/packages/e8/8b/a3f0618b03957de9db5489a04558a8826f43906628bb0b766033aa3b5548/pyproj-3.7.2-cp314-cp314t-win32.whl", hash = "sha256:c9b6f1d8ad3e80a0ee0903a778b6ece7dca1d1d40f6d114ae01bc8ddbad971aa", size = 6056848 }, - { url = "https://files.pythonhosted.org/packages/bc/56/413240dd5149dd3291eda55aa55a659da4431244a2fd1319d0ae89407cfb/pyproj-3.7.2-cp314-cp314t-win_amd64.whl", hash = "sha256:1914e29e27933ba6f9822663ee0600f169014a2859f851c054c88cf5ea8a333c", size = 6517676 }, - { url = "https://files.pythonhosted.org/packages/15/73/a7141a1a0559bf1a7aa42a11c879ceb19f02f5c6c371c6d57fd86cefd4d1/pyproj-3.7.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d9d25bae416a24397e0d85739f84d323b55f6511e45a522dd7d7eae70d10c7e4", size = 6391844 }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216 }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249 }, -] - -[[package]] -name = "pytest-cov" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876 }, -] - -[[package]] -name = "pytest-mock" -version = "3.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095 }, -] - -[[package]] -name = "pytest-rerunfailures" -version = "16.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/74f8e685be7ecd1572c1256132f18fce3a665d7e07649a3f23b7eb2d3bec/pytest_rerunfailures-16.3.tar.gz", hash = "sha256:37c9b1231c8083e9f4e724f50f7a21241822f9516c15c700ebbf218d6452355c", size = 34148 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/98/58a71d68d3126d7f6a6ed1944c37ec207a4ff3dc66cad3bed7b59d38df61/pytest_rerunfailures-16.3-py3-none-any.whl", hash = "sha256:6bdfb8ffb46c46072e6c16bdedee38b6c13eac620d9415ed5b63152cbf283170", size = 15396 }, -] - -[[package]] -name = "pytest-timeout" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382 }, -] - -[[package]] -name = "pytest-xdist" -version = "3.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "execnet" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396 }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, -] - -[[package]] -name = "python-discovery" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a6/12/38c1a0b1e64806780c9563e3fc9f6e472251839662587cfbe9bfaf2ae10a/python_discovery-1.4.0.tar.gz", hash = "sha256:eb8bc7daad3c226c147e45bb4e970a1feb1bf4048ee178e6db59e197b8010ce3", size = 68455 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/8d/3d316429f65029532bb1e28ff77b797d86b5ac3915bb44ca4e19aa283d43/python_discovery-1.4.0-py3-none-any.whl", hash = "sha256:26ed78d703e234879a66244c7d4114563fb13ec5cd30a2d1357e5fb4850782da", size = 33217 }, -] - -[[package]] -name = "python-json-logger" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021 }, -] - -[[package]] -name = "pytz" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141 }, -] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 }, -] - -[[package]] -name = "pywinpty" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/c3/3e75075c7f71735f22b66fab0481f2c98e3a4d58cba55cb50ba29114bcf6/pywinpty-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:dff25a9a6435f527d7c65608a7e62783fc12076e7d44487a4911ee91be5a8ac8", size = 2114430 }, - { url = "https://files.pythonhosted.org/packages/8d/1e/8a54166a8c5e4f5cb516514bdf4090be4d51a71e8d9f6d98c0aa00fe45d4/pywinpty-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:fbc1e230e5b193eef4431cba3f39996a288f9958f9c9f092c8a961d930ee8f68", size = 236191 }, - { url = "https://files.pythonhosted.org/packages/7c/d4/aeb5e1784d2c5bff6e189138a9ca91a090117459cea0c30378e1f2db3d54/pywinpty-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c9081df0e49ffa86d15db4a6ba61530630e48707f987df42c9d3313537e81fc0", size = 2113098 }, - { url = "https://files.pythonhosted.org/packages/b9/53/7278223c493ccfe4883239cf06c823c56460a8010e0fc778eef67858dc14/pywinpty-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:15e79d870e18b678fb8a5a6105fd38496b55697c66e6fc0378236026bc4d59e9", size = 234901 }, - { url = "https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab", size = 2112686 }, - { url = "https://files.pythonhosted.org/packages/fd/50/724ed5c38c504d4e58a88a072776a1e880d970789deaeb2b9f7bd9a5141a/pywinpty-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:fe1f7911805127c94cf51f89ab14096c6f91ffdcacf993d2da6082b2142a2523", size = 234591 }, - { url = "https://files.pythonhosted.org/packages/f7/ad/90a110538696b12b39fd8758a06d70ded899308198ad2305ac68e361126e/pywinpty-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:3f07a6cf1c1d470d284e614733c3d0f726d2c85e78508ea10a403140c3c0c18a", size = 2112360 }, - { url = "https://files.pythonhosted.org/packages/44/0f/7ffa221757a220402bc79fda44044c3f2cc57338d878ab7d622add6f4581/pywinpty-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:15c7c0b6f8e9d87aabbaff76468dabf6e6121332c40fc1d83548d02a9d6a3759", size = 233107 }, - { url = "https://files.pythonhosted.org/packages/28/88/2ff917caff61e55f38bcdb27de06ee30597881b2cae44fbba7627be015c4/pywinpty-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:d4b6b7b0fe0cdcd02e956bd57cfe9f4e5a06514eecf3b5ae174da4f951b58be9", size = 2113282 }, - { url = "https://files.pythonhosted.org/packages/63/32/40a775343ace542cc43ece3f1d1fce454021521ecac41c4c4573081c2336/pywinpty-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:34789d685fc0d547ce0c8a65e5a70e56f77d732fa6e03c8f74fefb8cbb252019", size = 234207 }, - { url = "https://files.pythonhosted.org/packages/8d/54/5d5e52f4cb75028104ca6faf36c10f9692389b1986d34471663b4ebebd6d/pywinpty-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c37e224a47a971d1a6e08649a1714dac4f63c11920780977829ed5c8cadead1", size = 2112910 }, - { url = "https://files.pythonhosted.org/packages/0a/44/dcd184824e21d4620b06c7db9fbb15c3ad0a0f1fa2e6de79969fb82647ec/pywinpty-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c4e9c3dff7d86ba81937438d5819f19f385a39d8f592d4e8af67148ceb4f6ab5", size = 233425 }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, -] - -[[package]] -name = "pyzmq" -version = "27.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "implementation_name == 'pypy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328 }, - { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803 }, - { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836 }, - { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038 }, - { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531 }, - { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786 }, - { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220 }, - { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155 }, - { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428 }, - { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497 }, - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, - { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436 }, - { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301 }, - { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197 }, - { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275 }, - { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469 }, - { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961 }, - { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282 }, - { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468 }, - { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394 }, - { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964 }, - { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029 }, - { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541 }, - { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197 }, - { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175 }, - { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427 }, - { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929 }, - { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193 }, - { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388 }, - { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316 }, - { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472 }, - { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401 }, - { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170 }, - { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265 }, - { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208 }, - { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747 }, - { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371 }, - { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862 }, -] - -[[package]] -name = "rangehttpserver" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/36/3b/1ec139d6028c6e5eb10301f040d6eee5c5427a4b1b4d614a2f78d3bba1bd/rangehttpserver-1.4.0.tar.gz", hash = "sha256:d5ddccee219b359598e41da0c5fbf30a2579297094f5a682755e2586388a5306", size = 6993 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/43/d7e2b9ad768c07b5473bea3ac7db9ca4d995c09399cbea3d4df1c0bd4955/rangehttpserver-1.4.0-py2.py3-none-any.whl", hash = "sha256:2a0c6926e4341de4cc19ec861292b005e4194ff497b1eefdeccb2992a5045452", size = 7773 }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, -] - -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075 }, -] - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, -] - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, -] - -[[package]] -name = "rfc3987-syntax" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lark" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, -] - -[[package]] -name = "rich" -version = "15.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654 }, -] - -[[package]] -name = "roman-numerals" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676 }, -] - -[[package]] -name = "rpds-py" -version = "2026.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/43/25a8dcd3feedd735039a8f0b5b7e3b118232b5eae288c4fd9ab200d41094/rpds_py-2026.5.1.tar.gz", hash = "sha256:07b24fea40541e28570e5b795a4a38fbdcd12550c06bd0748005ecc8116ca256", size = 64459 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/a0/acf8b6fc20bfdcd3a45bd3f57680fb198e157b7e997b9123b10763798bd2/rpds_py-2026.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3397a5ed7174dc2786bb214030232fc36fe8e5584fec43a9952cc542b1a12036", size = 355609 }, - { url = "https://files.pythonhosted.org/packages/b6/95/f8203fd997484b1690a6869cd0e503b6c3c6be55b0ecc36d1a491fe742f0/rpds_py-2026.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:99ab6ba7bfa2cb0f96a04e3652355bf04e3f51aceb1e943b8541dab7ba4828cc", size = 348460 }, - { url = "https://files.pythonhosted.org/packages/33/8c/b47326ad2f0be545a5e5c1a55937a12afaea7d392ba2837bb9680f57e6c9/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0efbe45632665e53e3db8fe1e5692db58fc5cb9bab4459d570b83efefe11164", size = 381031 }, - { url = "https://files.pythonhosted.org/packages/22/0b/e83bbd97ffac6f6389b605cd4e1c8ac5761dc7e977769c9255d8c5adb7bd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:01d17b29c0c23d82b1f4751147ec49cf451f1fc2554eb9ef5f957e55d2656ead", size = 387121 }, - { url = "https://files.pythonhosted.org/packages/fd/0e/d285d1bc8864245919c61e1ca82263e4a66d337759c3a4cef72766ff9afc/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7559f72b94ae52659086c595dfa017cde03155f7832071d30959049052cb3ece", size = 501026 }, - { url = "https://files.pythonhosted.org/packages/86/06/ccb2109a1e543437b5e43816f2b43b9554cc6783145528a4e3711e05c011/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e25b7088f9ccbfc0dfcaa52bf969300ca229e10ecf758974ebcbb080a4b37bb", size = 391865 }, - { url = "https://files.pythonhosted.org/packages/3d/33/237173db1cfef10105b3839a24de00eb8d2a523711add4632447cdf0aedd/rpds_py-2026.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613fc4ee9eaef26dc5840666214dd6fbcebcf32f46e76f4abc473059f4e13dda", size = 378012 }, - { url = "https://files.pythonhosted.org/packages/97/64/1eae54e34d5161f9969295e80bd6b62a55f2b6ac5f2a5b60d02c2140e758/rpds_py-2026.5.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:85264a90ff4c05c1568dd65f5921c837614b67c60358fb4c17df3b7f2e90690a", size = 391111 }, - { url = "https://files.pythonhosted.org/packages/d8/34/5bb334a5a0f65d77869217c4654f34c78a7d11b93938a3c076a2edeafc52/rpds_py-2026.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe71bca7d547acb17027c7fd1624ff8aae623499c498d3e7011182c4de5c25e0", size = 409225 }, - { url = "https://files.pythonhosted.org/packages/16/0f/007ec21283b5b040b4ec3bd95e0402591e22bfa7d5c93dfe01c465c2d2d7/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05fa4f41f37ec97c9c260441a940450a192f78d774d2b097eee1379f1e1246a", size = 556487 }, - { url = "https://files.pythonhosted.org/packages/ff/10/5437c94508169b6b22d8418fef7a66e9ffb5f3b9e9c94460f2eedafe06ff/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df1d2a1996755b24b9ecee92cb4d36c28f86f464a6a173349c26bab41e94b8c2", size = 620798 }, - { url = "https://files.pythonhosted.org/packages/e0/d5/9937dce4d6bda74157b954e7d1460db05a22f5929dccfeeba1ed27a93df0/rpds_py-2026.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8895840ac4809e5f60c88fd07617cd71326e73d6e5a8aa783c5c0f7c24985de2", size = 584053 }, - { url = "https://files.pythonhosted.org/packages/6c/31/750617dd0ae1752471bf43f9e41d263398fae7cde7849d23b8574a70e617/rpds_py-2026.5.1-cp311-cp311-win32.whl", hash = "sha256:3684a59b158a7683aaeb8e25352e9a9dd2122cec78f2d8530266e4f91b4c7b3f", size = 214390 }, - { url = "https://files.pythonhosted.org/packages/3c/bb/3dcab0e1d9516303f2eb672a5d6f62eca5a69e2886301e9c8c54b520c39b/rpds_py-2026.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:7bd530e6a530bb3ea892f194fafa455f3516ac25ecf7143fd33c09be62b0470a", size = 231097 }, - { url = "https://files.pythonhosted.org/packages/49/d6/c6bbf5cb1cf12b9732df8074b57f6ef8341ba884c95d40632ae8bddb44e4/rpds_py-2026.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:0a5ae4dbe43c1076983b72616496919872ae7bbe7a1e21cc48336bc3154d130b", size = 226361 }, - { url = "https://files.pythonhosted.org/packages/d4/e7/a78582dc57caa592dcc7d4fb69b61390561e908eb3d2f5df5928a8e354c0/rpds_py-2026.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3abe24a66e57adcfa645d718063a5fa5103ecc71ddbf26d78af8f9368018ff1d", size = 353040 }, - { url = "https://files.pythonhosted.org/packages/a3/43/35e3f136343aef451e545ce8c38d36c2f93c0ed88703db8b64ba2b205c68/rpds_py-2026.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b1d94308ddf0b1982f61f2eb54bf92997c9ece8a8093ef014250f4a517906c", size = 345775 }, - { url = "https://files.pythonhosted.org/packages/20/e1/0f2160c5982d3157734d5cb3ed63d8b2d583a73c9864f77b666449f32cf8/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa92420128dadce7f54bd73ba1825a273e9268fe9e35dbf7e6362890efa4e08", size = 376329 }, - { url = "https://files.pythonhosted.org/packages/d0/11/ee0ba42aff83bf4effdbc576673c6be64c5e173978c3f6d537e94482f77d/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca653c6546386227cd9800d1bef6a348099acf8db4250341da6d90f663d6dfcb", size = 383539 }, - { url = "https://files.pythonhosted.org/packages/11/df/d94aa6a499d4ac40afe2d7620f2c597fd3c0f182e854ad7cf3f596a81cb6/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66c93681c4729e4e3ecba31b8179fae083ff3118841672835140338b4b9867c1", size = 494674 }, - { url = "https://files.pythonhosted.org/packages/1f/75/33d30f43bb2f458de11979486a591b1bf6e5651765ed1704c6197c2dc773/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40ff257542e04796880e011e15cd4dc21c2599975df2aaa8f2c8495ca574e1a5", size = 389268 }, - { url = "https://files.pythonhosted.org/packages/f4/1e/2c9096fc19d5fd084b0184ca2b651e659aa0a37e6fdbecf6ece47f147fe1/rpds_py-2026.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6825cc329b290e93c5f6a9be2393118a763f6ccf6abd83704e0c102ca583644", size = 376280 }, - { url = "https://files.pythonhosted.org/packages/b9/e5/61ec9f8be8211ea7f48448195549e4aaf02004083475493b0e137702ecb2/rpds_py-2026.5.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:de42116e69cb53b911cc34aee5ab98f36c597b822545045d49e938818b99e5e4", size = 387233 }, - { url = "https://files.pythonhosted.org/packages/0d/ca/bcec1005c4f4a234f92a29078631fee49206c7265ccae966f18fd332e80e/rpds_py-2026.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0f920015df2a504bebaba6d4c31ccf3fcf942f92655c086da30b671aad19aa6", size = 405009 }, - { url = "https://files.pythonhosted.org/packages/72/e6/4d5718c5cf26c522dc7c9999e238da1e77380b81d0c5d1df11e271ddfeb1/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0408a24e44feb919423dc6d9da677cb5cddb894d2ca9e763967d156d9c60fab4", size = 553113 }, - { url = "https://files.pythonhosted.org/packages/d4/25/2ee807bdb3e1f0b7eddf7782acd5665a8b5205a331a7d7244a52c4812fd9/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cea68bcd53467561ae2f96a6bdad1544299ba97b5b0ddcd5ac3d376e5c781c24", size = 618838 }, - { url = "https://files.pythonhosted.org/packages/6a/c1/7d4c26f167f8c41501cc073d30ee22082b16ce358cf5b00ec97cbc7804ea/rpds_py-2026.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4be8b1d2a705cc37d08256004e1d07de143fa0075c8e85a3df020b776f62b732", size = 582436 }, - { url = "https://files.pythonhosted.org/packages/04/1d/9d12b0a337bab46f4769f8857f4007e3b2d639e14f9a44a0efe157696e64/rpds_py-2026.5.1-cp312-cp312-win32.whl", hash = "sha256:6736718bd4fc49cbcb538ba30516fdbef161522acefb739657d48b97bd864fed", size = 212734 }, - { url = "https://files.pythonhosted.org/packages/c5/93/e4116f2de7f56bc7406a76033dc501811ddeb22b7f056b92d632871ebb0c/rpds_py-2026.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:0a7d1eec967df0e9b22614a5e177622e0c89611d03727fa0cb48e45028907870", size = 229045 }, - { url = "https://files.pythonhosted.org/packages/cb/53/6c3419d85eb2ec5938a37627c585b42d76a63bb731d6e42ed4b079ebf486/rpds_py-2026.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1841d067089e117142d79b98aa0df2f08b52f2ecc1819dd2700636c0db74a473", size = 223967 }, - { url = "https://files.pythonhosted.org/packages/6c/32/14c961ad295f490eb0849ada8b79683e93a59b9de3afdd983eaf55fa6867/rpds_py-2026.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:efef4ac29c6ff495531eb17ee705b62841ecaa291b7c7077e848ea03e237164d", size = 352787 }, - { url = "https://files.pythonhosted.org/packages/ca/bb/d1b85117967c11191441a7274ae616c65d93901d082c588f89a50a8da5ae/rpds_py-2026.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c39f5b67a8a2e67179ada2a954227d670fe65fa9098457f698f56ddf248709b3", size = 345179 }, - { url = "https://files.pythonhosted.org/packages/7c/46/d84105f062e626a1b233f863907288a4708c2d833b8b4c6fb2764bc080c0/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5c30f3f04eef4fbd362226a6f31d7c8895ca4fbb6e0b790f6890a98d8da8559", size = 376173 }, - { url = "https://files.pythonhosted.org/packages/e2/ae/469d7959ce5b1201e1de135dc735b86db3b35dd0d1734f6a44246d5f061c/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:277f6c82f0580848796c7ecc8a7173aa3bfb928e4ff831261c2f60a81dc270db", size = 383162 }, - { url = "https://files.pythonhosted.org/packages/dc/a2/57853d31a1116a561aa072794602ad3f6341e18d70a8523f1bd5b9fc1e5a/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63c2c4c213f1a4e3f3de28ecab029dbdee976324e729c0d7a55211be72576b02", size = 495093 }, - { url = "https://files.pythonhosted.org/packages/99/63/3a8eabcad9314b7daf5c65f451d2c33d989235cd8a5762186cf2c3f5a4f8/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3350ec808fb538fe71a1f94dfaa0e29c598dfad805ce49f0caec5ae3183c652b", size = 389829 }, - { url = "https://files.pythonhosted.org/packages/4b/25/05678d97fc25e2622df14dc530fb82023174ecfff6733991ed0d78f167bd/rpds_py-2026.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b964e3ab599e718dc46c018d104b1ebc007cbc6567d827c94a687fca56d77e", size = 374786 }, - { url = "https://files.pythonhosted.org/packages/88/d1/8c90b6431e80a3b91b284a5c7c8c0c4f9c006444d90477a740d6e0f9c694/rpds_py-2026.5.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:19cb09fab7b7fc96b2a6e28f2e34b72a3705ff27b37edb77455316e5d3f3dc9b", size = 386920 }, - { url = "https://files.pythonhosted.org/packages/ff/99/4638f672ab356682d633ee0da9255f5b67ce6efd0b85eb94ad3e255e65a5/rpds_py-2026.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abe76bcdba31e576cb83eeb8797aa0d882b738fef6dc65d0601fc753806a5b46", size = 405059 }, - { url = "https://files.pythonhosted.org/packages/66/3f/3546524b6eb4cc2e1f363a3d638fa52f6c24faae3500c25fb488b02f1740/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bff7073db3899158fff55ebf57b113a67030af26f80a18978f9f0aa60250ddf", size = 553030 }, - { url = "https://files.pythonhosted.org/packages/c6/c3/7b3388c796fcf471bd17194242d4dc1a7608567c0fa422bcc1c5e79f9c1e/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8ba264fa49be666cd9cc56bf34ec7002fb3d27a4aee5bcb4d43d0d18feb1bb6f", size = 618975 }, - { url = "https://files.pythonhosted.org/packages/61/1e/a3cb07f2795075d1d88efddae2f541359fde5f08c81ee114c29c2949c90a/rpds_py-2026.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4860b603ddda0475a8885499b3729e90229d480105b42651962a5397d995fa89", size = 581178 }, - { url = "https://files.pythonhosted.org/packages/a1/74/e758c03a5ef46f04c37f2651a2893db846d569ba8a7bca469d4b58939bcd/rpds_py-2026.5.1-cp313-cp313-win32.whl", hash = "sha256:7944270ae71383f6e2657dd7d5ce4eeb4ac2d0059a6738f0510583d462ab4842", size = 212481 }, - { url = "https://files.pythonhosted.org/packages/70/ec/a2aca432db9c7359b40fa393eeeaa0d166c2f70175be956e75fa24197c44/rpds_py-2026.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:88647f43a73c4e01be19b04ceef0c8d3a1958153604d13c773becd8016f2a0cf", size = 228519 }, - { url = "https://files.pythonhosted.org/packages/29/60/a73bfdd45b096574556acf303bbd9fa9eed36ca8a818b514e2a5d5fe2b9d/rpds_py-2026.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:453895624ecf7db7063b1004e44037522bbaef9ff6a945e59bc71662d7a03abd", size = 223446 }, - { url = "https://files.pythonhosted.org/packages/18/e2/408105fd611823f00882aea810f3989a30d26b1bab8b6beb20f98c724e0e/rpds_py-2026.5.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:b4e4bc98639ec915f512fde3aa7a95e0041d95d9c3cc86eea841fa63cb1e8600", size = 355287 }, - { url = "https://files.pythonhosted.org/packages/8d/58/5c4a43436843c90d0f6d19f82c200c80e3843ca9fa07b237623327f6d384/rpds_py-2026.5.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cacedb7a6e167680acba45ad5716e89067d225dc80da0d7040cae8c81d4572fa", size = 347033 }, - { url = "https://files.pythonhosted.org/packages/fb/c2/1a71acdacaf4e259b10278fb87b039ded3cf80041bcd89dd8a3ea702ded6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68700371c5d7ae1412862ddfa719090925c93ecf351c566d66f09d04b136ea00", size = 376891 }, - { url = "https://files.pythonhosted.org/packages/c2/c8/535f3d9b65addd8e28aa87b83c6e526799c3717a88273db8ea795beeef7a/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:296c799becfa849c779c8725494fe9ed94959ed886787df4364b058465bad7f0", size = 385646 }, - { url = "https://files.pythonhosted.org/packages/1c/91/dc033f313345c354ade914dbe73cdb90b615a4409ea02430d5356794f3d8/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3858b908218ee108d0bbfb2095ccc237648053c9bf98affad7cb079acaf1d97", size = 498830 }, - { url = "https://files.pythonhosted.org/packages/27/fc/90fcbea459dbb8ddc18a2e0fd1de9412b48bc84ffff2db771cf714bacfd6/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb8d2e7cb2f850b169806d61d1b991738acec96500a75c30f49caf064ce7cef", size = 392830 }, - { url = "https://files.pythonhosted.org/packages/b2/1d/46cd11a228c9750684a798d98f878be6f614aa762438da7378f035e79e35/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b74c10ed6a8f190f4287f53bcfea348b92a84a9c9f70d30183d1e6172d580d", size = 379613 }, - { url = "https://files.pythonhosted.org/packages/24/4a/d9b0c6af3a1de03eb93741bbe8be2bdce84d8fda8224f3005451d86df389/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b9a6528956191c48c52294a592dbd4a8386d7048bdb25c0efcb6b966466c6d83", size = 388183 }, - { url = "https://files.pythonhosted.org/packages/c5/b4/db7aaabdda6d020afc87d981bcc2f57a434c7dec60ecfc2ab3dd50b20351/rpds_py-2026.5.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:af03e34e860047bc7a352b842856fcf78798fbb81132cc98bd2f907ab4eb9cd2", size = 408578 }, - { url = "https://files.pythonhosted.org/packages/08/d6/070f6a41cbb343e2ac4171859bf3f3623e0ab002f72619d6d505313ec2de/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fea6e836d10abbe191d557d33bd58bd5987725fe63aa1eefe557d230209855bd", size = 553573 }, - { url = "https://files.pythonhosted.org/packages/75/ab/1a71ea3589c4345dac0a0518f0e6a031cb42689277851b683c46d27463a5/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:fc0c0f878ea770a0a8a462456c5ad36fc9fe6358e6b76fdadc7f17575e0b8bf1", size = 620861 }, - { url = "https://files.pythonhosted.org/packages/8a/22/9bf80a56069c0c443fcfefac639a86a744550a2898817a6dfd3e26654924/rpds_py-2026.5.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e0b360f316d966b048b085857630b3cc51f3db2f07b06f440eac8f695374d1e3", size = 585633 }, - { url = "https://files.pythonhosted.org/packages/da/68/3b2c0a75c9e04125696f84ebdbbf304acf5a40b58ba4481cdb98a922c3ba/rpds_py-2026.5.1-cp313-cp313t-win32.whl", hash = "sha256:a2999883eedf72fdfb7520b92c7d4ec2572a71ff40239377aa604cc529eecafc", size = 210074 }, - { url = "https://files.pythonhosted.org/packages/e7/8b/609157d5a25d37d4f29f92840ba531f416907c34ae5c5739dd21fc2bef98/rpds_py-2026.5.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e07be2a9d7122bd6e82dea89814ef8dc893feb1aae97fec1630f3263bbb30e55", size = 228635 }, - { url = "https://files.pythonhosted.org/packages/d4/6f/19c1918a4b590d8de87e712e4abe4b3875771eff60216fb6153cf6665c68/rpds_py-2026.5.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1f2c391c3059798093b65df23aca2cac150460ae9c630d99dec83d703d9485b9", size = 349756 }, - { url = "https://files.pythonhosted.org/packages/e5/60/a06fe7da34eca79dacbf958a2ba0c6eea85bc2b29de20080bf40f72f66fa/rpds_py-2026.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:413b424f7c4ee65ab5e5be91f5731be0f8b41a1ee2b12dfe810d716312e95a78", size = 343831 }, - { url = "https://files.pythonhosted.org/packages/bf/ec/b2333b97b90e2a6ef6ca8ad386ee284968e74bcfe113b3f1a8d9036429a9/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c595a1d9255dce0599e13130d1440ab2506654f2b50294226ee06402f8fef63", size = 375127 }, - { url = "https://files.pythonhosted.org/packages/14/7f/e00aae54067f2b488c4637961d5f58204d470795fc791085fa3f15060d2e/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c27c5f6102eac8c03e7595a00827a53b271ba40a53b59ff8709170e0855ea4a", size = 379034 }, - { url = "https://files.pythonhosted.org/packages/be/cc/423999bbb8ae8dc93c77fc1d5e984ade5eb89d237d3bb884ccfa72ae2890/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c7fcf61d44cacecaf3aea542b0e053db77972a4573e7ceda16fb2b399161195", size = 490823 }, - { url = "https://files.pythonhosted.org/packages/0f/aa/c671bf660f12e68d3c52ff86c7066ed1372df5a0f4f2ff584e419b8207e7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c817a189d4ee14290420e5ff051e4dd6baa13f3edf84685071dee07a6d538ee", size = 388144 }, - { url = "https://files.pythonhosted.org/packages/19/c8/d63bb75b68afe77b229e3021c6031bcaf01da5db5b0e69d0d10f9ba679a7/rpds_py-2026.5.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21846aac0ed2e0589f38c12dc44e77bb64e494b771eadbcf169cba00566ba7ba", size = 371959 }, - { url = "https://files.pythonhosted.org/packages/82/35/c51122014d8274ff37dc606d60049c3db7d83da02b5b282511e5a906a9a6/rpds_py-2026.5.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b317c87a13f769a4e787819bd508aaa5d69aa09b0880de9af6d3a8a54571cdec", size = 383558 }, - { url = "https://files.pythonhosted.org/packages/e3/f9/2790cb99c136a5363acdeacf5c27c56f3de0d4118a1f48fca83404c99c89/rpds_py-2026.5.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce87129d9f2c14fa6c4a8601fb80eb4488c80d38a20cd13758ef11123e14995d", size = 402789 }, - { url = "https://files.pythonhosted.org/packages/e5/1b/e4fb584f8c75d35c38150ff6a332cda949e6f97acba1f4fd123b14ab56fe/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9cdddb6c1207d284d94fd1530adf57fbd797fe7c4b8704ba85f49414f2557e7d", size = 551405 }, - { url = "https://files.pythonhosted.org/packages/d8/f7/a6731b4216cb3793ea1af5391da240f5683dacc0d13e034fe5fc3503f240/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4e237e139f94d3c036fd28eb9f564c99055476ff4ff05cd42be55ce349b5aa02", size = 616975 }, - { url = "https://files.pythonhosted.org/packages/2c/ea/2e051a81d95d8e63f4b35a1c463a87e8766bc3d083c067c5dfb6bf220747/rpds_py-2026.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ed0954b524873214369184a9c82b0eaa45a3fbb9a798cd95b17e0d98499e7ea0", size = 578701 }, - { url = "https://files.pythonhosted.org/packages/65/56/b5f6fdb2083e32bca8a8993d89e70db114b4756c9e2c38421328126689d2/rpds_py-2026.5.1-cp314-cp314-win32.whl", hash = "sha256:2d88621d6a7d4dfa633d21abe90f280bb205274e16b1d1e61c6ad4640b2453b7", size = 209806 }, - { url = "https://files.pythonhosted.org/packages/fb/80/65a5aa96c155e611d1ed844e4e1f57f3e36b021f396d9f8585d756e6b90d/rpds_py-2026.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:cef8ac28d26f4dda3533060c20fbf80a325458fa9fd23ea72a73cdfa8e978838", size = 225985 }, - { url = "https://files.pythonhosted.org/packages/27/7c/ad185212e87b05f196daef92bc5f3caf07298eb47c295b5585c3dd3093ac/rpds_py-2026.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:eaaea962c68cdc68d4a533ba985ab8e9484277910bbfaa2ab3ef7732667bfed8", size = 221219 }, - { url = "https://files.pythonhosted.org/packages/23/58/e14ae18759020334646b031e708ab4158d653a938822bfb7b95ef2e93aa3/rpds_py-2026.5.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:21942f52dbbd5f8758bf021213d28bd45c39e873e65e2407faf5f1846f5761ad", size = 352148 }, - { url = "https://files.pythonhosted.org/packages/31/9b/5f4a1e2f960bca3ac5d052b139dd31eed97b259f9d909173821760d542e8/rpds_py-2026.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f414556f6e3958300ff941e40c9f97e3dc9774ddd1b3434c475d73dd354bbed3", size = 345196 }, - { url = "https://files.pythonhosted.org/packages/1a/71/1d9574d6a2fa20ab60eaa55c7467f5aa20cbc770f341a05f09c0876f59e2/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1013a8625c74043210190b246f5b1551e09757c1f356c6e4160ef96c5bc081", size = 374981 }, - { url = "https://files.pythonhosted.org/packages/0c/9a/37e99f4915a80aa71670263c1267f7ae0af95f53a3f61e6c3bdc016d4515/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc68e231a77a5f0d774ae278a1f8e55c0456501820847c1e4efb3829f3441df6", size = 379961 }, - { url = "https://files.pythonhosted.org/packages/a8/ff/6e73f74b89d2e0715e0fc86b7dde893f9a61ae2f9b256ff3bdfe41ac4e94/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9baffb505aff33acc69b422a19f77806680f3c8632227d79f48de8a810d1c2c5", size = 495965 }, - { url = "https://files.pythonhosted.org/packages/ea/e0/425faba25f59d74d4638b267f7c7a80e8649d2ef4db10a19b0c4a71e6e6f/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8d2f912928d426e8cfa396f7f3f8d29a59e6689c86dcca3c420730c1096322b", size = 389526 }, - { url = "https://files.pythonhosted.org/packages/c6/76/7a41960e3fddae47fab43a28684d5da981401dffd88253de0944148654cb/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f628283be835db980c941767d41c9a27b5239e54ba0a9c1335247e82406964", size = 376190 }, - { url = "https://files.pythonhosted.org/packages/27/60/5f38dc70824fc6951b51d35377e577a3a3a4c81a6769cc5a2de25ebe0ad1/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:1ebb2f0ab7e16132995a72de805170e0203df0c3dd22e1ef1cd1fdd90bd7a131", size = 383921 }, - { url = "https://files.pythonhosted.org/packages/60/1a/d60a38caa1505f4b9483c3fbbde12c94e1079154f4f401a6da96f7e77621/rpds_py-2026.5.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3df3d16ded76f1f8c9cdebd0e1ea55fdf4c23b812de189814da7cf229c22a81", size = 404766 }, - { url = "https://files.pythonhosted.org/packages/87/ff/602fd3f174d6425f0bce05ad0dfbec0e96b38d0f7d08a79af5aa20083885/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9af8905b8f854990e40d5206aa5ac58d9b0fe0b7f351ff2bb086c20f6c8c6a47", size = 551343 }, - { url = "https://files.pythonhosted.org/packages/b8/c1/1be13327acdbead3eca1fde03b6a34dbb011f1e864e217f0d32cc1779a7f/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:036a36a87fb1cd3b214d11c4b3c4f7d2ddad933625dca1c900b56a057c07740a", size = 618502 }, - { url = "https://files.pythonhosted.org/packages/f3/d7/afb49b49d7f2be8b7ba1a9f0977fa5168003437b93086726f066544e8351/rpds_py-2026.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ae3853454fe9ef283a03c96c2d835d39e84b14643a9d62c82ef0fb87d702ca", size = 581916 }, - { url = "https://files.pythonhosted.org/packages/25/d1/dbef8c1f8a10f07beb62b5f054e20099fd9924b3ec001b8f0b6ac7813a85/rpds_py-2026.5.1-cp314-cp314t-win32.whl", hash = "sha256:6c3d771a46ec18b12af06ce36243a9a80b07a5d0515236332d90863ca8bb326a", size = 207855 }, - { url = "https://files.pythonhosted.org/packages/2a/72/bfa4e61ab8e7dc1c8adf397e05e6cbdd4239357bd72b248d3de662f23915/rpds_py-2026.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c93c629be4636cf54337bd5f06c104d55e42ced54d681f6fe21ae510a65116f6", size = 225422 }, - { url = "https://files.pythonhosted.org/packages/27/3a/7b5da92b640f67b6717ccafc83cdd06bfa7ff2395c3685c68922bb54d703/rpds_py-2026.5.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:3574b55c604b8f75dacb007136508bbc0db406e626301778096a133327e7f2fb", size = 349576 }, - { url = "https://files.pythonhosted.org/packages/d7/8a/2aafd7ad355a1bd48ca76e2262b74b15e6432b5a1efe150efd4d779cd55d/rpds_py-2026.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:94068eb3ae6d43f5a786b7db96a406a34e6d5c24489feef32fd6e8946ea7b291", size = 343640 }, - { url = "https://files.pythonhosted.org/packages/f7/7d/6c9523c1abbe840a1b7fba3c516d48e1d3487cc80fea4366c4071cf56784/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a5b10e8ce894825f380a8f1b6444cf73c294dfea62afbb2d13e3a9e630cec1", size = 375322 }, - { url = "https://files.pythonhosted.org/packages/5a/5d/0b7b03fb1dc509321f01de3149784ab773e34c8573022029af8076afcb9c/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc09f82e63d4bcd58149572f857a431bae851dc747e313c3b5bdf7abb907fda8", size = 379066 }, - { url = "https://files.pythonhosted.org/packages/d7/e2/8ef6012999ebf1cb1c22f876d9ce5e63d960fd4631d2af3202d3f480aa25/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e10464d17df3b582745c25cec695cb9558bca2cb6ddb631aee1787fc72c767b2", size = 494586 }, - { url = "https://files.pythonhosted.org/packages/80/af/1eeb029bec67582c226b7809172207cd005073af4ebd906e65ff494f4983/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba05adbf15d994c38ec0b7ab32e858e5110c21e9009a00a86545fd220f84e038", size = 388415 }, - { url = "https://files.pythonhosted.org/packages/18/23/ffbe10711c4d766c1cab0557d6906c074f795814863c67b351355d29354a/rpds_py-2026.5.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77c004fdc7b891967106f78ddfd7b076bfe6813c6139c6fff6aed3bcaa960b26", size = 372427 }, - { url = "https://files.pythonhosted.org/packages/bd/3a/30ba4a6ad457e5b070c18d742a33fb77d8d922b565cc881f8a5313d63bfe/rpds_py-2026.5.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:83bcf894486c9d78dd290d3c0124ff6dd8875d3025e2090a8ec49fcc37c55fdd", size = 383615 }, - { url = "https://files.pythonhosted.org/packages/d3/69/62e242b53ce39c0814bd24e1a6e6eba6c92be716277745f317f9540a2e7b/rpds_py-2026.5.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3df104083952a0e0c6f10de33e440eabe98fb6317d23e1a58c68f6df08d01b9", size = 402786 }, - { url = "https://files.pythonhosted.org/packages/38/c1/a770b9c186928a1ed0f7e6d7ae50e7f3950ed23e3f9e366dbc8e38cb55de/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:980450826cf22e133c57e0835070bdd0dd3f73b9b708c3ce223def2cb9469e14", size = 551583 }, - { url = "https://files.pythonhosted.org/packages/21/7c/68e8579b95375b70d2a963103c42e705856cdb98569258bd807f4423891c/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:205dde846f24332ab0c1188699a043b8d165b79bb84529ce272c45048ff6be01", size = 616941 }, - { url = "https://files.pythonhosted.org/packages/70/a1/a6135aed5730ff03ab957182259987ac11e55fb392a28dc6f0592048a280/rpds_py-2026.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:3966b82dd563176396df030f3dd52a6e54cb69b718e95e78bd555ed3d1e0185d", size = 578349 }, - { url = "https://files.pythonhosted.org/packages/09/6e/f24201a76a84e6c49d0bdfdfcb735210e21701e9b21c5bfc0ba497dd62f6/rpds_py-2026.5.1-cp315-cp315-win32.whl", hash = "sha256:7818f8d0a415be74d2be3590b0a1c1f463a642f4d0217e7d10602dceef5b79aa", size = 209922 }, - { url = "https://files.pythonhosted.org/packages/9e/e4/966bc240bb0485fc265278f6de44d05834bf0b3618886e0b22e33d54c49a/rpds_py-2026.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:b3cc20c0d800af78fd0fac68086e28c1856cec51ea528bb81ea851aa40d39325", size = 226003 }, - { url = "https://files.pythonhosted.org/packages/5c/5c/a15a59269cd5e74472734516c73795c15eccfc841b3d4b0228c3f53f19d0/rpds_py-2026.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:3609e9939a8a76cd904cf98a3f1f13b5dc7e150adeaee89e0ea09652ea213e16", size = 221245 }, - { url = "https://files.pythonhosted.org/packages/e0/22/135ce03804e179a71ceb13be095deda4a279bc88f7a6b8fa161c5ad44e12/rpds_py-2026.5.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:5d333a7127d4b307601ac37792bee01bb95c867cbfacf21b6375b804d6bbd723", size = 352015 }, - { url = "https://files.pythonhosted.org/packages/3b/5f/f1f6d2652eb9d848f6eb369d8db83a2da6249bb49ad2c2a48f45d54538d3/rpds_py-2026.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:b5f077b44a4f7808520f66dae234988d867deb9aed9be5da057ce9ba831b2a41", size = 345016 }, - { url = "https://files.pythonhosted.org/packages/88/66/b74182775691ea2290c99e52ac8d5db844e56fbec90ce421f107658c8314/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d8f9b7b78c9538fc9e04e82ec0e888ff0c3cffcfad152c77e57cd09351a98a", size = 374775 }, - { url = "https://files.pythonhosted.org/packages/ff/8f/15e5a61d9f0a43902d36561d4f07cae6ae9f4716be825159fd72717f33af/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e3a8ae58895ac107ed934a6bf51e5846f95c53b9b940c2c6d310838fd5846358", size = 380270 }, - { url = "https://files.pythonhosted.org/packages/02/c3/f859b12763a80540cdf2af0f15b19904cf756a71d7bdd3f82ff3e5b1bbf9/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0957cf3c2b8632ec7aaebffebea8005b353cc2a237b6e2ae3c2cac0820704cfb", size = 495285 }, - { url = "https://files.pythonhosted.org/packages/1c/c7/ff27c2ac8411d30b03b1829fd88cae8dad1a4d0da48dd25e57c4038042e6/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c396c1304de421050b3681ea70f371874b54d41b0151e96109758144c231e30b", size = 389581 }, - { url = "https://files.pythonhosted.org/packages/6e/67/fe92ee32a6cc05c77228a2f8b1762e7124f386ec20ff83d0757b762d58d0/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad1bff7f666b9598e573815affd666aac6a13a585dde336f843e33350c7fadc", size = 376041 }, - { url = "https://files.pythonhosted.org/packages/f8/91/b4d6685c27aba55bd82f25b278be8237038117d05f9659a6213ad3408130/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:656a042550878f12d45752452d47094b7cfe5ad1e9d7b87b5a22ad3ae5ff8015", size = 383946 }, - { url = "https://files.pythonhosted.org/packages/bd/79/2c1d832a53c8e0f8e98fc970ec257b950fecd4f62be2ab7182b500a0cbc8/rpds_py-2026.5.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c4bd4f70294737b5206a3e8e30ccadbf8a60301831c8ea23eec5dbeea1ecfa", size = 405526 }, - { url = "https://files.pythonhosted.org/packages/78/c4/c98117b03c6a8581ab2c2dfccfe9a5ad82bd8128a3c28b46a6ad2d97c393/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:43bca78665423cabae77146f2fe7ce55272b6c8d55d82cca83effd42c7e13972", size = 551165 }, - { url = "https://files.pythonhosted.org/packages/3b/c1/bc479ca069200af730881b1bd525e3114b2b391a351509fcb1b772f28086/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:42d0f20e85e549c870749d0e247f0c10d318a45b7e9676d575d2dcb04a1b2e66", size = 618778 }, - { url = "https://files.pythonhosted.org/packages/77/65/38ab2f90df44c2febfb63cc10ced40763d9b4bc94d173e734528663fe7f5/rpds_py-2026.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b1be5c35683684d5331b93600c210e8367c254683d8a6df6bd21bd2da3a334fb", size = 581839 }, - { url = "https://files.pythonhosted.org/packages/15/2d/ce1f605fe036aadd460e5822e578c6c7ec3a860936cca37d6e0f299daa77/rpds_py-2026.5.1-cp315-cp315t-win32.whl", hash = "sha256:75808f6c38ce7749bb68cc2770161aae5045e6c6f6781a9782e74b93304399df", size = 207866 }, - { url = "https://files.pythonhosted.org/packages/79/cb/966040123eb102371559746908ef2c9471f4d43e17ec9a645a2258dab64b/rpds_py-2026.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:90bd6630002a1c7f09e7843dd79f0d24f3d2897cc25a753480917865d14f15b3", size = 225441 }, - { url = "https://files.pythonhosted.org/packages/42/56/3fe0fb34820ff667be791b3a3c22b85e8bcba54e9c832f47438c191fa7be/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:edf2765d84e42447f112ad877af8fe1db0089aaec5b28e88d6eab45e7fe99cea", size = 357151 }, - { url = "https://files.pythonhosted.org/packages/8b/f2/3eb9ccdb9f143b8c9b003978898cb497f942a324c077401e6b8834238e63/rpds_py-2026.5.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ad3773236e95f7f33991eb125224b7da66f206504d032a253a02da7e134519fb", size = 350195 }, - { url = "https://files.pythonhosted.org/packages/a7/24/dbda232bc4f3ed732120692ab0d2c8402cb020516556d8bee622dcef2413/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04df86b3f0fade39ec8fd0e0aab089b1da9fbd2b48df778a57ef96f5e7d38df", size = 381850 }, - { url = "https://files.pythonhosted.org/packages/40/30/32e769839a358f78810c234f160f2cc21d1e4e47e1c0e0e0d535be5a0219/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6142dbd80c4df62a5d899f0d616d417f84e0bc8d32526c8e5589019d75d028a7", size = 387899 }, - { url = "https://files.pythonhosted.org/packages/ab/86/ec84d243aadb3b34b71dd26a010d0930b2d284ff5fc9a69fec53810ee6fd/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b35217adefe87f2fe4db7e9766cabe84744bfe9616d9667be18988928c7f2dc", size = 501618 }, - { url = "https://files.pythonhosted.org/packages/74/25/b60e52686bbff777a64f9e4f4d3dd57980dc846913777177a2c92e4937aa/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b95d5e11fc712b752081183a55a244c03cd00570489edd7014d8899f8ceb8162", size = 394003 }, - { url = "https://files.pythonhosted.org/packages/9b/c7/b3a6a588cc2219510ef3f42e207483a93950bedd1e3a0fd4015c95cff9e5/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141c9498daf2ace9eda35d2b0e376f9ea8b058d84f2aef4f96fccfd449a2f251", size = 379778 }, - { url = "https://files.pythonhosted.org/packages/31/00/c7dba3fc8a3da8cb3f6db1eb3386be4d79c2e97c6890d20eb9ac66ae8c43/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:6f249f8b860a200ad35193af961183ebe9132710484e6f6ce0cf89fd83c63a9a", size = 392359 }, - { url = "https://files.pythonhosted.org/packages/93/dd/472ba494c70753f93745992c99855bee0636daf74e6984e5e003f150316f/rpds_py-2026.5.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4abbf391a70be864920858bf360f4fb380577c9a0f732438a1996726e2c195b", size = 412820 }, - { url = "https://files.pythonhosted.org/packages/1d/6f/93831a3bfe789542ed0c1d0d74b78b440f055d6dc3ea4640eba2d95e6e23/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:c74005a7bb87752acf351c93897ec63ad77a07a0da7ecad9c050e32e7286ba34", size = 557243 }, - { url = "https://files.pythonhosted.org/packages/1f/ff/0b3d604614ffc77522c6b288fdbce68957eb583da1002aa65ba38ac0ee40/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:8213afbe8a3a906fb9acb2014423fe3359ee783d0bf90995f70623a3217bfa6c", size = 623541 }, - { url = "https://files.pythonhosted.org/packages/ea/ea/e7b0251441da9adfeaebcf29601d10f2a1455fcf0772fae9e7e19032bd96/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049", size = 586326 }, -] - -[[package]] -name = "ruff" -version = "0.15.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652 }, - { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615 }, - { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683 }, - { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064 }, - { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987 }, - { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100 }, - { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903 }, - { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550 }, - { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027 }, - { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041 }, - { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795 }, - { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117 }, - { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867 }, - { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101 }, - { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774 }, - { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463 }, - { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498 }, -] - -[[package]] -name = "s3fs" -version = "2026.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiobotocore" }, - { name = "aiohttp" }, - { name = "fsspec" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/d8/76f3dc1558bdf4494b117a9f7a9cc0a5d9d34edadc9e5d7ceabc5a6a7c37/s3fs-2026.4.0.tar.gz", hash = "sha256:5bdce0abb00b0435ee150807a45fea727451dbc22de4cbc116464f8504ab9d37", size = 85986 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/a4/9d1ea10ebc9e028a289a72fec84da170689549a8102c8aacfcad26bc5035/s3fs-2026.4.0-py3-none-any.whl", hash = "sha256:de0d2a1f33cdf03831fd2382d278c6e4e31fe57c3bf2f703c61f8aec6b703e2a", size = 32392 }, -] - -[[package]] -name = "scanpy" -version = "1.11.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "anndata", marker = "python_full_version < '3.12'" }, - { name = "h5py", marker = "python_full_version < '3.12'" }, - { name = "joblib", marker = "python_full_version < '3.12'" }, - { name = "legacy-api-wrap", marker = "python_full_version < '3.12'" }, - { name = "matplotlib", marker = "python_full_version < '3.12'" }, - { name = "natsort", marker = "python_full_version < '3.12'" }, - { name = "networkx", marker = "python_full_version < '3.12'" }, - { name = "numba", marker = "python_full_version < '3.12'" }, - { name = "numpy", marker = "python_full_version < '3.12'" }, - { name = "packaging", marker = "python_full_version < '3.12'" }, - { name = "pandas", marker = "python_full_version < '3.12'" }, - { name = "patsy", marker = "python_full_version < '3.12'" }, - { name = "pynndescent", marker = "python_full_version < '3.12'" }, - { name = "scikit-learn", marker = "python_full_version < '3.12'" }, - { name = "scipy", marker = "python_full_version < '3.12'" }, - { name = "seaborn", marker = "python_full_version < '3.12'" }, - { name = "session-info2", marker = "python_full_version < '3.12'" }, - { name = "statsmodels", marker = "python_full_version < '3.12'" }, - { name = "tqdm", marker = "python_full_version < '3.12'" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, - { name = "umap-learn", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/a8/285f1a9c995906b7e0ae3c399208fe67cfba8126dd31359dfef0908f6edc/scanpy-1.11.5.tar.gz", hash = "sha256:b2ef5476dfb1144b7dd0fae90b0198699c7988e6b27f083904150642c7ba6b89", size = 14088122 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/e9/c1d43543da87cd27e8e2a74db85cf0b6c5cff2d5f04a86bd584d2fbc2bb0/scanpy-1.11.5-py3-none-any.whl", hash = "sha256:fcd383ddcf7acbf7c0ca232c25ad51b00aec9f8d2f7c8954b8c6ee0962257166", size = 2097836 }, -] - -[package.optional-dependencies] -leiden = [ - { name = "igraph", marker = "python_full_version < '3.12'" }, - { name = "leidenalg", marker = "python_full_version < '3.12'" }, -] - -[[package]] -name = "scanpy" -version = "1.12.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "anndata", marker = "python_full_version >= '3.12'" }, - { name = "certifi", marker = "python_full_version >= '3.12'" }, - { name = "fast-array-utils", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, extra = ["accel", "sparse"], marker = "python_full_version >= '3.12'" }, - { name = "h5py", marker = "python_full_version >= '3.12'" }, - { name = "joblib", marker = "python_full_version >= '3.12'" }, - { name = "legacy-api-wrap", marker = "python_full_version >= '3.12'" }, - { name = "matplotlib", marker = "python_full_version >= '3.12'" }, - { name = "natsort", marker = "python_full_version >= '3.12'" }, - { name = "networkx", marker = "python_full_version >= '3.12'" }, - { name = "numba", marker = "python_full_version >= '3.12'" }, - { name = "numpy", marker = "python_full_version >= '3.12'" }, - { name = "packaging", marker = "python_full_version >= '3.12'" }, - { name = "pandas", marker = "python_full_version >= '3.12'" }, - { name = "patsy", marker = "python_full_version >= '3.12'" }, - { name = "pynndescent", marker = "python_full_version >= '3.12'" }, - { name = "scikit-learn", marker = "python_full_version >= '3.12'" }, - { name = "scipy", marker = "python_full_version >= '3.12'" }, - { name = "seaborn", marker = "python_full_version >= '3.12'" }, - { name = "session-info2", marker = "python_full_version >= '3.12'" }, - { name = "statsmodels", marker = "python_full_version >= '3.12'" }, - { name = "tqdm", marker = "python_full_version >= '3.12'" }, - { name = "typing-extensions", marker = "python_full_version == '3.12.*'" }, - { name = "umap-learn", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/11/18/1ed26bd2231d9bbe9bfe6ba678fdd261d1ec664ab1bca1d30401dc2a9193/scanpy-1.12.1.tar.gz", hash = "sha256:489c6a3763311721089bf0d78e65fd1555606b45f1a9ee97aa85291a12ade67a", size = 14418570 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/e4/b648ac3267001e1582e0b6ffc22b16dada83e206d783115992e83a07773f/scanpy-1.12.1-py3-none-any.whl", hash = "sha256:d482c92d0d7ae7b954a603e16dfcf5f062c511bd5e30e5dfd701c28e1d6b0e78", size = 2148436 }, -] - -[package.optional-dependencies] -leiden = [ - { name = "igraph", marker = "python_full_version >= '3.12'" }, - { name = "leidenalg", marker = "python_full_version >= '3.12'" }, -] - -[[package]] -name = "scikit-image" -version = "0.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "imageio" }, - { name = "lazy-loader" }, - { name = "networkx" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "scipy" }, - { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/16/8a407688b607f86f81f8c649bf0d68a2a6d67375f18c2d660aba20f5b648/scikit_image-0.26.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b1ede33a0fb3731457eaf53af6361e73dd510f449dac437ab54573b26788baf0", size = 12355510 }, - { url = "https://files.pythonhosted.org/packages/6b/f9/7efc088ececb6f6868fd4475e16cfafc11f242ce9ab5fc3557d78b5da0d4/scikit_image-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7af7aa331c6846bd03fa28b164c18d0c3fd419dbb888fb05e958ac4257a78fdd", size = 12056334 }, - { url = "https://files.pythonhosted.org/packages/9f/1e/bc7fb91fb5ff65ef42346c8b7ee8b09b04eabf89235ab7dbfdfd96cbd1ea/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea6207d9e9d21c3f464efe733121c0504e494dbdc7728649ff3e23c3c5a4953", size = 13297768 }, - { url = "https://files.pythonhosted.org/packages/a5/2a/e71c1a7d90e70da67b88ccc609bd6ae54798d5847369b15d3a8052232f9d/scikit_image-0.26.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74aa5518ccea28121f57a95374581d3b979839adc25bb03f289b1bc9b99c58af", size = 13711217 }, - { url = "https://files.pythonhosted.org/packages/d4/59/9637ee12c23726266b91296791465218973ce1ad3e4c56fc81e4d8e7d6e1/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d5c244656de905e195a904e36dbc18585e06ecf67d90f0482cbde63d7f9ad59d", size = 14337782 }, - { url = "https://files.pythonhosted.org/packages/e7/5c/a3e1e0860f9294663f540c117e4bf83d55e5b47c281d475cc06227e88411/scikit_image-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21a818ee6ca2f2131b9e04d8eb7637b5c18773ebe7b399ad23dcc5afaa226d2d", size = 14805997 }, - { url = "https://files.pythonhosted.org/packages/d3/c6/2eeacf173da041a9e388975f54e5c49df750757fcfc3ee293cdbbae1ea0a/scikit_image-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:9490360c8d3f9a7e85c8de87daf7c0c66507960cf4947bb9610d1751928721c7", size = 11878486 }, - { url = "https://files.pythonhosted.org/packages/c3/a4/a852c4949b9058d585e762a66bf7e9a2cd3be4795cd940413dfbfbb0ce79/scikit_image-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:0baa0108d2d027f34d748e84e592b78acc23e965a5de0e4bb03cf371de5c0581", size = 11346518 }, - { url = "https://files.pythonhosted.org/packages/99/e8/e13757982264b33a1621628f86b587e9a73a13f5256dad49b19ba7dc9083/scikit_image-0.26.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d454b93a6fa770ac5ae2d33570f8e7a321bb80d29511ce4b6b78058ebe176e8c", size = 12376452 }, - { url = "https://files.pythonhosted.org/packages/e3/be/f8dd17d0510f9911f9f17ba301f7455328bf13dae416560126d428de9568/scikit_image-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3409e89d66eff5734cd2b672d1c48d2759360057e714e1d92a11df82c87cba37", size = 12061567 }, - { url = "https://files.pythonhosted.org/packages/b3/2b/c70120a6880579fb42b91567ad79feb4772f7be72e8d52fec403a3dde0c6/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c717490cec9e276afb0438dd165b7c3072d6c416709cc0f9f5a4c1070d23a44", size = 13084214 }, - { url = "https://files.pythonhosted.org/packages/f4/a2/70401a107d6d7466d64b466927e6b96fcefa99d57494b972608e2f8be50f/scikit_image-0.26.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df650e79031634ac90b11e64a9eedaf5a5e06fcd09bcd03a34be01745744466", size = 13561683 }, - { url = "https://files.pythonhosted.org/packages/13/a5/48bdfd92794c5002d664e0910a349d0a1504671ef5ad358150f21643c79a/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cefd85033e66d4ea35b525bb0937d7f42d4cdcfed2d1888e1570d5ce450d3932", size = 14112147 }, - { url = "https://files.pythonhosted.org/packages/ee/b5/ac71694da92f5def5953ca99f18a10fe98eac2dd0a34079389b70b4d0394/scikit_image-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f5bf622d7c0435884e1e141ebbe4b2804e16b2dd23ae4c6183e2ea99233be70", size = 14661625 }, - { url = "https://files.pythonhosted.org/packages/23/4d/a3cc1e96f080e253dad2251bfae7587cf2b7912bcd76fd43fd366ff35a87/scikit_image-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:abed017474593cd3056ae0fe948d07d0747b27a085e92df5474f4955dd65aec0", size = 11911059 }, - { url = "https://files.pythonhosted.org/packages/35/8a/d1b8055f584acc937478abf4550d122936f420352422a1a625eef2c605d8/scikit_image-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d57e39ef67a95d26860c8caf9b14b8fb130f83b34c6656a77f191fa6d1d04d8", size = 11348740 }, - { url = "https://files.pythonhosted.org/packages/4f/48/02357ffb2cca35640f33f2cfe054a4d6d5d7a229b88880a64f1e45c11f4e/scikit_image-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a2e852eccf41d2d322b8e60144e124802873a92b8d43a6f96331aa42888491c7", size = 12346329 }, - { url = "https://files.pythonhosted.org/packages/67/b9/b792c577cea2c1e94cda83b135a656924fc57c428e8a6d302cd69aac1b60/scikit_image-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98329aab3bc87db352b9887f64ce8cdb8e75f7c2daa19927f2e121b797b678d5", size = 12031726 }, - { url = "https://files.pythonhosted.org/packages/07/a9/9564250dfd65cb20404a611016db52afc6268b2b371cd19c7538ea47580f/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915bb3ba66455cf8adac00dc8fdf18a4cd29656aec7ddd38cb4dda90289a6f21", size = 13094910 }, - { url = "https://files.pythonhosted.org/packages/a3/b8/0d8eeb5a9fd7d34ba84f8a55753a0a3e2b5b51b2a5a0ade648a8db4a62f7/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b36ab5e778bf50af5ff386c3ac508027dc3aaeccf2161bdf96bde6848f44d21b", size = 13660939 }, - { url = "https://files.pythonhosted.org/packages/2f/d6/91d8973584d4793d4c1a847d388e34ef1218d835eeddecfc9108d735b467/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09bad6a5d5949c7896c8347424c4cca899f1d11668030e5548813ab9c2865dcb", size = 14138938 }, - { url = "https://files.pythonhosted.org/packages/39/9a/7e15d8dc10d6bbf212195fb39bdeb7f226c46dd53f9c63c312e111e2e175/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aeb14db1ed09ad4bee4ceb9e635547a8d5f3549be67fc6c768c7f923e027e6cd", size = 14752243 }, - { url = "https://files.pythonhosted.org/packages/8f/58/2b11b933097bc427e42b4a8b15f7de8f24f2bac1fd2779d2aea1431b2c31/scikit_image-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac529eb9dbd5954f9aaa2e3fe9a3fd9661bfe24e134c688587d811a0233127f1", size = 11906770 }, - { url = "https://files.pythonhosted.org/packages/ad/ec/96941474a18a04b69b6f6562a5bd79bd68049fa3728d3b350976eccb8b93/scikit_image-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a2d211bc355f59725efdcae699b93b30348a19416cc9e017f7b2fb599faf7219", size = 11342506 }, - { url = "https://files.pythonhosted.org/packages/03/e5/c1a9962b0cf1952f42d32b4a2e48eed520320dbc4d2ff0b981c6fa508b6b/scikit_image-0.26.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9eefb4adad066da408a7601c4c24b07af3b472d90e08c3e7483d4e9e829d8c49", size = 12663278 }, - { url = "https://files.pythonhosted.org/packages/ae/97/c1a276a59ce8e4e24482d65c1a3940d69c6b3873279193b7ebd04e5ee56b/scikit_image-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6caec76e16c970c528d15d1c757363334d5cb3069f9cea93d2bead31820511f3", size = 12405142 }, - { url = "https://files.pythonhosted.org/packages/d4/4a/f1cbd1357caef6c7993f7efd514d6e53d8fd6f7fe01c4714d51614c53289/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07200fe09b9d99fcdab959859fe0f7db8df6333d6204344425d476850ce3604", size = 12942086 }, - { url = "https://files.pythonhosted.org/packages/5b/6f/74d9fb87c5655bd64cf00b0c44dc3d6206d9002e5f6ba1c9aeb13236f6bf/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92242351bccf391fc5df2d1529d15470019496d2498d615beb68da85fe7fdf37", size = 13265667 }, - { url = "https://files.pythonhosted.org/packages/a7/73/faddc2413ae98d863f6fa2e3e14da4467dd38e788e1c23346cf1a2b06b97/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:52c496f75a7e45844d951557f13c08c81487c6a1da2e3c9c8a39fcde958e02cc", size = 14001966 }, - { url = "https://files.pythonhosted.org/packages/02/94/9f46966fa042b5d57c8cd641045372b4e0df0047dd400e77ea9952674110/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20ef4a155e2e78b8ab973998e04d8a361d49d719e65412405f4dadd9155a61d9", size = 14359526 }, - { url = "https://files.pythonhosted.org/packages/5d/b4/2840fe38f10057f40b1c9f8fb98a187a370936bf144a4ac23452c5ef1baf/scikit_image-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c9087cf7d0e7f33ab5c46d2068d86d785e70b05400a891f73a13400f1e1faf6a", size = 12287629 }, - { url = "https://files.pythonhosted.org/packages/22/ba/73b6ca70796e71f83ab222690e35a79612f0117e5aaf167151b7d46f5f2c/scikit_image-0.26.0-cp313-cp313t-win_arm64.whl", hash = "sha256:27d58bc8b2acd351f972c6508c1b557cfed80299826080a4d803dd29c51b707e", size = 11647755 }, - { url = "https://files.pythonhosted.org/packages/51/44/6b744f92b37ae2833fd423cce8f806d2368859ec325a699dc30389e090b9/scikit_image-0.26.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:63af3d3a26125f796f01052052f86806da5b5e54c6abef152edb752683075a9c", size = 12365810 }, - { url = "https://files.pythonhosted.org/packages/40/f5/83590d9355191f86ac663420fec741b82cc547a4afe7c4c1d986bf46e4db/scikit_image-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ce00600cd70d4562ed59f80523e18cdcc1fae0e10676498a01f73c255774aefd", size = 12075717 }, - { url = "https://files.pythonhosted.org/packages/72/48/253e7cf5aee6190459fe136c614e2cbccc562deceb4af96e0863f1b8ee29/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6381edf972b32e4f54085449afde64365a57316637496c1325a736987083e2ab", size = 13161520 }, - { url = "https://files.pythonhosted.org/packages/73/c3/cec6a3cbaadfdcc02bd6ff02f3abfe09eaa7f4d4e0a525a1e3a3f4bce49c/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6624a76c6085218248154cc7e1500e6b488edcd9499004dd0d35040607d7505", size = 13684340 }, - { url = "https://files.pythonhosted.org/packages/d4/0d/39a776f675d24164b3a267aa0db9f677a4cb20127660d8bf4fd7fef66817/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f775f0e420faac9c2aa6757135f4eb468fb7b70e0b67fa77a5e79be3c30ee331", size = 14203839 }, - { url = "https://files.pythonhosted.org/packages/ee/25/2514df226bbcedfe9b2caafa1ba7bc87231a0c339066981b182b08340e06/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede4d6d255cc5da9faeb2f9ba7fedbc990abbc652db429f40a16b22e770bb578", size = 14770021 }, - { url = "https://files.pythonhosted.org/packages/8d/5b/0671dc91c0c79340c3fe202f0549c7d3681eb7640fe34ab68a5f090a7c7f/scikit_image-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:0660b83968c15293fd9135e8d860053ee19500d52bf55ca4fb09de595a1af650", size = 12023490 }, - { url = "https://files.pythonhosted.org/packages/65/08/7c4cb59f91721f3de07719085212a0b3962e3e3f2d1818cbac4eeb1ea53e/scikit_image-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:b8d14d3181c21c11170477a42542c1addc7072a90b986675a71266ad17abc37f", size = 11473782 }, - { url = "https://files.pythonhosted.org/packages/49/41/65c4258137acef3d73cb561ac55512eacd7b30bb4f4a11474cad526bc5db/scikit_image-0.26.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cde0bbd57e6795eba83cb10f71a677f7239271121dc950bc060482834a668ad1", size = 12686060 }, - { url = "https://files.pythonhosted.org/packages/e7/32/76971f8727b87f1420a962406388a50e26667c31756126444baf6668f559/scikit_image-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:163e9afb5b879562b9aeda0dd45208a35316f26cc7a3aed54fd601604e5cf46f", size = 12422628 }, - { url = "https://files.pythonhosted.org/packages/37/0d/996febd39f757c40ee7b01cdb861867327e5c8e5f595a634e8201462d958/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724f79fd9b6cb6f4a37864fe09f81f9f5d5b9646b6868109e1b100d1a7019e59", size = 12962369 }, - { url = "https://files.pythonhosted.org/packages/48/b4/612d354f946c9600e7dea012723c11d47e8d455384e530f6daaaeb9bf62c/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3268f13310e6857508bd87202620df996199a016a1d281b309441d227c822394", size = 13272431 }, - { url = "https://files.pythonhosted.org/packages/0a/6e/26c00b466e06055a086de2c6e2145fe189ccdc9a1d11ccc7de020f2591ad/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fac96a1f9b06cd771cbbb3cd96c5332f36d4efd839b1d8b053f79e5887acde62", size = 14016362 }, - { url = "https://files.pythonhosted.org/packages/47/88/00a90402e1775634043c2a0af8a3c76ad450866d9fa444efcc43b553ba2d/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c1e7bd342f43e7a97e571b3f03ba4c1293ea1a35c3f13f41efdc8a81c1dc8f2", size = 14364151 }, - { url = "https://files.pythonhosted.org/packages/da/ca/918d8d306bd43beacff3b835c6d96fac0ae64c0857092f068b88db531a7c/scikit_image-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b702c3bb115e1dcf4abf5297429b5c90f2189655888cbed14921f3d26f81d3a4", size = 12413484 }, - { url = "https://files.pythonhosted.org/packages/dc/cd/4da01329b5a8d47ff7ec3c99a2b02465a8017b186027590dc7425cee0b56/scikit_image-0.26.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0608aa4a9ec39e0843de10d60edb2785a30c1c47819b67866dd223ebd149acaf", size = 11769501 }, -] - -[[package]] -name = "scikit-learn" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835 }, - { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381 }, - { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632 }, - { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788 }, - { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706 }, - { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451 }, - { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242 }, - { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075 }, - { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492 }, - { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904 }, - { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359 }, - { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898 }, - { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770 }, - { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458 }, - { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341 }, - { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022 }, - { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409 }, - { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760 }, - { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045 }, - { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324 }, - { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651 }, - { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045 }, - { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994 }, - { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518 }, - { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667 }, - { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524 }, - { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133 }, - { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223 }, - { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518 }, - { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546 }, - { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305 }, - { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257 }, - { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673 }, - { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467 }, - { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395 }, - { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647 }, -] - -[[package]] -name = "scipy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675 }, - { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057 }, - { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032 }, - { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533 }, - { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057 }, - { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300 }, - { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333 }, - { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314 }, - { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512 }, - { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248 }, - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954 }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662 }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366 }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017 }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842 }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890 }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557 }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856 }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682 }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340 }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199 }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001 }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719 }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595 }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429 }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952 }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063 }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449 }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943 }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621 }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708 }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135 }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977 }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601 }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667 }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159 }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771 }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910 }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980 }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543 }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510 }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131 }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032 }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766 }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007 }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333 }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066 }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763 }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984 }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877 }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750 }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858 }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723 }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098 }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397 }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163 }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291 }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317 }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327 }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165 }, -] - -[[package]] -name = "scverse-misc" -version = "0.0.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "session-info2", marker = "python_full_version < '3.12'" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/05/6123a4362e2810ef216f152e249a66799f9c37975dabf89b69abb2d68c42/scverse_misc-0.0.3.tar.gz", hash = "sha256:18c46eeeac8ccef8f435e41a8ee86173b3d7ef6ea1167fde97a17553f70d3210", size = 23128 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/3e/9dad188e00d08f5381b95dd1f8f7b1dc9eb244250d93fea3e9cb7d0e61f0/scverse_misc-0.0.3-py3-none-any.whl", hash = "sha256:295702368db8f5fe946b5296135990492b3fc891a45bd6dd27798775db4c61c6", size = 8845 }, -] - -[[package]] -name = "scverse-misc" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "session-info2", marker = "python_full_version >= '3.12'" }, - { name = "typing-extensions", marker = "python_full_version == '3.12.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/6f/991549a8fc56c0e18e53b41819be8115eb767f0354e82bcba10d0eab9a0c/scverse_misc-0.0.7.tar.gz", hash = "sha256:d01467d806b4f783b52675451eccd913945d08d73609ea66470088b5446cca6a", size = 31565 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a5/a276bcdece26e2de16daa8de9e88979c40399f4ea6bdc352581c0a3f8b0f/scverse_misc-0.0.7-py3-none-any.whl", hash = "sha256:11c00be7b04fe903b6834df58159ae4910d4faae21356802a99303a18546f109", size = 13933 }, -] - -[[package]] -name = "seaborn" -version = "0.13.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "matplotlib" }, - { name = "numpy" }, - { name = "pandas" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/11/00d3c3dfc25ad54e731d91449895a79e4bf2384dc3ac01809010ba88f6d5/seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987", size = 294914 }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554 }, -] - -[[package]] -name = "send2trash" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, -] - -[[package]] -name = "session-info2" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/17/c6a81d91781734bd10d7842c32de665f34395b4db234abdc80dac271632b/session_info2-0.4.1.tar.gz", hash = "sha256:3bb2bf7b73b2e13a1737e9aa91a6dae55e2c49e83bee973f24245f31ae264a1f", size = 25207 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/d7/6893c9c2a52e4bcbeca2a2bf2aee970a686cc7bf555f97db13b00f35250e/session_info2-0.4.1-py3-none-any.whl", hash = "sha256:423b3f6bb7023433cfc3f791a6fdbb6a2cfbe226770ae6c127c3b2c4cf5a9d56", size = 17696 }, -] - -[[package]] -name = "setuptools" -version = "82.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223 }, -] - -[[package]] -name = "shapely" -version = "2.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/8d/1ff672dea9ec6a7b5d422eb6d095ed886e2e523733329f75fdcb14ee1149/shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618", size = 1820038 }, - { url = "https://files.pythonhosted.org/packages/4f/ce/28fab8c772ce5db23a0d86bf0adaee0c4c79d5ad1db766055fa3dab442e2/shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d", size = 1626039 }, - { url = "https://files.pythonhosted.org/packages/70/8b/868b7e3f4982f5006e9395c1e12343c66a8155c0374fdc07c0e6a1ab547d/shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09", size = 3001519 }, - { url = "https://files.pythonhosted.org/packages/13/02/58b0b8d9c17c93ab6340edd8b7308c0c5a5b81f94ce65705819b7416dba5/shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26", size = 3110842 }, - { url = "https://files.pythonhosted.org/packages/af/61/8e389c97994d5f331dcffb25e2fa761aeedfb52b3ad9bcdd7b8671f4810a/shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7", size = 4021316 }, - { url = "https://files.pythonhosted.org/packages/d3/d4/9b2a9fe6039f9e42ccf2cb3e84f219fd8364b0c3b8e7bbc857b5fbe9c14c/shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2", size = 4178586 }, - { url = "https://files.pythonhosted.org/packages/16/f6/9840f6963ed4decf76b08fd6d7fed14f8779fb7a62cb45c5617fa8ac6eab/shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6", size = 1543961 }, - { url = "https://files.pythonhosted.org/packages/38/1e/3f8ea46353c2a33c1669eb7327f9665103aa3a8dfe7f2e4ef714c210b2c2/shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc", size = 1722856 }, - { url = "https://files.pythonhosted.org/packages/24/c0/f3b6453cf2dfa99adc0ba6675f9aaff9e526d2224cbd7ff9c1a879238693/shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94", size = 1833550 }, - { url = "https://files.pythonhosted.org/packages/86/07/59dee0bc4b913b7ab59ab1086225baca5b8f19865e6101db9ebb7243e132/shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359", size = 1643556 }, - { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308 }, - { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844 }, - { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842 }, - { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714 }, - { url = "https://files.pythonhosted.org/packages/53/46/319c9dc788884ad0785242543cdffac0e6530e4d0deb6c4862bc4143dcf3/shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454", size = 1542745 }, - { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861 }, - { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644 }, - { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887 }, - { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931 }, - { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855 }, - { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960 }, - { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851 }, - { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890 }, - { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151 }, - { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130 }, - { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802 }, - { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460 }, - { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223 }, - { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760 }, - { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078 }, - { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178 }, - { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756 }, - { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290 }, - { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463 }, - { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145 }, - { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806 }, - { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803 }, - { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301 }, - { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247 }, - { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019 }, - { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137 }, - { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884 }, - { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320 }, - { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931 }, - { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406 }, - { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511 }, - { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607 }, - { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682 }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, -] - -[[package]] -name = "slicerator" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/52/f38586b82b2935f8b59a09b0a79c545a22ed062e728c9418bafeb51f61e0/slicerator-1.1.0.tar.gz", hash = "sha256:44010a7f5cd87680c07213b5cabe81d1fb71252962943e5373ee7d14605d6046", size = 38283 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/ae/fa6cd331b364ad2bbc31652d025f5747d89cbb75576733dfdf8efe3e4d62/slicerator-1.1.0-py3-none-any.whl", hash = "sha256:167668d48c6d3a5ba0bd3d54b2688e81ee267dc20aef299e547d711e6f3c441a", size = 10274 }, -] - -[[package]] -name = "snowballstemmer" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/ee/67eef9600338e245ad7838230969a34c823ddbdbccc5e1fc43cd75b55bc9/snowballstemmer-3.1.0.tar.gz", hash = "sha256:fd9e34526b23340cd23ffea6c9f9760974ecc2c2ac9e1d81401443ccdb2a801f", size = 122523 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/83/ddbf4533c62dd32667ef1238952abef155f3d3391f5be69a352ad1638a42/snowballstemmer-3.1.0-py3-none-any.whl", hash = "sha256:17e6d1da216aa07db6dad37139ea70cf13c4b2e9a096f6e64a9648fc657d3154", size = 104550 }, -] - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, -] - -[[package]] -name = "soupsieve" -version = "2.8.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304 }, -] - -[[package]] -name = "spatial-image" -version = "1.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "xarray" }, - { name = "xarray-dataclass" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4e/e7/d82eec7ceb782e17d60df9b68c78b2e13e86b143c4225198b99cfed9650f/spatial_image-1.2.3.tar.gz", hash = "sha256:b0bb8ed84c9c11de00b3bbed7041375048327e0ffcb233f61aca7a24dcd75537", size = 32254 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/5a/8ef888a4f56fa2ea5c10a7d6ff02286f503a93ea298bcaa9f51a41a20df8/spatial_image-1.2.3-py3-none-any.whl", hash = "sha256:b5280386a4d540c32c14cb4fdf74bd0242c99c168a7dac36204b29f8c27ce19a", size = 8737 }, -] - -[[package]] -name = "spatialdata" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anndata" }, - { name = "annsel" }, - { name = "click" }, - { name = "dask" }, - { name = "dask-image" }, - { name = "datashader" }, - { name = "distributed" }, - { name = "fsspec", extra = ["http", "s3"] }, - { name = "geopandas" }, - { name = "multiscale-spatial-image" }, - { name = "networkx" }, - { name = "numba" }, - { name = "numpy" }, - { name = "ome-zarr" }, - { name = "pandas" }, - { name = "pooch" }, - { name = "pyarrow" }, - { name = "rich" }, - { name = "scikit-image" }, - { name = "scipy" }, - { name = "setuptools" }, - { name = "shapely" }, - { name = "spatial-image" }, - { name = "typing-extensions" }, - { name = "universal-pathlib" }, - { name = "xarray" }, - { name = "xarray-spatial", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "xarray-spatial", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/50/248fae79a62f3d9dffbe70e9b66be6e3f8aaf77caa20a8c931a79256b689/spatialdata-0.7.3.tar.gz", hash = "sha256:6054d66d00e19af96368a897faa0b369f3a25dbf9e0371f091129f8522ee6986", size = 359317 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/bd/76cd29c982ed8f06db839411f1f44c521bf36b8714074652368353856b44/spatialdata-0.7.3-py3-none-any.whl", hash = "sha256:15e0df647f149a8be70cef6b866dcf033c71890440583bca8bb1c20c37566e6c", size = 199244 }, -] - -[[package]] -name = "spatialdata-plot" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "matplotlib" }, - { name = "matplotlib-scalebar" }, - { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "scikit-learn" }, - { name = "spatialdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b0/4c/cf5b141a624d09f6dbcae67d4fac611824a8c922834a5afb5d31b236dbdc/spatialdata_plot-0.4.0.tar.gz", hash = "sha256:2f92664fdfe7b6e91d1d1563a5468a78fd980e3369e85ae7dd8bd812ab16e0ed", size = 18971339 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/ab2141c09b5eda2b13c727d8ddaff56f364f41a2127f30260199629c65dc/spatialdata_plot-0.4.0-py3-none-any.whl", hash = "sha256:e64b713e97d05caf84516007002739c38b84bbd59450424f0c2d83593179b1ed", size = 114794 }, -] - -[[package]] -name = "spatialleiden" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "igraph" }, - { name = "leidenalg" }, - { name = "numpy" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/0b/ed7b63a2914379395bf3cc575ac2f1a32849d77eb50fc8d1922ab57c17f6/spatialleiden-0.4.0.tar.gz", hash = "sha256:f3f9080a5707ba2cb76c5afc0099173692781cddcbe499a193614b1cdf3cb67d", size = 27480 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/d6/4d62161e229d799faffd38326cfa483fa7750c05568df6b6a87640758925/spatialleiden-0.4.0-py3-none-any.whl", hash = "sha256:bb1e76a681d2663c28870af04fdd3060e374b037a1b8e542090755ff1d43741e", size = 20932 }, -] - -[[package]] -name = "sphinx" -version = "9.0.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "alabaster", marker = "python_full_version < '3.12'" }, - { name = "babel", marker = "python_full_version < '3.12'" }, - { name = "colorama", marker = "python_full_version < '3.12' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.12'" }, - { name = "imagesize", marker = "python_full_version < '3.12'" }, - { name = "jinja2", marker = "python_full_version < '3.12'" }, - { name = "packaging", marker = "python_full_version < '3.12'" }, - { name = "pygments", marker = "python_full_version < '3.12'" }, - { name = "requests", marker = "python_full_version < '3.12'" }, - { name = "roman-numerals", marker = "python_full_version < '3.12'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713 }, -] - -[[package]] -name = "sphinx" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.12'" }, - { name = "babel", marker = "python_full_version >= '3.12'" }, - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.12'" }, - { name = "imagesize", marker = "python_full_version >= '3.12'" }, - { name = "jinja2", marker = "python_full_version >= '3.12'" }, - { name = "packaging", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "requests", marker = "python_full_version >= '3.12'" }, - { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742 }, -] - -[[package]] -name = "sphinx-autodoc-annotation" -version = "1.0.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/2b/81f620d831cd0632ba7583723ab42e840ba766b86868307ee0877c40a2e2/sphinx-autodoc-annotation-1.0-1.tar.gz", hash = "sha256:4a3d03081efe1e5f2bc9b9d00746550f45b9f543b0c79519c523168ca7f7d89a", size = 4049 } - -[[package]] -name = "sphinx-autodoc-typehints" -version = "3.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/f6/bdd93582b2aaad2cfe9eb5695a44883c8bc44572dd3c351a947acbb13789/sphinx_autodoc_typehints-3.6.1.tar.gz", hash = "sha256:fa0b686ae1b85965116c88260e5e4b82faec3687c2e94d6a10f9b36c3743e2fe", size = 37563 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/6a/c0360b115c81d449b3b73bf74b64ca773464d5c7b1b77bda87c5e874853b/sphinx_autodoc_typehints-3.6.1-py3-none-any.whl", hash = "sha256:dd818ba31d4c97f219a8c0fcacef280424f84a3589cedcb73003ad99c7da41ca", size = 20869 }, -] - -[[package]] -name = "sphinx-autodoc-typehints" -version = "3.10.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/fd/44b8cf4465af19bf6684142f83b9b4123f6b97dc640fbdcd01f7fc01064e/sphinx_autodoc_typehints-3.10.3.tar.gz", hash = "sha256:01798d25041a16b95c5e053a45b133228e66ff37dbeef0ae8b3add4937aa41a2", size = 78698 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/30/4891d0503d4c32d857fb60ee1ea5b46f1bb13880866624f955bcb1dc9834/sphinx_autodoc_typehints-3.10.3-py3-none-any.whl", hash = "sha256:c6f9909dbdf81085f01b8a677f4da9accf97bd403d9bf820718fd5a745da355f", size = 40080 }, -] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343 }, -] - -[[package]] -name = "sphinx-design" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350 }, -] - -[[package]] -name = "sphinx-rtd-theme" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-jquery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/84/68/a1bfbf38c0f7bccc9b10bbf76b94606f64acb1552ae394f0b8285bfaea25/sphinx_rtd_theme-3.1.0.tar.gz", hash = "sha256:b44276f2c276e909239a4f6c955aa667aaafeb78597923b1c60babc76db78e4c", size = 7620915 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl", hash = "sha256:1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89", size = 7655617 }, -] - -[[package]] -name = "sphinx-tabs" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/30/ca5b0de830f369968d8e3483dd45a8908fd10169c05cd9837f0bd075982e/sphinx_tabs-3.5.0.tar.gz", hash = "sha256:91dba1187e4c35fd37380a56ac228bbd54c6c649b2351829f3bf033718277537", size = 17006 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/45/6adc5efeb19fd5fed4027e520b5c668ce58236a2b271ade5533c4c116276/sphinx_tabs-3.5.0-py3-none-any.whl", hash = "sha256:154be49de4d5c8249ea08c5d9bf88ca8f9c31e00a178305a93cbc33e000339e5", size = 9871 }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, -] - -[[package]] -name = "sphinxcontrib-bibtex" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "pybtex" }, - { name = "pybtex-docutils" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/15/6a/8e0b2c2420286389e7fed78ff361ec30e2f1d58c8560af8d64df5e7b61e0/sphinxcontrib_bibtex-2.7.0.tar.gz", hash = "sha256:fee700f7aae29bb8f654c62913f00d34ac44fc0b8ca0fa67ac922ff4453addee", size = 120669 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/c0/d28e62407f4733bbe0169287bc012f0ac3b4a2021066b285570654119c8b/sphinxcontrib_bibtex-2.7.0-py3-none-any.whl", hash = "sha256:28cf0ec7a957d1c7548d5749317ed472ce877e1b629f430f88e3789aa51f87b1", size = 40287 }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, -] - -[[package]] -name = "sphinxcontrib-jquery" -version = "4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, -] - -[[package]] -name = "sphinxcontrib-spelling" -version = "8.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyenchant" }, - { name = "requests" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/cd/fa8039cedce6295644ff5f03367742d21e7922da426e8c666b7f5a213682/sphinxcontrib_spelling-8.0.2.tar.gz", hash = "sha256:afbc7b8e93721ab88f12bdd39d848b92017b3763b9ed6226b4b0e54b06664fea", size = 30955 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c5/bcd32aa919c9e1652cca5bed6478202656a320c407793b547f8c16c179e3/sphinxcontrib_spelling-8.0.2-py3-none-any.whl", hash = "sha256:db8b3b2945683d49e87a8a5133d2b8ed4206cb593038b986ca8686a485f9980d", size = 14587 }, -] - -[[package]] -name = "sqlalchemy" -version = "2.0.50" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/5d/3172686af1770e4de2805f919a51441085f589ddadf3dd76ec582f84f497/sqlalchemy-2.0.50-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1aa6e403663a9c43c8fef7ce4bdb4cf48bcd8d352e91deda2a99f963270bd508", size = 2161366 }, - { url = "https://files.pythonhosted.org/packages/0f/90/e98dedea3c3e663a17afcd003a34ba45efdac2cea3b6f2e4585e2b1e2537/sqlalchemy-2.0.50-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51b637a84f9fa35ae1f9017e786cb142974a25305085e1b378b3647a67f65ad3", size = 3318926 }, - { url = "https://files.pythonhosted.org/packages/3b/4f/501308c2babb62c11753ecb4ee88ba9eef019419a4d6cbf7cb13e2bad353/sqlalchemy-2.0.50-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2dab927761d9108550f0cf8e66ff21af56f907a0ce0a689793db615e2b55f62c", size = 3319199 }, - { url = "https://files.pythonhosted.org/packages/ac/39/d88996c5e03ed6248c3a788d20f0b8d8b376b9f8a495e4bab9df7c72d2f8/sqlalchemy-2.0.50-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:545eae198d37bcf837a10ede3684e2af32458d6f35c597c35c2de7502dc38fc4", size = 3270301 }, - { url = "https://files.pythonhosted.org/packages/42/1b/1ae0e65161b51cc43e5ca75430ef79d80e23b5042d645586c2c342c3b92e/sqlalchemy-2.0.50-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fec460e18cdbb4c7773531122ce9a27e96c6ca17af3933941d94da475ad2c86", size = 3293465 }, - { url = "https://files.pythonhosted.org/packages/83/29/17c0003f2c0dfa6d1b97672475707e3ec5980db09defd7fa20beb6833bbd/sqlalchemy-2.0.50-cp311-cp311-win32.whl", hash = "sha256:e6e814658818fd165e749e3d8490ef16cc7f379a118c37ada8b0589ffbaaac22", size = 2120694 }, - { url = "https://files.pythonhosted.org/packages/c9/18/280d00654cc19d1fccf236fa5070f6dd04b84dde6f1b2e637bde0ff340a7/sqlalchemy-2.0.50-cp311-cp311-win_amd64.whl", hash = "sha256:1c5f858fe79c9f5d8fda065c06186356acb7f8df3cd52dbd5ee3f200e4b144f5", size = 2145315 }, - { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807 }, - { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358 }, - { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994 }, - { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399 }, - { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216 }, - { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194 }, - { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186 }, - { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697 }, - { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260 }, - { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280 }, - { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580 }, - { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375 }, - { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229 }, - { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126 }, - { url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519 }, - { url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063 }, - { url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069 }, - { url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453 }, - { url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413 }, - { url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063 }, - { url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830 }, - { url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435 }, - { url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059 }, - { url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366 }, - { url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879 }, - { url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117 }, - { url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823 }, - { url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794 }, - { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861 }, -] - -[[package]] -name = "squidpy" -source = { editable = "." } -dependencies = [ - { name = "aiohttp" }, - { name = "anndata" }, - { name = "cycler" }, - { name = "dask", extra = ["array"] }, - { name = "dask-image" }, - { name = "docrep" }, - { name = "fast-array-utils", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "fast-array-utils", version = "1.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "fsspec" }, - { name = "imagecodecs" }, - { name = "matplotlib" }, - { name = "matplotlib-scalebar" }, - { name = "networkx" }, - { name = "numba" }, - { name = "numpy" }, - { name = "omnipath" }, - { name = "pandas" }, - { name = "pillow" }, - { name = "pooch" }, - { name = "pyyaml" }, - { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "scikit-image" }, - { name = "scikit-learn" }, - { name = "spatialdata" }, - { name = "spatialdata-plot" }, - { name = "statsmodels" }, - { name = "tifffile", version = "2026.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "tifffile", version = "2026.5.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "tqdm" }, - { name = "validators" }, - { name = "xarray" }, - { name = "zarr", version = "3.1.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "zarr", version = "3.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, -] - -[package.optional-dependencies] -jax = [ - { name = "jax" }, -] -leiden = [ - { name = "leidenalg" }, - { name = "spatialleiden" }, -] - -[package.dev-dependencies] -dev = [ - { name = "hatch" }, - { name = "ipykernel" }, - { name = "ipywidgets" }, - { name = "jupyterlab" }, - { name = "jupytext" }, - { name = "notebook" }, - { name = "pre-commit" }, - { name = "ruff" }, -] -docs = [ - { name = "docutils" }, - { name = "ipython" }, - { name = "ipywidgets" }, - { name = "myst-nb" }, - { name = "nbsphinx" }, - { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "sphinx-autodoc-annotation" }, - { name = "sphinx-autodoc-typehints", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "sphinx-autodoc-typehints", version = "3.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinx-rtd-theme" }, - { name = "sphinx-tabs" }, - { name = "sphinxcontrib-bibtex" }, - { name = "sphinxcontrib-spelling" }, -] -test = [ - { name = "coverage", extra = ["toml"] }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "pytest-rerunfailures" }, - { name = "pytest-timeout" }, - { name = "pytest-xdist" }, - { name = "scanpy", version = "1.11.5", source = { registry = "https://pypi.org/simple" }, extra = ["leiden"], marker = "python_full_version < '3.12'" }, - { name = "scanpy", version = "1.12.1", source = { registry = "https://pypi.org/simple" }, extra = ["leiden"], marker = "python_full_version >= '3.12'" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiohttp", specifier = ">=3.8.1" }, - { name = "anndata", specifier = ">=0.9" }, - { name = "cycler", specifier = ">=0.11" }, - { name = "dask", extras = ["array"], specifier = ">=2021.2" }, - { name = "dask-image", specifier = ">=0.5" }, - { name = "docrep", specifier = ">=0.3.1" }, - { name = "fast-array-utils" }, - { name = "fsspec", specifier = ">=2021.11" }, - { name = "imagecodecs", specifier = ">=2025.8.2,<2026" }, - { name = "jax", marker = "extra == 'jax'" }, - { name = "leidenalg", marker = "extra == 'leiden'" }, - { name = "matplotlib", specifier = ">=3.3" }, - { name = "matplotlib-scalebar", specifier = ">=0.8" }, - { name = "networkx", specifier = ">=2.6" }, - { name = "numba", specifier = ">=0.56.4" }, - { name = "numpy", specifier = ">=1.23" }, - { name = "omnipath", specifier = ">=1.0.7" }, - { name = "pandas", specifier = ">=2.1" }, - { name = "pillow", specifier = ">=8" }, - { name = "pooch", specifier = ">=1.6" }, - { name = "pyyaml", specifier = ">=6" }, - { name = "scanpy", specifier = ">=1.9.3" }, - { name = "scikit-image", specifier = ">=0.25" }, - { name = "scikit-learn", specifier = ">=0.24" }, - { name = "spatialdata", specifier = ">=0.7.1" }, - { name = "spatialdata-plot", specifier = ">=0.3.3" }, - { name = "spatialleiden", marker = "extra == 'leiden'", specifier = ">=0.4" }, - { name = "statsmodels", specifier = ">=0.12" }, - { name = "tifffile", specifier = "!=2022.4.22" }, - { name = "tqdm", specifier = ">=4.50.2" }, - { name = "validators", specifier = ">=0.18.2" }, - { name = "xarray", specifier = ">=2024.10" }, - { name = "zarr", specifier = ">=3" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "hatch", specifier = ">=1.9" }, - { name = "ipykernel" }, - { name = "ipywidgets" }, - { name = "jupyterlab" }, - { name = "jupytext" }, - { name = "notebook" }, - { name = "pre-commit", specifier = ">=3" }, - { name = "ruff" }, -] -docs = [ - { name = "docutils", specifier = "<0.22" }, - { name = "ipython" }, - { name = "ipywidgets", specifier = ">=8" }, - { name = "myst-nb", specifier = ">=0.17.1" }, - { name = "nbsphinx", specifier = ">=0.8.1" }, - { name = "sphinx", specifier = ">=5.3" }, - { name = "sphinx-autodoc-annotation" }, - { name = "sphinx-autodoc-typehints", specifier = ">=1.10.3" }, - { name = "sphinx-copybutton", specifier = ">=0.5" }, - { name = "sphinx-design" }, - { name = "sphinx-rtd-theme" }, - { name = "sphinx-tabs" }, - { name = "sphinxcontrib-bibtex", specifier = ">=2.3" }, - { name = "sphinxcontrib-spelling", specifier = ">=7.6.2" }, -] -test = [ - { name = "coverage", extras = ["toml"], specifier = ">=7" }, - { name = "pytest", specifier = ">=7" }, - { name = "pytest-cov", specifier = ">=4" }, - { name = "pytest-mock", specifier = ">=3.5" }, - { name = "pytest-rerunfailures" }, - { name = "pytest-timeout", specifier = ">=2.1" }, - { name = "pytest-xdist", specifier = ">=3" }, - { name = "scanpy", extras = ["leiden"] }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, -] - -[[package]] -name = "statsmodels" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, - { name = "patsy" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/81/e8d74b34f85285f7335d30c5e3c2d7c0346997af9f3debf9a0a9a63de184/statsmodels-0.14.6.tar.gz", hash = "sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a", size = 20689085 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/4d/df4dd089b406accfc3bb5ee53ba29bb3bdf5ae61643f86f8f604baa57656/statsmodels-0.14.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ad5c2810fc6c684254a7792bf1cbaf1606cdee2a253f8bd259c43135d87cfb4", size = 10121514 }, - { url = "https://files.pythonhosted.org/packages/82/af/ec48daa7f861f993b91a0dcc791d66e1cf56510a235c5cbd2ab991a31d5c/statsmodels-0.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341fa68a7403e10a95c7b6e41134b0da3a7b835ecff1eb266294408535a06eb6", size = 10003346 }, - { url = "https://files.pythonhosted.org/packages/a9/2c/c8f7aa24cd729970728f3f98822fb45149adc216f445a9301e441f7ac760/statsmodels-0.14.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdf1dfe2a3ca56f5529118baf33a13efed2783c528f4a36409b46bbd2d9d48eb", size = 10129872 }, - { url = "https://files.pythonhosted.org/packages/40/c6/9ae8e9b0721e9b6eb5f340c3a0ce8cd7cce4f66e03dd81f80d60f111987f/statsmodels-0.14.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3764ba8195c9baf0925a96da0743ff218067a269f01d155ca3558deed2658ca", size = 10381964 }, - { url = "https://files.pythonhosted.org/packages/28/8c/cf3d30c8c2da78e2ad1f50ade8b7fabec3ff4cdfc56fbc02e097c4577f90/statsmodels-0.14.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e8d2e519852adb1b420e018f5ac6e6684b2b877478adf7fda2cfdb58f5acb5d", size = 10409611 }, - { url = "https://files.pythonhosted.org/packages/bf/cc/018f14ecb58c6cb89de9d52695740b7d1f5a982aa9ea312483ea3c3d5f77/statsmodels-0.14.6-cp311-cp311-win_amd64.whl", hash = "sha256:2738a00fca51196f5a7d44b06970ace6b8b30289839e4808d656f8a98e35faa7", size = 9580385 }, - { url = "https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5", size = 10091932 }, - { url = "https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c", size = 9997345 }, - { url = "https://files.pythonhosted.org/packages/48/f5/3a73b51e6450c31652c53a8e12e24eac64e3824be816c0c2316e7dbdcb7d/statsmodels-0.14.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c00a42863e4f4733ac9d078bbfad816249c01451740e6f5053ecc7db6d6368", size = 10058649 }, - { url = "https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d", size = 10339446 }, - { url = "https://files.pythonhosted.org/packages/56/4a/dce451c74c4050535fac1ec0c14b80706d8fc134c9da22db3c8a0ec62c33/statsmodels-0.14.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81e7dcc5e9587f2567e52deaff5220b175bf2f648951549eae5fc9383b62bc37", size = 10368705 }, - { url = "https://files.pythonhosted.org/packages/60/15/3daba2df40be8b8a9a027d7f54c8dedf24f0d81b96e54b52293f5f7e3418/statsmodels-0.14.6-cp312-cp312-win_amd64.whl", hash = "sha256:b5eb07acd115aa6208b4058211138393a7e6c2cf12b6f213ede10f658f6a714f", size = 9543991 }, - { url = "https://files.pythonhosted.org/packages/81/59/a5aad5b0cc266f5be013db8cde563ac5d2a025e7efc0c328d83b50c72992/statsmodels-0.14.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47ee7af083623d2091954fa71c7549b8443168f41b7c5dce66510274c50fd73e", size = 10072009 }, - { url = "https://files.pythonhosted.org/packages/53/dd/d8cfa7922fc6dc3c56fa6c59b348ea7de829a94cd73208c6f8202dd33f17/statsmodels-0.14.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa60d82e29fcd0a736e86feb63a11d2380322d77a9369a54be8b0965a3985f71", size = 9980018 }, - { url = "https://files.pythonhosted.org/packages/ee/77/0ec96803eba444efd75dba32f2ef88765ae3e8f567d276805391ec2c98c6/statsmodels-0.14.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89ee7d595f5939cc20bf946faedcb5137d975f03ae080f300ebb4398f16a5bd4", size = 10060269 }, - { url = "https://files.pythonhosted.org/packages/10/b9/fd41f1f6af13a1a1212a06bb377b17762feaa6d656947bf666f76300fc05/statsmodels-0.14.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:730f3297b26749b216a06e4327fe0be59b8d05f7d594fb6caff4287b69654589", size = 10324155 }, - { url = "https://files.pythonhosted.org/packages/ee/0f/a6900e220abd2c69cd0a07e3ad26c71984be6061415a60e0f17b152ecf08/statsmodels-0.14.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f1c08befa85e93acc992b72a390ddb7bd876190f1360e61d10cf43833463bc9c", size = 10349765 }, - { url = "https://files.pythonhosted.org/packages/98/08/b79f0c614f38e566eebbdcff90c0bcacf3c6ba7a5bbb12183c09c29ca400/statsmodels-0.14.6-cp313-cp313-win_amd64.whl", hash = "sha256:8021271a79f35b842c02a1794465a651a9d06ec2080f76ebc3b7adce77d08233", size = 9540043 }, - { url = "https://files.pythonhosted.org/packages/71/de/09540e870318e0c7b58316561d417be45eff731263b4234fdd2eee3511a8/statsmodels-0.14.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:00781869991f8f02ad3610da6627fd26ebe262210287beb59761982a8fa88cae", size = 10069403 }, - { url = "https://files.pythonhosted.org/packages/ab/f0/63c1bfda75dc53cee858006e1f46bd6d6f883853bea1b97949d0087766ca/statsmodels-0.14.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:73f305fbf31607b35ce919fae636ab8b80d175328ed38fdc6f354e813b86ee37", size = 9989253 }, - { url = "https://files.pythonhosted.org/packages/c1/98/b0dfb4f542b2033a3341aa5f1bdd97024230a4ad3670c5b0839d54e3dcab/statsmodels-0.14.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e443e7077a6e2d3faeea72f5a92c9f12c63722686eb80bb40a0f04e4a7e267ad", size = 10090802 }, - { url = "https://files.pythonhosted.org/packages/34/0e/2408735aca9e764643196212f9069912100151414dd617d39ffc72d77eee/statsmodels-0.14.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3414e40c073d725007a6603a18247ab7af3467e1af4a5e5a24e4c27bc26673b4", size = 10337587 }, - { url = "https://files.pythonhosted.org/packages/0f/36/4d44f7035ab3c0b2b6a4c4ebb98dedf36246ccbc1b3e2f51ebcd7ac83abb/statsmodels-0.14.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a518d3f9889ef920116f9fa56d0338069e110f823926356946dae83bc9e33e19", size = 10363350 }, - { url = "https://files.pythonhosted.org/packages/26/33/f1652d0c59fa51de18492ee2345b65372550501ad061daa38f950be390b6/statsmodels-0.14.6-cp314-cp314-win_amd64.whl", hash = "sha256:151b73e29f01fe619dbce7f66d61a356e9d1fe5e906529b78807df9189c37721", size = 9588010 }, -] - -[[package]] -name = "tabulate" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814 }, -] - -[[package]] -name = "tblib" -version = "3.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/8a/14c15ae154895cc131174f858c707790d416c444fc69f93918adfd8c4c0b/tblib-3.2.2.tar.gz", hash = "sha256:e9a652692d91bf4f743d4a15bc174c0b76afc750fe8c7b6d195cc1c1d6d2ccec", size = 35046 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/be/5d2d47b1fb58943194fb59dcf222f7c4e35122ec0ffe8c36e18b5d728f0b/tblib-3.2.2-py3-none-any.whl", hash = "sha256:26bdccf339bcce6a88b2b5432c988b266ebbe63a4e593f6b578b1d2e723d2b76", size = 12893 }, -] - -[[package]] -name = "terminado" -version = "0.18.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, -] - -[[package]] -name = "texttable" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/dc/0aff23d6036a4d3bf4f1d8c8204c5c79c4437e25e0ae94ffe4bbb55ee3c2/texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638", size = 12831 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768 }, -] - -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, -] - -[[package]] -name = "tifffile" -version = "2026.3.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "numpy", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/cb/2f6d79c7576e22c116352a801f4c3c8ace5957e9aced862012430b62e14f/tifffile-2026.3.3.tar.gz", hash = "sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7", size = 388745 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl", hash = "sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170", size = 243960 }, -] - -[[package]] -name = "tifffile" -version = "2026.5.15" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "numpy", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/66/0aef917d525767a40edebe088f8ed6a4417e6eb489c58f6805bfa872636b/tifffile-2026.5.15.tar.gz", hash = "sha256:ee4f3e07ee0d8ff4745a8c735ac2b72caa3173c7d6059b00fdc3ff492a0b635b", size = 429998 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/6e/7d8850ff112f8f80d394ca45e89b975a3a43559d47af3137b767669b3294/tifffile-2026.5.15-py3-none-any.whl", hash = "sha256:6715515a53cabc0cefc5c9f13a0ae2c250e63e2ca784ce02d0b6c333810c2a17", size = 266665 }, -] - -[[package]] -name = "tinycss2" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, -] - -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704 }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454 }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561 }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824 }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227 }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859 }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204 }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084 }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285 }, - { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924 }, - { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018 }, - { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948 }, - { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341 }, - { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159 }, - { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290 }, - { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141 }, - { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847 }, - { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088 }, - { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866 }, - { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887 }, - { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704 }, - { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628 }, - { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180 }, - { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674 }, - { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976 }, - { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755 }, - { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265 }, - { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726 }, - { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859 }, - { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713 }, - { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084 }, - { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973 }, - { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223 }, - { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973 }, - { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082 }, - { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490 }, - { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263 }, - { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736 }, - { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717 }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461 }, - { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855 }, - { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144 }, - { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683 }, - { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196 }, - { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393 }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583 }, -] - -[[package]] -name = "tomli-w" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675 }, -] - -[[package]] -name = "tomlkit" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/db/03eaf4331631ef6b27d6e3c9b68c54dc6f0d63d87201fed600cc409307fd/tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3", size = 161875 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328 }, -] - -[[package]] -name = "toolz" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093 }, -] - -[[package]] -name = "tornado" -version = "6.5.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/57/6d7303a77ae439d9189108f76c0c4fd89ee5e2cc8387bffb55232565c4ed/tornado-6.5.6.tar.gz", hash = "sha256:9a365179fe8ff6b8766f602c0f67c185d778193e9bdd828b19f0b6ed7764177d", size = 518139 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/0d/b4f481e18c5a51864e6d12b9a05ecf72919696680b747c958c3fc1f4fbae/tornado-6.5.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:65fcfaafb079435c2c19dc9e07c0f1cf0fa9051759ed0a7d0a3ba7ea7f64919c", size = 447737 }, - { url = "https://files.pythonhosted.org/packages/9e/9c/5430c39fcab1144d35860f457b15e9c08b4bc7ac86764354204e983d6183/tornado-6.5.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:38bc01b4acacded2de63ae78023548e41ebe6fbed3ec05a796d7ae3ad893887e", size = 445899 }, - { url = "https://files.pythonhosted.org/packages/8b/79/fa7e14a2f939c807a8d30619b4eb604eab219601b78792516ebe22d40cf9/tornado-6.5.6-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b942e6a137fda31ff54bf8e6e2c8d1c37f1f50583f3ed53fb840b53b9601d104", size = 448964 }, - { url = "https://files.pythonhosted.org/packages/a7/71/bd67d5f5199f937dafe03a49a37989f60f600ff6fef34c79412a829d97bd/tornado-6.5.6-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8666946e70171b8c3f1fc9b7876fac492e84822c4c7f3746f4e8f8bc9ac92a79", size = 449935 }, - { url = "https://files.pythonhosted.org/packages/cc/a4/c24388c9cf5b3c3a513b56a158af9f23092c9a2810d789e294310797df21/tornado-6.5.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c34cfab7ad6d104f052f55de06d39bbafc5885cfeb4da688803308dbcfa90b7", size = 449767 }, - { url = "https://files.pythonhosted.org/packages/a5/eb/6a07ad550c3f7b37244bd0becdf293ec3d3e961783d8b720a97df50de1b2/tornado-6.5.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:385f35e4e22fb52551dfcda4cdc8c30c61c2c001aef5ddad99cdfe116952efd3", size = 449174 }, - { url = "https://files.pythonhosted.org/packages/bb/84/3469e098dccdb6763130e06aacd786bb4363fca7b590a55c101ddf34ed30/tornado-6.5.6-cp39-abi3-win32.whl", hash = "sha256:db475f1b67b2809b10bb16264829087724ca8d24fe4ed47f7b8675cae453ef86", size = 450230 }, - { url = "https://files.pythonhosted.org/packages/d2/3c/273a04e0b9dd9016f1685cca0c1c8795a71ac88a34a8c889a0b443483226/tornado-6.5.6-cp39-abi3-win_amd64.whl", hash = "sha256:6739bf1e8eb09230f1280ddbd3236f0309db70f2c551a8dbc40f62babdf82f79", size = 450667 }, - { url = "https://files.pythonhosted.org/packages/02/98/0cffe22a224f60c5fb1e3aa0b76f9da2e1ca78b0e9545e3d077c68ce60a7/tornado-6.5.6-cp39-abi3-win_arm64.whl", hash = "sha256:2543597b24a695d72338a9a77818362d72387c03ae173f1f169eadc5c91466ac", size = 449690 }, -] - -[[package]] -name = "tqdm" -version = "4.67.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, -] - -[[package]] -name = "traitlets" -version = "5.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/22/40f55b26baeab80c2d7b3f1db0682f8954e4617fee7d90ce634022ef05c6/traitlets-5.15.0.tar.gz", hash = "sha256:4fead733f81cf1c4c938e06f8ca4633896833c9d89eff878159457f4d4392971", size = 163197 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877 }, -] - -[[package]] -name = "trove-classifiers" -version = "2026.5.22.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/b6/1c41aa221b157b624ea1a72e975404ef228724d249011ee411ac211a615e/trove_classifiers-2026.5.22.10.tar.gz", hash = "sha256:5477e9974e91904fb2cfa4a7581ab6e2f30c2c38d847fd00ed866080748101d5", size = 17061 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/02/9a14d3048ffa4f45b7c60956a9b22688dd925d6de50f6baf7e55f3664942/trove_classifiers-2026.5.22.10-py3-none-any.whl", hash = "sha256:01fe864225726e03efb843827ecabfe319fc4dee8dd66d65b8996cb09be46e2c", size = 14225 }, -] - -[[package]] -name = "typeguard" -version = "4.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/1c/dfba5c4633cafc4c701f237d2ba63b416805047fd6d96aab4cfc40969f98/typeguard-4.5.2.tar.gz", hash = "sha256:5a16dcac23502039299c97c8941651bc33d7ea8cc4b2f7d6bbb1b528f6eea423", size = 80240 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/29/74eeb4d3f3ae61ca096b018ad486b3b3c74b17bec09ab4edab721cbefec3/typeguard-4.5.2-py3-none-any.whl", hash = "sha256:fcf9de18bd945cdb4c7b996e12b4c51ce83f92f191314a6d7cf1739586ec98cf", size = 36748 }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, -] - -[[package]] -name = "tzdata" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321 }, -] - -[[package]] -name = "umap-learn" -version = "0.5.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numba" }, - { name = "numpy" }, - { name = "pynndescent" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/ee/af4171241117f85c74b5ca6448ea1033cc28d599c13651d67289bacd4083/umap_learn-0.5.12.tar.gz", hash = "sha256:6aff02ecac5f2aad9f3c65ee518d7ae93e1a985ae38721fdcffceee4232c33c7", size = 96672 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/98/f63318ccbe75c810011fe9233884c5d348d94d90005de1b79e5f93bef9c0/umap_learn-0.5.12-py3-none-any.whl", hash = "sha256:f2a85d2a2adcb52b541bed9b27a23ca169b56bb1b23283abeebfb8dfb8a42fe5", size = 91849 }, -] - -[[package]] -name = "universal-pathlib" -version = "0.3.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fsspec" }, - { name = "pathlib-abc" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/6e/d997a70ee8f4c61f9a7e2f4f8af721cf072a3326848fc881b05187e52558/universal_pathlib-0.3.10.tar.gz", hash = "sha256:4487cbc90730a48cfb64f811d99e14b6faed6d738420cd5f93f59f48e6930bfb", size = 261110 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/1a/5d9a402b39ec892d856bbdd9db502ff73ce28cdf4aff72eb1ce1d6843506/universal_pathlib-0.3.10-py3-none-any.whl", hash = "sha256:dfaf2fb35683d2eb1287a3ed7b215e4d6016aa6eaf339c607023d22f90821c66", size = 83528 }, -] - -[[package]] -name = "uri-template" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087 }, -] - -[[package]] -name = "userpath" -version = "1.9.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/b7/30753098208505d7ff9be5b3a32112fb8a4cb3ddfccbbb7ba9973f2e29ff/userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815", size = 11140 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/99/3ec6335ded5b88c2f7ed25c56ffd952546f7ed007ffb1e1539dc3b57015a/userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d", size = 9065 }, -] - -[[package]] -name = "uv" -version = "0.11.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/8e/ec34c19d0f254fcbcc5c1ce8c7f06e47e0f69a7e1a0269c1d59cb0b0f279/uv-0.11.17.tar.gz", hash = "sha256:1d1be74deec997db1dda05a7e67541c904d65cbfd72e455d3c0a2a1e4bf2cddf", size = 4203607 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/2e/e6d42f9d39009eee976f1e5dfd31d3d1943e6e593ad7b191cf11e9744a36/uv-0.11.17-py3-none-linux_armv6l.whl", hash = "sha256:8426bfe315564d414cbc5ba5467595dc6348965e19acec742914f47da3ff269f", size = 23551216 }, - { url = "https://files.pythonhosted.org/packages/d0/ee/d72bcc60f3585653a4b768425854d737d98d65c1765547d25c2999547ea9/uv-0.11.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6d1a033cc68cabb4141d6c1e3b66ffc6e970b98ba42e210f33270251e0bd8697", size = 22997377 }, - { url = "https://files.pythonhosted.org/packages/58/34/1bc69798d9ae998fbc42c61b02883f2ba00d04bdd858e589604d01846287/uv-0.11.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:58c07ffc272c847d29cd98ca5082fa4304a645f87c718ec900e3cca9026bd096", size = 21630197 }, - { url = "https://files.pythonhosted.org/packages/6b/93/1be48ec6a8933d9a77d0ce5240ed63f68869f68517ccf5d62268ed03f3e8/uv-0.11.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:036d6e2940afe8b79637530b01b9241d8cfd174b07f1179a1ebbd42409c38ca3", size = 23414940 }, - { url = "https://files.pythonhosted.org/packages/00/31/b7488ff49d80090ea9d05d67a4d381a1b4479502e9853e654caa1c1c678e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:283186700c3e65a4644a73a917232da7d3e4a94d25ea0377a44f5b263fa49577", size = 23096330 }, - { url = "https://files.pythonhosted.org/packages/fe/95/42b6137c5de06278d229c7eef2f314df2a738cd799795bbb44dace21bd6e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2e44dfbfc7778d0d90edc6738f237c91e5e37e4e3cfe94c8a312cec56a41485", size = 23101906 }, - { url = "https://files.pythonhosted.org/packages/17/7c/0ca03b2d19965db6d5dfe0c8cf96a3d0b424503c8cbc3cd2ffdc5869a15d/uv-0.11.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a817eeb3026f27a53d3f4b7855a5105f6787dd192140e201eda4d2b9a11b72e", size = 24444409 }, - { url = "https://files.pythonhosted.org/packages/b5/fb/179f55a3b19d47c30ec1f41b9b964da74dfa7053ff310a70a9c4d8cb998d/uv-0.11.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf8f5ad959583dcd2c4ae445c754a97c05700246ff89259f3fd285c9c20f4c00", size = 25540153 }, - { url = "https://files.pythonhosted.org/packages/f7/29/592f42012765c43ae45c112110e214bca7b0cfc08c4c1b52e1dfa47dedd5/uv-0.11.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce16892a45134d20165c1ceababe06f3e9ce6a58902db1eff812c8c93626823f", size = 24665906 }, - { url = "https://files.pythonhosted.org/packages/0e/51/b75808766f895248553c6370968509cd4f726e6943e310a8f7a171036ad0/uv-0.11.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da839e5a491c9a701d7d327a199cafc76ac27a03ac84fd2a8d4bf32c3af2448", size = 24863325 }, - { url = "https://files.pythonhosted.org/packages/ee/6a/6f27ee69e97f480104bb8ec335f04c2a12add98edfcc4844a68e9538b6e2/uv-0.11.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ec004b3c9bf9cb7756067ad1bd0bf64eb843e6fa2edbfbb3135ee152c14cea91", size = 23521674 }, - { url = "https://files.pythonhosted.org/packages/df/11/1344aca7c710f794750f74de0e552a54ab24193ecc01fa3b3ae22ff822a1/uv-0.11.17-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:659227cac719b618cc91e02be9e274ad5bd72d74fa278123e6373537e9f28216", size = 24224725 }, - { url = "https://files.pythonhosted.org/packages/ad/44/7b11550c1453ea13b81e549c83523e6ab6ed3231d09b2fd6b9eb19acceaf/uv-0.11.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e301d844eed9401f0f0351de12c55f1306ca05372acb0f28d35717c8ba663a22", size = 24301643 }, - { url = "https://files.pythonhosted.org/packages/1a/36/8f683bc60547b8f93d0e752a8574d13fad776999cb978482b360c053ca22/uv-0.11.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f0bf483c0d9fa14283992d56061b498b9d3d4adebd285af8744dc33f64dadfba", size = 23786049 }, - { url = "https://files.pythonhosted.org/packages/10/dc/7a495db39c2970de4fa375c337dbd617b16780911f88f0511f8fe7f6747c/uv-0.11.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:2ccd5487a4a192bc832ea04c867a26883757db8fdfe88bed85d8129c82f9e505", size = 25049786 }, - { url = "https://files.pythonhosted.org/packages/37/dd/74eff72d749eaf7e19f489878e21a368a7fef58d26ea0c63ec044ecd78b1/uv-0.11.17-py3-none-win32.whl", hash = "sha256:12b701fa32c5be3691759a73956e4462f30fa7b0dfa52ec66cb305bbb6ea4129", size = 22479213 }, - { url = "https://files.pythonhosted.org/packages/79/99/8af4a92b99a8a4823297c26df727fe957267e03e1196e3caa803c3f6ccb2/uv-0.11.17-py3-none-win_amd64.whl", hash = "sha256:44ec1fe3af839f87370dcf0400c0cab917cc1ce697d563e860fc7d9ed72655e7", size = 25083161 }, - { url = "https://files.pythonhosted.org/packages/00/76/a689077832d585d29d87f9cd0d65eca1af58abd29a4eab004d0a8a858b9c/uv-0.11.17-py3-none-win_arm64.whl", hash = "sha256:37c915bfcf86f99c1c5be7c9ed21e0d80624067ba47bc8916a3cb0530bc94d27", size = 23544936 }, -] - -[[package]] -name = "validators" -version = "0.35.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/66/a435d9ae49850b2f071f7ebd8119dd4e84872b01630d6736761e6e7fd847/validators-0.35.0.tar.gz", hash = "sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a", size = 73399 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/6e/3e955517e22cbdd565f2f8b2e73d52528b14b8bcfdb04f62466b071de847/validators-0.35.0-py3-none-any.whl", hash = "sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd", size = 44712 }, -] - -[[package]] -name = "virtualenv" -version = "21.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "python-discovery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/f0/b47ecf438211a25a97f8f0e4b23c22bc2496ebfea18dd6ec16210f09cc36/virtualenv-21.4.1.tar.gz", hash = "sha256:2ca543c713b72840ceffd94e9bdedfbd09a661defa1f7f69e5429ad4059442e2", size = 7613344 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/dc/ac4f3a987a87e1a18556896f257c4e15c95ed157b7975347ec6b313b75ce/virtualenv-21.4.1-py3-none-any.whl", hash = "sha256:caf4ff72d1b4039057f41d8e8466e859513d67c0400d9c6b62c02c9d1ebc3e12", size = 7594078 }, -] - -[[package]] -name = "wcwidth" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825 }, -] - -[[package]] -name = "webcolors" -version = "25.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, -] - -[[package]] -name = "webencodings" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503 }, -] - -[[package]] -name = "wrapt" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ac/4370bde262c0e633e6c4f0e56d55095710024cf9a5cecc20c59a10de483c/wrapt-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd57607acc85678925940bd5df0385ff8332083a32fa8d7a43f8767f4997263c", size = 80321 }, - { url = "https://files.pythonhosted.org/packages/eb/79/b8ff3a61e71babf58a8cf4c0d63358e8bad383e15bf7f35e62d2f6b6e4a4/wrapt-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ae574d65c9fa8e86f64f6a7c2668f9fcd507b183e0e577619f504b883cb0a6c", size = 81216 }, - { url = "https://files.pythonhosted.org/packages/6e/fd/c0cac1f77c9c4f6fe58a920ca632ce379bb8be928720e11e8d73de28a5e9/wrapt-2.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a04c28c10ba7fd12842b109d2edb0678872a2fe65277ca4ff06a0d61edee245", size = 159208 }, - { url = "https://files.pythonhosted.org/packages/d9/4f/744132a7b2fbefa6b81118ec5942eca5fc2e9a129f9055a0c5e46885a549/wrapt-2.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2f02472a1cbbf3884b365714a810b5947134a95ad6952b554cb8cce9d492b0", size = 160322 }, - { url = "https://files.pythonhosted.org/packages/d6/95/b7cd9a22a06cf93e6482904ee6afc956248983553593fd1009296d1b3b31/wrapt-2.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac2745950b2bff80219c15ebf2fa9d8427eba7e249739f97e55c9d169e47e9e1", size = 153243 }, - { url = "https://files.pythonhosted.org/packages/4c/4a/eb79423192015f46f0db2872e7e04a3dde8d359b83411e8959e7c9287eaa/wrapt-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a97e5b6c457f0cd3cfc19ebb2d84463e60c3ece754cc831e4281a3ca29bb18", size = 159231 }, - { url = "https://files.pythonhosted.org/packages/ec/dc/435015b58ce33c6fc4104158fa91ddb0e809ab03a5751fb7465d1d461456/wrapt-2.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c803a3d331796255af51ba2c79ed0ac8275865b516c09e61f248d1e7aff31ce9", size = 152351 }, - { url = "https://files.pythonhosted.org/packages/77/ac/5d203f98df8fd136b95c5227139aea02d34505e18baf812d0c005df61963/wrapt-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9b984d1eb252145d6302c1dbd5e87fc6d404d45531447c84eadec04bf1fcb027", size = 158347 }, - { url = "https://files.pythonhosted.org/packages/52/2f/a92427dbdc74e54c1674abbed27e61b2cb5e7a94441b8c1270c70671d928/wrapt-2.2.1-cp311-cp311-win32.whl", hash = "sha256:8a983a603a18c8708f024f7f6991b2e66159219abbf894634c5056243c55f3cd", size = 77562 }, - { url = "https://files.pythonhosted.org/packages/c8/56/987b9c13b3e1c1a3c6de71284076f996b79caec90e75a87c044a40c23db9/wrapt-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:9c210a6994b21aa9b29e81c8d11560e8fdab54c117e9cff37870d0a27bde1343", size = 80616 }, - { url = "https://files.pythonhosted.org/packages/7e/25/d01f560888d99d94a959c85533de349ce68d71ace3f2591d6ea8f632cfed/wrapt-2.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:401229e9d63ca09f9b8891ecf83798d26c11bbb445d11ed9f1836b6d4585b38a", size = 79025 }, - { url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021 }, - { url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692 }, - { url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364 }, - { url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079 }, - { url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205 }, - { url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922 }, - { url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388 }, - { url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682 }, - { url = "https://files.pythonhosted.org/packages/30/72/83ea3790ea352439442349388e29ff07b76e0686265f9088bbb505d1608d/wrapt-2.2.1-cp312-cp312-win32.whl", hash = "sha256:2f8c90c8afde51969487be4e1343ae049b268854877d415c2510baf833775052", size = 77857 }, - { url = "https://files.pythonhosted.org/packages/ef/cb/99450668dd3502d62a54a1c8aa56e44f34cb8c1261b381cfe2e7926c3b75/wrapt-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ce32763ac31ce94fe9aada947e479b1975012bff166da409b4b9e4e376cf7e5", size = 80825 }, - { url = "https://files.pythonhosted.org/packages/e6/3a/87512881be64e743f9ee4c66f4cbe8e884974bef2a5989af71f999653ac7/wrapt-2.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d1b4d0e0c2119587a31f5c029abd547e0c81d93b89d394566fe1588659eb579", size = 79087 }, - { url = "https://files.pythonhosted.org/packages/88/d1/a1b08f8f4fac8cbb156fa51cf64ee2c7f7f74f9875ba3cf70b3c58368694/wrapt-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d2beb1c7cab10603aecdc42f8edd6ff013f9a32e4543474e38e6b77ce9975aeb", size = 80831 }, - { url = "https://files.pythonhosted.org/packages/54/ce/57890814991446a845e09b3445ce8b694f27eb0577004f2c2a36a9772ed4/wrapt-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0cb7e4dd71f4c32e5e84843cd3c4cd65dda034314004bbe1d7f99af2426ab80", size = 81375 }, - { url = "https://files.pythonhosted.org/packages/38/65/08d7a6c76ac4493bdb668205ee9c1de1bd5daca61717c3e9aa49b4c01499/wrapt-2.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95821352042722cd9f1108874579a47989d0a7e12a37d87d2fc4af20fd99ab8a", size = 167417 }, - { url = "https://files.pythonhosted.org/packages/62/ce/f1ccbee7a1bfe5cdc6b3da6bab4b45713d628b9294da32a39f563d648140/wrapt-2.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abd621552ede77c4c69be7fac44ba911225b0c812b6ba604e5964cf98085b474", size = 166948 }, - { url = "https://files.pythonhosted.org/packages/86/2a/f85d48d1cd4869aee6704028d257d740a47c1c467b457ce396b4b5b55d07/wrapt-2.2.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e3677c7146ce694874941ba82b57092cc4875445aadf29d72807351023105143", size = 158148 }, - { url = "https://files.pythonhosted.org/packages/fe/5c/93939ad11d4a12358ab1aab219a2ef5efa5612e0db6b9fc65af8af1a891b/wrapt-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9a5934eaea872e17936b5f45501eba5ab0bce9a74122e172b663d7c28c459c4a", size = 165905 }, - { url = "https://files.pythonhosted.org/packages/e0/22/b8c2aa89862ff58605934d7abf4b70e6a5a1c33df96656f49035ccdf1c8a/wrapt-2.2.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f5b9daf6b629fce418e0cc3dd0436eac045188fa35deadb7a7f3941d5b8203f9", size = 156712 }, - { url = "https://files.pythonhosted.org/packages/5d/78/bf00a7b02239c12bb02ddcc3c0b971bfcc36e578c5a44f1ccfef5b458545/wrapt-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f53ac9f3ef573326d009ed809beff4efcac6451931c2b8132586da4b9e53ff31", size = 166560 }, - { url = "https://files.pythonhosted.org/packages/fe/93/6390ca9c5b787683cef588d04f57c8d41b9a2323b5597a65f18638c90ef2/wrapt-2.2.1-cp313-cp313-win32.whl", hash = "sha256:1ffa9cfd4bdb581539951b14ae661ff20ed0c3599b3e911a131ee0ec5ac11337", size = 77817 }, - { url = "https://files.pythonhosted.org/packages/97/73/ce10f0e71c0cfaa1a65faadb8efd4852028b3bb9ba28932b8889df769d38/wrapt-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:368eac1e20fd0bb03dd3cc42bf9887154c3861b60989389ccb5fac032617d215", size = 80736 }, - { url = "https://files.pythonhosted.org/packages/c7/4c/89f4a6818fafbbd840330e4fa3873073e1bfc166133a64cac7f8fde7a5e3/wrapt-2.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:c754dafdf5aaf0b401b644a90a30046929a0dd1a536e0ff0ec959a59155d9c7f", size = 79099 }, - { url = "https://files.pythonhosted.org/packages/bf/f2/9a8741c46f8c208ac0a45b25ba170bcb4fb72a2781d5fb97dbd7b6be73cb/wrapt-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ed928d0fda15fc0adc8d13305c8b3c0f2fba5b0669950c9e6d019d9162a3b3e8", size = 82802 }, - { url = "https://files.pythonhosted.org/packages/9c/0d/e9c855716a3705eef1416456bdf062b60620726fdc59428ff670fc3c60dc/wrapt-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fafb4e739e43544d12cb4abd1605fd4683b6ca6a9ad682b7fd8f4d21973eafa8", size = 83329 }, - { url = "https://files.pythonhosted.org/packages/3b/d6/a88f1c13112b7831adac75cea65d8310e0d696d570c8961844c90a57b865/wrapt-2.2.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:74d6a0c31472fe5d814917266b9f46495d7c61ed890af08b468acea92fb89a8d", size = 202937 }, - { url = "https://files.pythonhosted.org/packages/42/65/e29d54aef06a4d898a5b8a25589a0b3769bde454f922fad8f6f89fbfb650/wrapt-2.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab5be648d5a0b86b7438864f8df3c705a65cef35a2fd3e5561e3e203167e0f27", size = 209997 }, - { url = "https://files.pythonhosted.org/packages/2a/91/e4454263516cf0e12640912fbca9a83654e424f0a6ddb79f5cd7ce14bf33/wrapt-2.2.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d8f204c8e3a8bf9ece17e0a83d137fd807440977f8a5e762d59306795011440", size = 194856 }, - { url = "https://files.pythonhosted.org/packages/de/d0/fe0ee202286afdf4a7f77dd29f195703145764d572aec209c5086e57d924/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d047f6498c973874ba08ac3f97c69a2c4b2211c8de6f4c205f75cb1c9522596e", size = 205654 }, - { url = "https://files.pythonhosted.org/packages/23/b6/87d860dfc6460c246af70b1fd5c8b76df77571b42a493459423ded94fd7d/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:7a4fdb9326aab4a5a477a1640e5ad786a8495901009d7e7b038371edd23a9d2b", size = 192206 }, - { url = "https://files.pythonhosted.org/packages/df/46/3eea8cde077d985f239a38c0257087b8064fd9ee9b1a99e282d2c86da4ef/wrapt-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8cc5094b08abeae52da9c73c8a32003623be691a5193df2f4e3eac3d557c394", size = 198428 }, - { url = "https://files.pythonhosted.org/packages/18/dc/b927ee9c7fc67adc3a5658f246a0d275425eb840ba36e7b702e70f18bde8/wrapt-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:9907a4402ab6db12b7077a0ea5d7a4d028ecb22c8eee2b53527080d347cd1562", size = 79448 }, - { url = "https://files.pythonhosted.org/packages/ec/b3/fd30b473fe498c70e6b9a5f328b8d3fbaf1b8c3c481465f59724bba8eb70/wrapt-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:5590d63f5243251641cf543009b4c9314a79d0598fdb8a8e4cfc918494536c53", size = 83021 }, - { url = "https://files.pythonhosted.org/packages/ee/f3/96c39153a8737a6e9aa85adef254ac4195bea3f2d24efc60472ccc3c9e2e/wrapt-2.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:c318a64b53d97b841d7b5e637517e50a27be64bc695128422953d4b21710954e", size = 80295 }, - { url = "https://files.pythonhosted.org/packages/0a/a3/11d7f34ebbf3231bc907a3e6d5ee051b14d034c1bc7b65a97d5cc00516df/wrapt-2.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f56a647e4eaf5f0ca40330fb070f566bdf9f7b0db89a1af20d71c28dcd7a0ab", size = 80879 }, - { url = "https://files.pythonhosted.org/packages/13/3c/b74cfd984cef560b900fb1a727af20352d89e1f06bf2e1114dd3f00f5f5a/wrapt-2.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:64b7deeda4b70408e382328d8bbe52a256fe9bc63ae3db86d804608367e5422c", size = 81462 }, - { url = "https://files.pythonhosted.org/packages/15/a3/7c8f704b8dc07dfe0a5d01c2edbfd88317aa8e5e3fa7c743eb7a085ae767/wrapt-2.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9cf53ba90717db2e292401de290776c498d4bbfb0d4a559ca2895db8b9dcb5c", size = 167251 }, - { url = "https://files.pythonhosted.org/packages/80/85/a34d1888d97247da6c2ff6118c3a721c73ed8cc4dd198c00208bb73b6f80/wrapt-2.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf3638274ab9d9b724c9baa0b4c04e132cd6faefb78b4dd3dd1a02a4bdaad41e", size = 166316 }, - { url = "https://files.pythonhosted.org/packages/e9/d7/72ffaeb01eebc704afe3fb99e840480f4bda45f0fa66e3381b6a39251c8f/wrapt-2.2.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aed9658797d0b45d6c49adcfc6b41f66e6f2d0c6de3ec79e16cf4b1855df240f", size = 157952 }, - { url = "https://files.pythonhosted.org/packages/24/5b/36f5d6b024e4edfdd90b140742d11ebcf7836daf5c9daf326c55c24db412/wrapt-2.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1d676ee388bc42a04d56dd7deb5605244dac2e35cc2fadbb43c9fa25bbd93508", size = 166130 }, - { url = "https://files.pythonhosted.org/packages/81/06/9296d9e97bfdef5483dfcc859d57b095b257144b2bc5300ab521e06f4bc7/wrapt-2.2.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e395f7bc31851ef9b612050368cb446e9bc14cd7454b025018980349caf25ae5", size = 156604 }, - { url = "https://files.pythonhosted.org/packages/53/37/16953929ed6776175720e58fc966e779926d8d71e2c7b2273230590ca71f/wrapt-2.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f1845c2a8cc1180ccccfa45785dd06f562730d19ef75be180334254012b6283", size = 166007 }, - { url = "https://files.pythonhosted.org/packages/b9/73/20ee58c0612dae7c31131a7095345812ed2c7b389019e175f68cde34e5b4/wrapt-2.2.1-cp314-cp314-win32.whl", hash = "sha256:436addbc4bb4fc0a88c702577f51195d7d73683a7f3e0e5b253d8404d7847243", size = 78327 }, - { url = "https://files.pythonhosted.org/packages/22/b3/ef7c3295d02e0448a71c639a36a057f46d524d057c9486291a7a3039e65c/wrapt-2.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:50972a1d974ea07725a7f6b1cec5f8759008afd030a0024843ebe7d52de47f2b", size = 81144 }, - { url = "https://files.pythonhosted.org/packages/ac/dc/7bdf336953f99f4ceb0a584bb8870e42c8f26f93ea10c87834dad62f1668/wrapt-2.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1c9934ea5d92957e3cd0adbc0845539dccfd62710ebe16195a8c66c53954db36", size = 79569 }, - { url = "https://files.pythonhosted.org/packages/6a/6d/6dfae80150ff1919c356d1dd528f049bcdfaae29b4d284bc957e022caef4/wrapt-2.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17de18fc12cea55b8a9587314cb830573e37fb33b247a7515696350863714188", size = 82892 }, - { url = "https://files.pythonhosted.org/packages/82/7b/4e34766a7d7804ffce9e71befe47e9b3225dc350c49c94493c4ab39fd3a5/wrapt-2.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9dec1aca52dddde7df94818310fa2fe79739c8f385b2014c4cb1035f5508199", size = 83333 }, - { url = "https://files.pythonhosted.org/packages/9d/57/0b34db3e8de44ccfece62d7b337abd1631dd810f5adc5f3db571727836b5/wrapt-2.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69f2e9244542cb34dd59c7f073445b9e54ad9f3fce8d93606c368a1b499fc413", size = 202899 }, - { url = "https://files.pythonhosted.org/packages/e5/45/ac0c459f154b99d92789a6cba7ca727185b83513b986f8ec7fe2aacddcbf/wrapt-2.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d83966dc7f4f45e8b97b5933685ac2e6e67fc0e19246ea314bceb9a8970c956", size = 209986 }, - { url = "https://files.pythonhosted.org/packages/b7/e4/77e37ff33ad018fa81ade52c25fa327b80b56f81d734279a63614fcb4cbc/wrapt-2.2.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78b0aa6bfb7be8deed0ab23e7aa028cc5210c29bc2d32a04d52b50e517a7307e", size = 194893 }, - { url = "https://files.pythonhosted.org/packages/dd/9d/7ea651d1ab032fc5fa222fbec91d0f8a1397f6ae04ebb93fa7219aa921d7/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:05d5cb74d1b232ec8cfa130a8f900708699ff2491d97b8f85a4cdc5996294b85", size = 205636 }, - { url = "https://files.pythonhosted.org/packages/09/af/8e88031a701275b9085c54e64bc88c0b1cd55c77eadd400691c371cd76c4/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f6518b94edb9150452e9aba08027d4cc293433753ec1fbefb4629a21cbc74181", size = 192267 }, - { url = "https://files.pythonhosted.org/packages/bf/a8/e657ca876b06710194f243d81c4b0896ade646e244bdbec2d87c8c56a8bd/wrapt-2.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ed55af48b3eb28f43228ca2306788892bcb629eb2b5c4876e2a3659872c2f17a", size = 198378 }, - { url = "https://files.pythonhosted.org/packages/c8/59/822efe4ea722a3961331bfa35b7d90937790d2c20f0616de1997ccc3aebd/wrapt-2.2.1-cp314-cp314t-win32.whl", hash = "sha256:2e08688ab16525897da6589d56d0aebaf417bbe91c2d8e3b96203b1efa596e85", size = 80226 }, - { url = "https://files.pythonhosted.org/packages/ab/31/2a7dc5f6abb2fca0b6e1610e120419f603650aceb4f1d3ac4cae0354e162/wrapt-2.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fd0135d34387f5fd087d9be368ea77ea89cf2451dc1cd1c622d35021bcb3ab50", size = 83835 }, - { url = "https://files.pythonhosted.org/packages/9f/c0/782b86e28d1ceebeb74cccea12d2cd3d2ba0bd68e3dec20b1bc5873f6127/wrapt-2.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:f70db64e8266d7c45d3b735f2e08eeb434b5e03da9a479ae42b2e2e486a21a00", size = 80722 }, - { url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000 }, -] - -[[package]] -name = "xarray" -version = "2026.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "pandas" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/a6/6fe936a798a3a38a79c7422d1a31afd2e9a14690fcb0ccff96bc01f04bf2/xarray-2026.4.0.tar.gz", hash = "sha256:c4ac9a01a945d90d5b1628e2af045099a9d4943536d4f2ee3ae963c3b222d15b", size = 3132311 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/83/6d810a8a9ebc9c307989b418840c20e46907c74d707beb67ab566773e6fc/xarray-2026.4.0-py3-none-any.whl", hash = "sha256:d43751d9fb4a90f9249c30431684f00c41bc874f1edccd862631a40cbc0edf08", size = 1414326 }, -] - -[[package]] -name = "xarray-dataclass" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "typing-extensions" }, - { name = "xarray" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/99/04705ee6ff4d6d4db986058ceb19f2561014dde41e2f2ab73cc5ed35c958/xarray_dataclass-3.0.0.tar.gz", hash = "sha256:0fa438efda992b859dee07dc2f99c2660886402c366706c074c4c92795b354f1", size = 8376 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/ea/bc1de04d06b7c59fc3ff647a11fa248bf80af5a6227647a31c6250c32ce6/xarray_dataclass-3.0.0-py3-none-any.whl", hash = "sha256:7956f29005671b2acaa0da700d8f359105dce84ef84f76ce2dd05b76dba3e826", size = 16862 }, -] - -[[package]] -name = "xarray-spatial" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "datashader", marker = "python_full_version < '3.12'" }, - { name = "numba", marker = "python_full_version < '3.12'" }, - { name = "numpy", marker = "python_full_version < '3.12'" }, - { name = "xarray", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/ae/8501c1a111423be48cf67406e6bf4ae0258109f3c03e27e10d6768a36185/xarray_spatial-0.5.3.tar.gz", hash = "sha256:2563f5fb00acb61065cf534af572290b4f91f134c0dcaaf84c93a4a37113ebed", size = 67748871 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ee/9d0bb9f1bbaf347efe2ca6ad7bae259d6ed9e7a2b4441bd16b88723145a1/xarray_spatial-0.5.3-py3-none-any.whl", hash = "sha256:84f5a3e07a1ac3212bcfff3c4677daa3c529a533f1493eb555f8e498b7d9746e", size = 2068246 }, -] - -[[package]] -name = "xarray-spatial" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "numba", marker = "python_full_version >= '3.12'" }, - { name = "numpy", marker = "python_full_version >= '3.12'" }, - { name = "scipy", marker = "python_full_version >= '3.12'" }, - { name = "urllib3", marker = "python_full_version >= '3.12'" }, - { name = "xarray", marker = "python_full_version >= '3.12'" }, - { name = "zstandard", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/5b/5c69a4b56cabd93e65cbbf2a64b0609b754ea3aaaa69edefe3306cb8af06/xarray_spatial-0.10.0.tar.gz", hash = "sha256:6837e3b14f3f3d7aa641d120fbc43562a2456c6f03de911bbbcdb7d89b2963e6", size = 14375557 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/be/d22d593b7ccbec145d105ea311387819de0f38c3f0fde798a2cd7a2bbb68/xarray_spatial-0.10.0-py3-none-any.whl", hash = "sha256:0cb2ba7a3ddf7407b24258f662665cab82a362060b719c30b298a7df925182e0", size = 14533173 }, -] - -[[package]] -name = "yarl" -version = "1.24.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971 }, - { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507 }, - { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343 }, - { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704 }, - { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281 }, - { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020 }, - { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450 }, - { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384 }, - { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153 }, - { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322 }, - { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057 }, - { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502 }, - { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253 }, - { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345 }, - { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558 }, - { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808 }, - { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610 }, - { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957 }, - { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164 }, - { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688 }, - { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902 }, - { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931 }, - { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030 }, - { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392 }, - { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612 }, - { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487 }, - { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333 }, - { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025 }, - { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507 }, - { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719 }, - { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438 }, - { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719 }, - { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901 }, - { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229 }, - { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978 }, - { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733 }, - { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113 }, - { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899 }, - { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862 }, - { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060 }, - { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613 }, - { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012 }, - { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887 }, - { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620 }, - { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599 }, - { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604 }, - { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161 }, - { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619 }, - { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362 }, - { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667 }, - { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069 }, - { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670 }, - { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916 }, - { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625 }, - { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574 }, - { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534 }, - { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481 }, - { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529 }, - { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338 }, - { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147 }, - { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272 }, - { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962 }, - { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063 }, - { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438 }, - { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458 }, - { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589 }, - { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424 }, - { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690 }, - { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248 }, - { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084 }, - { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272 }, - { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497 }, - { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002 }, - { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524 }, - { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165 }, - { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010 }, - { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128 }, - { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382 }, - { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964 }, - { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204 }, - { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510 }, - { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584 }, - { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410 }, - { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980 }, - { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219 }, - { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576 }, -] - -[[package]] -name = "zarr" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.12'", -] -dependencies = [ - { name = "donfig", marker = "python_full_version < '3.12'" }, - { name = "google-crc32c", marker = "python_full_version < '3.12'" }, - { name = "numcodecs", marker = "python_full_version < '3.12'" }, - { name = "numpy", marker = "python_full_version < '3.12'" }, - { name = "packaging", marker = "python_full_version < '3.12'" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/31/5a/b8a0cf39a14c770c30bd1f2d120c54000c8cd9e84e8e79f38d9a7ce58071/zarr-3.1.6.tar.gz", hash = "sha256:d95e72cbea4b90e9a70679468b8266400331756232576ae2b43400ac5108d0eb", size = 386531 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/7c/ba8ca8cbe9dbef8e83a95fc208fed8e6686c98b4719aaa0aa7f3d31fe390/zarr-3.1.6-py3-none-any.whl", hash = "sha256:b5a82c5079d1c3d4ee8f06746fa3b9a98a7d804300fa3f4be154362a33e1207e", size = 295655 }, -] - -[[package]] -name = "zarr" -version = "3.2.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14'", - "python_full_version == '3.13.*'", - "python_full_version == '3.12.*'", -] -dependencies = [ - { name = "donfig", marker = "python_full_version >= '3.12'" }, - { name = "google-crc32c", marker = "python_full_version >= '3.12'" }, - { name = "numcodecs", marker = "python_full_version >= '3.12'" }, - { name = "numpy", marker = "python_full_version >= '3.12'" }, - { name = "packaging", marker = "python_full_version >= '3.12'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/8d/aeb164004f87543b06ef54f885d02c342c31ceb274e2bbec470a98927621/zarr-3.2.1.tar.gz", hash = "sha256:71565b738a0e7e8ed226f0516eba8c6bb53440ad7669a8c48ebb3534a161d035", size = 675161 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl", hash = "sha256:f78cdd3d9687ad0e9f9cba2c5683b64f0c52589c19f685eeabe872e93cc0d2c7", size = 319617 }, -] - -[[package]] -name = "zict" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/ac/3c494dd7ec5122cff8252c1a209b282c0867af029f805ae9befd73ae37eb/zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5", size = 33238 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/80/ab/11a76c1e2126084fde2639514f24e6111b789b0bfa4fc6264a8975c7e1f1/zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae", size = 43332 }, -] - -[[package]] -name = "zipp" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238 }, -] - -[[package]] -name = "zstandard" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254 }, - { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559 }, - { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020 }, - { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126 }, - { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390 }, - { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914 }, - { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635 }, - { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277 }, - { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377 }, - { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493 }, - { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018 }, - { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672 }, - { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753 }, - { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047 }, - { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484 }, - { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183 }, - { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533 }, - { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738 }, - { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436 }, - { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019 }, - { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012 }, - { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148 }, - { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652 }, - { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993 }, - { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806 }, - { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659 }, - { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933 }, - { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008 }, - { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517 }, - { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292 }, - { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237 }, - { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922 }, - { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276 }, - { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679 }, - { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735 }, - { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440 }, - { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070 }, - { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001 }, - { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120 }, - { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230 }, - { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173 }, - { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736 }, - { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368 }, - { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022 }, - { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889 }, - { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952 }, - { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054 }, - { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113 }, - { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936 }, - { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232 }, - { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671 }, - { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887 }, - { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658 }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849 }, - { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095 }, - { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751 }, - { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818 }, - { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402 }, - { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108 }, - { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248 }, - { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330 }, - { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123 }, - { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591 }, - { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513 }, - { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118 }, - { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940 }, -] From 8e4bb93478404aada3e622c8ad9a55d2f6fbdcf1 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 10:13:06 +0200 Subject: [PATCH 073/131] undo changes with main --- src/squidpy/experimental/utils/_labels.py | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/squidpy/experimental/utils/_labels.py diff --git a/src/squidpy/experimental/utils/_labels.py b/src/squidpy/experimental/utils/_labels.py new file mode 100644 index 000000000..5d18e8370 --- /dev/null +++ b/src/squidpy/experimental/utils/_labels.py @@ -0,0 +1,28 @@ +"""Shared internal helpers for working with labels elements. + +Not part of the public API - symbols here are private and may change +without notice. +""" + +from __future__ import annotations + +import spatialdata as sd +import xarray as xr +from spatialdata._logging import logger as logg + + +def resolve_labels_array(sdata: sd.SpatialData, labels_key: str, scale: str | None) -> xr.DataArray: + """Resolve a labels element to its 2-D ``xarray.DataArray``. + + Single-scale elements pass through; multi-scale (``xarray.DataTree``) + elements require an explicit ``scale`` and raise otherwise. Passing + ``scale`` for a single-scale element logs a warning and is ignored. + """ + node = sdata.labels[labels_key] + if isinstance(node, xr.DataTree): + if scale is None: + raise ValueError(f"Labels `{labels_key}` is multi-scale; pass `scale` (e.g. 'scale0').") + return node[scale].ds["image"] + if scale is not None: + logg.warning(f"`scale={scale!r}` ignored: labels at `{labels_key}` are single-scale.") + return node From 9a489f2fd316adc324401c671e16a9a46757196a Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Mon, 1 Jun 2026 10:19:40 +0200 Subject: [PATCH 074/131] resuse resolve_labels_array --- src/squidpy/experimental/tl/_tiling_qc.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/squidpy/experimental/tl/_tiling_qc.py b/src/squidpy/experimental/tl/_tiling_qc.py index 1c6145610..ae0773576 100644 --- a/src/squidpy/experimental/tl/_tiling_qc.py +++ b/src/squidpy/experimental/tl/_tiling_qc.py @@ -50,6 +50,7 @@ compute_cell_info_tiled, extract_labels_tile_lazy, ) +from squidpy.experimental.utils._labels import resolve_labels_array __all__ = ["calculate_tiling_qc"] @@ -563,15 +564,7 @@ def calculate_tiling_qc( if n_neighbors < 1: raise ValueError(f"n_neighbors must be >= 1, got {n_neighbors}.") - labels_node = sdata.labels[labels_key] - if isinstance(labels_node, xr.DataTree): - if scale is None: - raise ValueError("When using multi-scale labels, please specify the scale.") - labels_da = labels_node[scale].ds["image"] - else: - if scale is not None: - logg.warning(f"`scale={scale!r}` ignored: labels at {labels_key!r} are single-scale.") - labels_da = labels_node + labels_da = resolve_labels_array(sdata, labels_key, scale) cell_info = _compute_centroids_for_labels(sdata, labels_key, labels_da, scale) if not cell_info: From 759e09e2fcf7429464fdc97570c8776dea507cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Selman=20=C3=96zleyen?= <32667648+selmanozleyen@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:26:05 +0200 Subject: [PATCH 075/131] Update api.md --- docs/api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 508b33285..1c8fdde61 100644 --- a/docs/api.md +++ b/docs/api.md @@ -154,4 +154,3 @@ See the {doc}`extensibility guide ` for how to implement a custo experimental.im.apply_stain_normalization experimental.im.StainReference experimental.im.ReinhardParams -``` \ No newline at end of file From ad200ea6808027e65ce17d482f8573960be6e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Selman=20=C3=96zleyen?= <32667648+selmanozleyen@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:27:05 +0200 Subject: [PATCH 076/131] Update api.md --- docs/api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.md b/docs/api.md index 1c8fdde61..1bf4fdf92 100644 --- a/docs/api.md +++ b/docs/api.md @@ -154,3 +154,4 @@ See the {doc}`extensibility guide ` for how to implement a custo experimental.im.apply_stain_normalization experimental.im.StainReference experimental.im.ReinhardParams +``` From c9eb7e43444cd7d245fa09e2fb292714be9a3ad4 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:03:44 +0200 Subject: [PATCH 077/131] rename file and refactor --- src/squidpy/experimental/_fit/_estimator.py | 58 -------- src/squidpy/experimental/_fit/_registry.py | 46 ------ .../_fit/align_samples/_stalign.py | 126 ---------------- .../{_fit => _methods}/__init__.py | 20 +-- .../{_fit => _methods}/_families.py | 0 .../experimental/_methods/_registry.py | 65 +++++++++ .../{_fit => _methods}/_result.py | 0 .../align_landmarks/__init__.py | 8 +- .../align_landmarks/_landmark.py | 135 ++++++++++-------- .../align_samples/__init__.py | 4 +- .../_methods/align_samples/_stalign.py | 117 +++++++++++++++ .../align_samples/_stalign_impl/__init__.py | 0 .../align_samples/_stalign_impl/_core.py | 0 .../align_samples/_stalign_impl/_helpers.py | 0 .../align_samples/_stalign_impl/_tools.py | 0 src/squidpy/experimental/tl/__init__.py | 3 +- src/squidpy/experimental/tl/_align/_api.py | 4 +- tests/experimental/fit/test_core.py | 49 +++---- tests/experimental/fit/test_stalign.py | 14 +- 19 files changed, 303 insertions(+), 346 deletions(-) delete mode 100644 src/squidpy/experimental/_fit/_estimator.py delete mode 100644 src/squidpy/experimental/_fit/_registry.py delete mode 100644 src/squidpy/experimental/_fit/align_samples/_stalign.py rename src/squidpy/experimental/{_fit => _methods}/__init__.py (62%) rename src/squidpy/experimental/{_fit => _methods}/_families.py (100%) create mode 100644 src/squidpy/experimental/_methods/_registry.py rename src/squidpy/experimental/{_fit => _methods}/_result.py (100%) rename src/squidpy/experimental/{_fit => _methods}/align_landmarks/__init__.py (79%) rename src/squidpy/experimental/{_fit => _methods}/align_landmarks/_landmark.py (60%) rename src/squidpy/experimental/{_fit => _methods}/align_samples/__init__.py (81%) create mode 100644 src/squidpy/experimental/_methods/align_samples/_stalign.py rename src/squidpy/experimental/{_fit => _methods}/align_samples/_stalign_impl/__init__.py (100%) rename src/squidpy/experimental/{_fit => _methods}/align_samples/_stalign_impl/_core.py (100%) rename src/squidpy/experimental/{_fit => _methods}/align_samples/_stalign_impl/_helpers.py (100%) rename src/squidpy/experimental/{_fit => _methods}/align_samples/_stalign_impl/_tools.py (100%) diff --git a/src/squidpy/experimental/_fit/_estimator.py b/src/squidpy/experimental/_fit/_estimator.py deleted file mode 100644 index aea3abc93..000000000 --- a/src/squidpy/experimental/_fit/_estimator.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Estimator contract: a pure, in-memory fit.""" - -from __future__ import annotations - -import importlib.util -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, ClassVar - -if TYPE_CHECKING: - from squidpy.experimental._fit._result import FitResult - - -class Estimator(ABC): - """Base class for an in-memory model fit. - - Subclasses implement :meth:`fit`, which takes already-extracted in-memory - data (NumPy arrays, sparse matrices, or an :class:`~anndata.AnnData`) and - returns a :class:`FitResult`. Following scikit-learn, ``fit`` assumes the - training data fits in memory -- there is no streaming / out-of-core - contract (scikit-learn's ``partial_fit`` is the analogous opt-in exception - and is intentionally not modelled here). - - Reading data out of a container and writing results back are *not* part of - this contract; both are handled by the calling function. - - Attributes - ---------- - name - Stable identifier used as the registry key. - requires - Optional third-party packages the estimator needs at fit time (e.g. - ``("jax",)``). Advertised here so :meth:`check_requirements` can fail - early with an actionable message before any heavy import fires. - """ - - name: ClassVar[str] - requires: ClassVar[tuple[str, ...]] = () - - @abstractmethod - def fit(self, *args: Any, **kwargs: Any) -> FitResult: - """Fit the model on in-memory data and return a :class:`FitResult`.""" - - def check_requirements(self) -> None: - """Raise :class:`ImportError` if any package in :attr:`requires` is missing. - - Estimators should call this at the top of :meth:`fit`, before importing - their optional backend, so callers get a clean message instead of a - deep ``ModuleNotFoundError``. - """ - missing = [pkg for pkg in self.requires if importlib.util.find_spec(pkg) is None] - if missing: - verb = "is" if len(missing) == 1 else "are" - names = ", ".join(repr(p) for p in missing) - extras = ",".join(missing) - raise ImportError( - f"Estimator {self.name!r} requires {names}, which {verb} not installed. " - f'Install with `pip install "squidpy[{extras}]"`.' - ) diff --git a/src/squidpy/experimental/_fit/_registry.py b/src/squidpy/experimental/_fit/_registry.py deleted file mode 100644 index 4df835f1d..000000000 --- a/src/squidpy/experimental/_fit/_registry.py +++ /dev/null @@ -1,46 +0,0 @@ -"""A flat registry mapping method names to estimator classes.""" - -from __future__ import annotations - -from collections.abc import Callable -from typing import TYPE_CHECKING, TypeVar - -if TYPE_CHECKING: - from squidpy.experimental._fit._estimator import Estimator - -E = TypeVar("E", bound="type[Estimator]") - - -class Registry: - """A flat ``name -> Estimator`` registry for one *family* of methods. - - One :class:`Registry` is created per family (e.g. ``align``, ``impute``), - so keys are plain method names -- there is no ``(method, mode)`` compound - key, because the family already pins the rest. - """ - - def __init__(self, name: str) -> None: - self.name = name - self._estimators: dict[str, type[Estimator]] = {} - - def register(self, key: str) -> Callable[[E], E]: - """Return a class decorator registering an :class:`Estimator` under ``key``.""" - - def decorator(cls: E) -> E: - if key in self._estimators: - raise ValueError(f"Estimator {key!r} is already registered in the {self.name!r} registry.") - self._estimators[key] = cls - return cls - - return decorator - - def get(self, key: str) -> type[Estimator]: - """Return the :class:`Estimator` class registered under ``key``.""" - try: - return self._estimators[key] - except KeyError: - raise ValueError(f"Unknown {self.name} method {key!r}. Available: {sorted(self._estimators)}.") from None - - def keys(self) -> tuple[str, ...]: - """Return the registered method names.""" - return tuple(self._estimators) diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign.py b/src/squidpy/experimental/_fit/align_samples/_stalign.py deleted file mode 100644 index 749370a06..000000000 --- a/src/squidpy/experimental/_fit/align_samples/_stalign.py +++ /dev/null @@ -1,126 +0,0 @@ -"""STalign estimator: JAX LDDMM point-cloud registration. - -The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see -:mod:`._stalign_impl`. This module only adds the thin :class:`Estimator` / -:class:`FitResult` adapter onto the :mod:`squidpy.experimental._fit` core. JAX -is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module -is cheap and does not require JAX. -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any - -import numpy as np - -from squidpy.experimental._fit._estimator import Estimator -from squidpy.experimental._fit._families import ALIGN_SAMPLES -from squidpy.experimental._fit._result import FitResult - -if TYPE_CHECKING: - from ._stalign_impl._tools import STalignConfig - - -@dataclass -class StalignFitResult(FitResult): - """Fitted STalign deformation, in ``(x, y)`` convention. - - ``deltas`` is the per-point displacement that maps the *query* points into - the *reference* frame; :meth:`transform` bakes it in. STalign produces a - diffeomorphic (non-affine) map, so ``affine`` is ``None`` here -- the full - map (velocity field, velocity grid, affine initialisation) is kept under - ``metadata['stalign_result']`` for power users who need to transform - arbitrary points via ``stalign_result.transform_points``. - """ - - deltas: np.ndarray - affine: np.ndarray | None = None - metadata: dict[str, Any] = field(default_factory=dict) - - def transform(self, x: np.ndarray) -> np.ndarray: - """Apply the fitted displacement to the ``(N, 2)`` ``(x, y)`` points it was fit on. - - ``deltas`` is per-point, so ``x`` must have the same shape as the query - cloud passed to :meth:`StalignEstimator.fit`. To map *arbitrary* points, - use ``metadata['stalign_result'].transform_points``. - """ - coords = np.asarray(x, dtype=float) - if coords.shape != self.deltas.shape: - raise ValueError( - f"`transform` expects coordinates of shape {self.deltas.shape} (the fitted query cloud), " - f"found {coords.shape}. Use `metadata['stalign_result'].transform_points` for arbitrary points." - ) - return coords + self.deltas - - -@ALIGN_SAMPLES.register("stalign") -class StalignEstimator(Estimator): - """Align a query point cloud onto a reference with a JAX LDDMM solver.""" - - name = "stalign" - requires = ("jax",) - - def fit( - self, - ref: np.ndarray, - query: np.ndarray, - *, - config: STalignConfig | None = None, - landmarks_source: np.ndarray | None = None, - landmarks_target: np.ndarray | None = None, - ) -> StalignFitResult: - """Fit a deformation mapping ``query`` onto ``ref``. - - Parameters - ---------- - ref - ``(N, 2)`` reference point cloud in ``(x, y)`` order. - query - ``(M, 2)`` query point cloud in ``(x, y)`` order, to be aligned to - ``ref``. Both are plain in-memory arrays; extracting them from an - ``AnnData`` / ``SpatialData`` is the caller's responsibility. - config - Optional :class:`STalignConfig` of solver hyperparameters. - landmarks_source, landmarks_target - Optional corresponding ``(x, y)`` landmark arrays used to - initialise the affine. Must be provided together. - - Returns - ------- - A :class:`StalignFitResult` whose ``deltas`` move ``query`` into the - reference frame. - """ - self.check_requirements() - # Import the JAX-backed solver only after requirements pass, so callers - # without JAX get the clean ImportError from check_requirements rather - # than a confusing failure from a module-level `import jax`. - from ._stalign_impl._tools import stalign_points - - ref_xy = np.asarray(ref, dtype=float) - query_xy = np.asarray(query, dtype=float) - for arr_name, arr in (("ref", ref_xy), ("query", query_xy)): - if arr.ndim != 2 or arr.shape[1] != 2: - raise ValueError(f"Expected `{arr_name}` to be an (N, 2) array, found shape {arr.shape}.") - - # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. - src_rc = query_xy[:, [1, 0]] - tgt_rc = ref_xy[:, [1, 0]] - lm_src_rc = None if landmarks_source is None else np.asarray(landmarks_source, dtype=float)[:, [1, 0]] - lm_tgt_rc = None if landmarks_target is None else np.asarray(landmarks_target, dtype=float)[:, [1, 0]] - - result = stalign_points( - source_points=src_rc, - target_points=tgt_rc, - config=config, - landmarks_source=lm_src_rc, - landmarks_target=lm_tgt_rc, - ) - - aligned_xy = np.asarray(result.aligned_points)[:, [1, 0]] - deltas_xy = aligned_xy - query_xy - return StalignFitResult( - deltas=deltas_xy, - affine=None, - metadata={"method": "stalign", "stalign_result": result}, - ) diff --git a/src/squidpy/experimental/_fit/__init__.py b/src/squidpy/experimental/_methods/__init__.py similarity index 62% rename from src/squidpy/experimental/_fit/__init__.py rename to src/squidpy/experimental/_methods/__init__.py index 68440899d..5bf65cc3f 100644 --- a/src/squidpy/experimental/_fit/__init__.py +++ b/src/squidpy/experimental/_methods/__init__.py @@ -3,13 +3,10 @@ This subpackage factors the *fit* half of fitting-style tools (alignment, imputation, ...) out of any particular container or write-back path: -- :class:`Estimator` -- a model whose :meth:`~Estimator.fit` takes in-memory - data and returns a :class:`FitResult` (scikit-learn-style; training is - assumed to fit in memory). - :class:`FitResult` -- the fitted parameters plus an optional, pure in-memory :meth:`~FitResult.transform`. Container write-back lives in the calling function, not here. -- :class:`Registry` -- a flat ``name -> Estimator`` map, one per family. +- :class:`Registry` -- a flat ``name -> function`` map, one per family. Concrete estimators live in per-family subpackages (``align_samples``, ``align_landmarks``, ...) and pull their optional dependencies (e.g. JAX) in @@ -18,7 +15,7 @@ Example ------- >>> import numpy as np ->>> from squidpy.experimental._fit import Estimator, FitResult, Registry +>>> from squidpy.experimental._fit import FitResult, Registry >>> demo = Registry("demo") >>> class Shift(FitResult): ... def __init__(self, delta): @@ -27,21 +24,16 @@ ... def transform(self, x): ... return np.asarray(x) + self.delta >>> @demo.register("mean_shift") -... class MeanShift(Estimator): -... name = "mean_shift" -... -... def fit(self, ref, query): -... return Shift(ref.mean(0) - query.mean(0)) ->>> est = demo.get("mean_shift")() ->>> result = est.fit(np.ones((3, 2)), np.zeros((3, 2))) +... def fit_mean_shift(ref, query): +... return Shift(ref.mean(0) - query.mean(0)) +>>> result = demo.get("mean_shift")(np.ones((3, 2)), np.zeros((3, 2))) >>> result.transform(np.zeros((3, 2))).tolist() [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]] """ from __future__ import annotations -from squidpy.experimental._fit._estimator import Estimator from squidpy.experimental._fit._registry import Registry from squidpy.experimental._fit._result import FitResult -__all__ = ["Estimator", "FitResult", "Registry"] +__all__ = ["FitResult", "Registry"] diff --git a/src/squidpy/experimental/_fit/_families.py b/src/squidpy/experimental/_methods/_families.py similarity index 100% rename from src/squidpy/experimental/_fit/_families.py rename to src/squidpy/experimental/_methods/_families.py diff --git a/src/squidpy/experimental/_methods/_registry.py b/src/squidpy/experimental/_methods/_registry.py new file mode 100644 index 000000000..8026c14fe --- /dev/null +++ b/src/squidpy/experimental/_methods/_registry.py @@ -0,0 +1,65 @@ +"""A flat registry mapping method names to fitting functions.""" + +from __future__ import annotations + +import functools +import importlib.util +from collections.abc import Callable +from typing import Any, TypeVar + +F = TypeVar("F", bound=Callable[..., Any]) + + +class Registry: + """A flat ``name -> function`` registry for one *family* of methods. + + One :class:`Registry` is created per family (e.g. ``align``, ``impute``), + so keys are plain method names -- there is no ``(method, mode)`` compound + key, because the family already pins the rest. + """ + + def __init__(self, name: str) -> None: + self.name = name + self._registry: dict[str, Callable[..., Any]] = {} + + def register(self, key: str, *, requires: tuple[str, ...] = ()) -> Callable[[F], F]: + """Return a decorator registering a method/function under ``key``.""" + + def decorator(func: F) -> F: + if key in self._registry: + raise ValueError(f"Method {key!r} is already registered in the {self.name!r} registry.") + + if requires: + @functools.wraps(func) + def wrapped(*args: Any, **kwargs: Any) -> Any: + missing = [pkg for pkg in requires if importlib.util.find_spec(pkg) is None] + if missing: + verb = "is" if len(missing) == 1 else "are" + names = ", ".join(repr(p) for p in missing) + extras = ",".join(missing) + raise ImportError( + f"Method {key!r} requires {names}, which {verb} not installed. " + f'Install with `pip install "squidpy[{extras}]"`.' + ) + return func(*args, **kwargs) + + wrapped.__requires__ = requires # type: ignore[attr-defined] + self._registry[key] = wrapped + return wrapped # type: ignore[return-value] + else: + self._registry[key] = func + return func + + return decorator + + def get(self, key: str) -> Callable[..., Any]: + """Return the function registered under ``key``.""" + try: + return self._registry[key] + except KeyError: + raise ValueError(f"Unknown {self.name} method {key!r}. Available: {sorted(self._registry)}.") from None + + def keys(self) -> tuple[str, ...]: + """Return the registered method names.""" + return tuple(self._registry) + diff --git a/src/squidpy/experimental/_fit/_result.py b/src/squidpy/experimental/_methods/_result.py similarity index 100% rename from src/squidpy/experimental/_fit/_result.py rename to src/squidpy/experimental/_methods/_result.py diff --git a/src/squidpy/experimental/_fit/align_landmarks/__init__.py b/src/squidpy/experimental/_methods/align_landmarks/__init__.py similarity index 79% rename from src/squidpy/experimental/_fit/align_landmarks/__init__.py rename to src/squidpy/experimental/_methods/align_landmarks/__init__.py index e4f528e93..36d355977 100644 --- a/src/squidpy/experimental/_fit/align_landmarks/__init__.py +++ b/src/squidpy/experimental/_methods/align_landmarks/__init__.py @@ -10,13 +10,13 @@ from squidpy.experimental._fit._families import ALIGN_LANDMARKS from squidpy.experimental._fit.align_landmarks._landmark import ( AffineFitResult, - LandmarkAffineEstimator, - LandmarkSimilarityEstimator, + fit_affine, + fit_similarity, ) __all__ = [ "ALIGN_LANDMARKS", "AffineFitResult", - "LandmarkAffineEstimator", - "LandmarkSimilarityEstimator", + "fit_affine", + "fit_similarity", ] diff --git a/src/squidpy/experimental/_fit/align_landmarks/_landmark.py b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py similarity index 60% rename from src/squidpy/experimental/_fit/align_landmarks/_landmark.py rename to src/squidpy/experimental/_methods/align_landmarks/_landmark.py index 2fffb3f5e..f3a7635c2 100644 --- a/src/squidpy/experimental/_fit/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py @@ -14,13 +14,12 @@ from __future__ import annotations -from abc import abstractmethod +from collections.abc import Callable from dataclasses import dataclass, field from typing import Any import numpy as np -from squidpy.experimental._fit._estimator import Estimator from squidpy.experimental._fit._families import ALIGN_LANDMARKS from squidpy.experimental._fit._result import FitResult @@ -46,73 +45,87 @@ def transform(self, x: np.ndarray) -> np.ndarray: return coords @ self.matrix[:2, :2].T + self.matrix[:2, 2] -class _LandmarkEstimator(Estimator): - """Shared logic for closed-form landmark fits. - - The base :meth:`fit` does all the work that is common across models -- - validate inputs, enforce ``N >= 3``, build the :class:`AffineFitResult`. - Subclasses only provide the matrix solver via :meth:`_solve`, so there is - no per-model branching here. - """ - - def fit( - self, - landmarks_ref: np.ndarray, - landmarks_query: np.ndarray, - *, - source_cs: str | None = None, - target_cs: str | None = None, - ) -> AffineFitResult: - """Fit an affine mapping ``landmarks_query`` onto ``landmarks_ref``. - - Parameters - ---------- - landmarks_ref, landmarks_query - Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). - source_cs, target_cs - Optional coordinate-system labels stamped onto the result for - traceability; they do not affect the fit. - """ - ref = _validate_landmarks(landmarks_ref, name="landmarks_ref") - query = _validate_landmarks(landmarks_query, name="landmarks_query") - if ref.shape != query.shape: - raise ValueError( - f"`landmarks_ref` and `landmarks_query` must have the same shape; got {ref.shape} and {query.shape}." - ) - if ref.shape[0] < 3: - raise ValueError(f"`{self.name}` needs at least 3 landmark pairs, got {ref.shape[0]}.") - - matrix = self._solve(ref, query) - return AffineFitResult( - matrix=matrix, - source_cs=source_cs, - target_cs=target_cs, - metadata={"method": self.name}, +def _fit_landmark_relation( + landmarks_ref: np.ndarray, + landmarks_query: np.ndarray, + *, + method: str, + solve_fn: Callable[[np.ndarray, np.ndarray], np.ndarray], + source_cs: str | None = None, + target_cs: str | None = None, +) -> AffineFitResult: + ref = _validate_landmarks(landmarks_ref, name="landmarks_ref") + query = _validate_landmarks(landmarks_query, name="landmarks_query") + if ref.shape != query.shape: + raise ValueError( + f"`landmarks_ref` and `landmarks_query` must have the same shape; got {ref.shape} and {query.shape}." ) + if ref.shape[0] < 3: + raise ValueError(f"`{method}` needs at least 3 landmark pairs, got {ref.shape[0]}.") - @abstractmethod - def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: - """Return the ``(3, 3)`` matrix mapping ``query_xy`` onto ``ref_xy``.""" + matrix = solve_fn(ref, query) + return AffineFitResult( + matrix=matrix, + source_cs=source_cs, + target_cs=target_cs, + metadata={"method": method}, + ) @ALIGN_LANDMARKS.register("similarity") -class LandmarkSimilarityEstimator(_LandmarkEstimator): - """4-DOF similarity fit (rotation + uniform scale + translation), via spatialdata.""" - - name = "similarity" - - def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: - return _fit_similarity(ref_xy, query_xy) +def fit_similarity( + landmarks_ref: np.ndarray, + landmarks_query: np.ndarray, + *, + source_cs: str | None = None, + target_cs: str | None = None, +) -> AffineFitResult: + """4-DOF similarity fit (rotation + uniform scale + translation), via spatialdata. + + Parameters + ---------- + landmarks_ref, landmarks_query + Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). + source_cs, target_cs + Optional coordinate-system labels stamped onto the result for + traceability; they do not affect the fit. + """ + return _fit_landmark_relation( + landmarks_ref, + landmarks_query, + method="similarity", + solve_fn=_fit_similarity, + source_cs=source_cs, + target_cs=target_cs, + ) @ALIGN_LANDMARKS.register("affine") -class LandmarkAffineEstimator(_LandmarkEstimator): - """6-DOF affine fit (rotation + non-uniform scale + shear + translation), via skimage.""" - - name = "affine" - - def _solve(self, ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: - return _fit_affine(ref_xy, query_xy) +def fit_affine( + landmarks_ref: np.ndarray, + landmarks_query: np.ndarray, + *, + source_cs: str | None = None, + target_cs: str | None = None, +) -> AffineFitResult: + """6-DOF affine fit (rotation + non-uniform scale + shear + translation), via skimage. + + Parameters + ---------- + landmarks_ref, landmarks_query + Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). + source_cs, target_cs + Optional coordinate-system labels stamped onto the result for + traceability; they do not affect the fit. + """ + return _fit_landmark_relation( + landmarks_ref, + landmarks_query, + method="affine", + solve_fn=_fit_affine, + source_cs=source_cs, + target_cs=target_cs, + ) def _validate_landmarks(points: np.ndarray, *, name: str) -> np.ndarray: diff --git a/src/squidpy/experimental/_fit/align_samples/__init__.py b/src/squidpy/experimental/_methods/align_samples/__init__.py similarity index 81% rename from src/squidpy/experimental/_fit/align_samples/__init__.py rename to src/squidpy/experimental/_methods/align_samples/__init__.py index f3abcc568..5c38b9cc3 100644 --- a/src/squidpy/experimental/_fit/align_samples/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/__init__.py @@ -8,6 +8,6 @@ from __future__ import annotations from squidpy.experimental._fit._families import ALIGN_SAMPLES -from squidpy.experimental._fit.align_samples._stalign import StalignEstimator, StalignFitResult +from squidpy.experimental._fit.align_samples._stalign import StalignFitResult, fit_stalign -__all__ = ["ALIGN_SAMPLES", "StalignEstimator", "StalignFitResult"] +__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignFitResult"] diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py new file mode 100644 index 000000000..003957390 --- /dev/null +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -0,0 +1,117 @@ +"""STalign estimator: JAX LDDMM point-cloud registration. + +The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see +:mod:`._stalign_impl`. This module only adds the thin :class:`Estimator` / +:class:`FitResult` adapter onto the :mod:`squidpy.experimental._fit` core. JAX +is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module +is cheap and does not require JAX. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +import numpy as np + +from squidpy.experimental._fit._families import ALIGN_SAMPLES +from squidpy.experimental._fit._result import FitResult + +if TYPE_CHECKING: + from ._stalign_impl._tools import STalignConfig + + +@dataclass +class StalignFitResult(FitResult): + """Fitted STalign deformation, in ``(x, y)`` convention. + + ``deltas`` is the per-point displacement that maps the *query* points into + the *reference* frame; :meth:`transform` bakes it in. STalign produces a + diffeomorphic (non-affine) map, so ``affine`` is ``None`` here -- the full + map (velocity field, velocity grid, affine initialisation) is kept under + ``metadata['stalign_result']`` for power users who need to transform + arbitrary points via ``stalign_result.transform_points``. + """ + + deltas: np.ndarray + affine: np.ndarray | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def transform(self, x: np.ndarray) -> np.ndarray: + """Apply the fitted displacement to the ``(N, 2)`` ``(x, y)`` points it was fit on. + + ``deltas`` is per-point, so ``x`` must have the same shape as the query + cloud passed to :meth:`fit_stalign`. To map *arbitrary* points, + use ``metadata['stalign_result'].transform_points``. + """ + coords = np.asarray(x, dtype=float) + if coords.shape != self.deltas.shape: + raise ValueError( + f"`transform` expects coordinates of shape {self.deltas.shape} (the fitted query cloud), " + f"found {coords.shape}. Use `metadata['stalign_result'].transform_points` for arbitrary points." + ) + return coords + self.deltas + + +@ALIGN_SAMPLES.register("stalign", requires=("jax",)) +def fit_stalign( + ref: np.ndarray, + query: np.ndarray, + *, + config: STalignConfig | None = None, + landmarks_source: np.ndarray | None = None, + landmarks_target: np.ndarray | None = None, +) -> StalignFitResult: + """Fit a deformation mapping ``query`` onto ``ref``. + + Parameters + ---------- + ref + ``(N, 2)`` reference point cloud in ``(x, y)`` order. + query + ``(M, 2)`` query point cloud in ``(x, y)`` order, to be aligned to + ``ref``. Both are plain in-memory arrays; extracting them from an + ``AnnData`` / ``SpatialData`` is the caller's responsibility. + config + Optional :class:`STalignConfig` of solver hyperparameters. + landmarks_source, landmarks_target + Optional corresponding ``(x, y)`` landmark arrays used to + initialise the affine. Must be provided together. + + Returns + ------- + A :class:`StalignFitResult` whose ``deltas`` move ``query`` into the + reference frame. + """ + # Import the JAX-backed solver only after requirements pass, so callers + # without JAX get the clean ImportError from check_requirements rather + # than a confusing failure from a module-level `import jax`. + from ._stalign_impl._tools import stalign_points + + ref_xy = np.asarray(ref, dtype=float) + query_xy = np.asarray(query, dtype=float) + for arr_name, arr in (("ref", ref_xy), ("query", query_xy)): + if arr.ndim != 2 or arr.shape[1] != 2: + raise ValueError(f"Expected `{arr_name}` to be an (N, 2) array, found shape {arr.shape}.") + + # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. + src_rc = query_xy[:, [1, 0]] + tgt_rc = ref_xy[:, [1, 0]] + lm_src_rc = None if landmarks_source is None else np.asarray(landmarks_source, dtype=float)[:, [1, 0]] + lm_tgt_rc = None if landmarks_target is None else np.asarray(landmarks_target, dtype=float)[:, [1, 0]] + + result = stalign_points( + source_points=src_rc, + target_points=tgt_rc, + config=config, + landmarks_source=lm_src_rc, + landmarks_target=lm_tgt_rc, + ) + + aligned_xy = np.asarray(result.aligned_points)[:, [1, 0]] + deltas_xy = aligned_xy - query_xy + return StalignFitResult( + deltas=deltas_xy, + affine=None, + metadata={"method": "stalign", "stalign_result": result}, + ) diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py similarity index 100% rename from src/squidpy/experimental/_fit/align_samples/_stalign_impl/__init__.py rename to src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py similarity index 100% rename from src/squidpy/experimental/_fit/align_samples/_stalign_impl/_core.py rename to src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py similarity index 100% rename from src/squidpy/experimental/_fit/align_samples/_stalign_impl/_helpers.py rename to src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py diff --git a/src/squidpy/experimental/_fit/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py similarity index 100% rename from src/squidpy/experimental/_fit/align_samples/_stalign_impl/_tools.py rename to src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 1949aa6f9..772cf72e8 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -1,8 +1,7 @@ from __future__ import annotations from ._align import align, align_by_landmarks -from ._tiling_qc import , calculate_tiling_qc, TilingQCParams +from ._tiling_qc import TilingQCParams, calculate_tiling_qc -__all__ = ["TilingQCParams", "calculate_tiling_qc"] __all__ = ["align", "align_by_landmarks", "calculate_tiling_qc", "TilingQCParams"] diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 7d31a3ad5..e1e18dc99 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -81,7 +81,7 @@ def align( ref_xy = get_coords(ref_adata, spatial_key) query_xy = get_coords(query_adata, spatial_key) - result = ALIGN_SAMPLES.get(method)().fit(ref_xy, query_xy, **method_kwargs) + result = ALIGN_SAMPLES.get(method)(ref_xy, query_xy, **method_kwargs) return writeback_obs( result, @@ -132,7 +132,7 @@ def align_by_landmarks( """ validate_output_mode(output_mode) - result = ALIGN_LANDMARKS.get(method)().fit( + result = ALIGN_LANDMARKS.get(method)( landmarks_ref, landmarks_query, source_cs=cs_name_query, diff --git a/tests/experimental/fit/test_core.py b/tests/experimental/fit/test_core.py index 89968f64a..b6c25a290 100644 --- a/tests/experimental/fit/test_core.py +++ b/tests/experimental/fit/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental._fit import Estimator, FitResult, Registry +from squidpy.experimental._fit import FitResult, Registry @dataclass @@ -20,21 +20,17 @@ def transform(self, x: np.ndarray) -> np.ndarray: return np.asarray(x, dtype=float) + self.delta -class _MeanShiftEstimator(Estimator): - """Toy estimator: fit the offset that maps the query centroid onto the ref centroid.""" - - name = "mean_shift" - - def fit(self, ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: - delta = np.asarray(ref, dtype=float).mean(0) - np.asarray(query, dtype=float).mean(0) - return _MeanShiftResult(delta=delta, metadata={"method": self.name}) +def fit_mean_shift(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: + """Toy estimator function: fit the offset that maps the query centroid onto the ref centroid.""" + delta = np.asarray(ref, dtype=float).mean(0) - np.asarray(query, dtype=float).mean(0) + return _MeanShiftResult(delta=delta, metadata={"method": "mean_shift"}) def test_fit_then_transform_round_trip() -> None: ref = np.array([[1.0, 1.0], [3.0, 3.0]]) # centroid (2, 2) query = np.array([[0.0, 0.0], [2.0, 2.0]]) # centroid (1, 1) - result = _MeanShiftEstimator().fit(ref, query) + result = fit_mean_shift(ref, query) assert isinstance(result, FitResult) np.testing.assert_allclose(result.delta, [1.0, 1.0]) @@ -55,17 +51,17 @@ def test_registry_register_get_keys() -> None: reg = Registry("demo") @reg.register("mean_shift") - class _Registered(_MeanShiftEstimator): - pass + def _registered(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: + return fit_mean_shift(ref, query) assert reg.keys() == ("mean_shift",) - assert reg.get("mean_shift") is _Registered - assert isinstance(reg.get("mean_shift")().fit(np.ones((2, 2)), np.zeros((2, 2))), FitResult) + assert reg.get("mean_shift") is _registered + assert isinstance(reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))), FitResult) def test_registry_unknown_key_lists_available() -> None: reg = Registry("demo") - reg.register("a")(_MeanShiftEstimator) + reg.register("a")(fit_mean_shift) with pytest.raises(ValueError, match=r"Unknown demo method 'b'. Available: \['a'\]"): reg.get("b") @@ -73,23 +69,28 @@ def test_registry_unknown_key_lists_available() -> None: def test_registry_rejects_duplicate_key() -> None: reg = Registry("demo") - reg.register("dup")(_MeanShiftEstimator) + reg.register("dup")(fit_mean_shift) with pytest.raises(ValueError, match="already registered"): - reg.register("dup")(_MeanShiftEstimator) + reg.register("dup")(fit_mean_shift) def test_check_requirements_passes_when_none() -> None: - # _MeanShiftEstimator has the default empty `requires`. - _MeanShiftEstimator().check_requirements() + reg = Registry("demo") + # By default, registering without requires parameter does not wrap/check. + reg.register("mean_shift")(fit_mean_shift) + result = reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))) + assert isinstance(result, FitResult) def test_check_requirements_raises_for_missing_dependency() -> None: - class _NeedsGhost(_MeanShiftEstimator): - name = "needs_ghost" - requires = ("squidpy_nonexistent_pkg_xyz",) + reg = Registry("demo") + + @reg.register("needs_ghost", requires=("squidpy_nonexistent_pkg_xyz",)) + def _needs_ghost(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: + return fit_mean_shift(ref, query) with pytest.raises( - ImportError, match=r"requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]" + ImportError, match=r"Method 'needs_ghost' requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]" ): - _NeedsGhost().check_requirements() + reg.get("needs_ghost")(np.ones((2, 2)), np.zeros((2, 2))) diff --git a/tests/experimental/fit/test_stalign.py b/tests/experimental/fit/test_stalign.py index f1d8f3bfb..382d2c4aa 100644 --- a/tests/experimental/fit/test_stalign.py +++ b/tests/experimental/fit/test_stalign.py @@ -11,7 +11,7 @@ pytest.importorskip("jax") -from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES, StalignEstimator, StalignFitResult +from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES, StalignFitResult, fit_stalign from squidpy.experimental._fit.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, @@ -41,13 +41,13 @@ def _tiny_config() -> STalignConfig: def test_stalign_registered_in_align_family() -> None: assert "stalign" in ALIGN_SAMPLES.keys() - assert ALIGN_SAMPLES.get("stalign") is StalignEstimator + assert ALIGN_SAMPLES.get("stalign") is fit_stalign def test_stalign_fit_returns_displacement_result() -> None: ref, query = _points_xy(), _points_xy() - result = StalignEstimator().fit(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, config=_tiny_config()) assert isinstance(result, StalignFitResult) assert result.deltas.shape == query.shape @@ -61,14 +61,14 @@ def test_stalign_fit_returns_displacement_result() -> None: def test_stalign_transform_bakes_in_deltas() -> None: ref, query = _points_xy(), _points_xy() - result = StalignEstimator().fit(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, config=_tiny_config()) np.testing.assert_allclose(result.transform(query), query + result.deltas) def test_stalign_transform_rejects_wrong_shape() -> None: ref, query = _points_xy(), _points_xy() - result = StalignEstimator().fit(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, config=_tiny_config()) with pytest.raises(ValueError, match="expects coordinates of shape"): result.transform(np.zeros((1, 2))) @@ -78,7 +78,7 @@ def test_stalign_fit_with_landmarks() -> None: ref, query = _points_xy(), _points_xy() landmarks = ref[:3] - result = StalignEstimator().fit( + result = fit_stalign( ref, query, config=_tiny_config(), @@ -92,4 +92,4 @@ def test_stalign_fit_with_landmarks() -> None: def test_stalign_fit_rejects_non_2d_input() -> None: with pytest.raises(ValueError, match=r"Expected `query` to be an \(N, 2\) array"): - StalignEstimator().fit(_points_xy(), np.zeros((5, 3)), config=_tiny_config()) + fit_stalign(_points_xy(), np.zeros((5, 3)), config=_tiny_config()) From 77f3e7d940486f6cd54295b10644be65a816df82 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:06:14 +0200 Subject: [PATCH 078/131] move files --- tests/experimental/{fit => _methods}/__init__.py | 0 tests/experimental/{fit => _methods}/test_core.py | 0 tests/experimental/{fit => _methods}/test_stalign.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/experimental/{fit => _methods}/__init__.py (100%) rename tests/experimental/{fit => _methods}/test_core.py (100%) rename tests/experimental/{fit => _methods}/test_stalign.py (100%) diff --git a/tests/experimental/fit/__init__.py b/tests/experimental/_methods/__init__.py similarity index 100% rename from tests/experimental/fit/__init__.py rename to tests/experimental/_methods/__init__.py diff --git a/tests/experimental/fit/test_core.py b/tests/experimental/_methods/test_core.py similarity index 100% rename from tests/experimental/fit/test_core.py rename to tests/experimental/_methods/test_core.py diff --git a/tests/experimental/fit/test_stalign.py b/tests/experimental/_methods/test_stalign.py similarity index 100% rename from tests/experimental/fit/test_stalign.py rename to tests/experimental/_methods/test_stalign.py From 57f9d21d52007ed98e338db6c18610bd99b7b711 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:06:21 +0200 Subject: [PATCH 079/131] fix imports --- src/squidpy/experimental/_methods/_families.py | 2 +- .../experimental/_methods/align_landmarks/_landmark.py | 4 ++-- src/squidpy/experimental/_methods/align_samples/_stalign.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/squidpy/experimental/_methods/_families.py b/src/squidpy/experimental/_methods/_families.py index 1399d9d2c..2ef6b01a5 100644 --- a/src/squidpy/experimental/_methods/_families.py +++ b/src/squidpy/experimental/_methods/_families.py @@ -9,7 +9,7 @@ from __future__ import annotations -from squidpy.experimental._fit._registry import Registry +from squidpy.experimental._methods._registry import Registry #: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. #: Consumed by ``squidpy.experimental.tl.align``. diff --git a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py index f3a7635c2..5da8f7ff4 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py @@ -20,8 +20,8 @@ import numpy as np -from squidpy.experimental._fit._families import ALIGN_LANDMARKS -from squidpy.experimental._fit._result import FitResult +from squidpy.experimental._methods._families import ALIGN_LANDMARKS +from squidpy.experimental._methods._result import FitResult @dataclass diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py index 003957390..e207a49f4 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -2,7 +2,7 @@ The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see :mod:`._stalign_impl`. This module only adds the thin :class:`Estimator` / -:class:`FitResult` adapter onto the :mod:`squidpy.experimental._fit` core. JAX +:class:`FitResult` adapter onto the :mod:`squidpy.experimental._methods` core. JAX is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module is cheap and does not require JAX. """ @@ -14,8 +14,8 @@ import numpy as np -from squidpy.experimental._fit._families import ALIGN_SAMPLES -from squidpy.experimental._fit._result import FitResult +from squidpy.experimental._methods._families import ALIGN_SAMPLES +from squidpy.experimental._methods._result import FitResult if TYPE_CHECKING: from ._stalign_impl._tools import STalignConfig From 5d69264c4518a1bafa9a18139c17b2e2e456ca92 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:07:47 +0200 Subject: [PATCH 080/131] fixes --- src/squidpy/experimental/_methods/__init__.py | 6 +++--- .../experimental/_methods/align_landmarks/__init__.py | 6 +++--- .../experimental/_methods/align_samples/__init__.py | 6 +++--- .../_methods/align_samples/_stalign_impl/__init__.py | 4 ++-- .../_methods/align_samples/_stalign_impl/_helpers.py | 2 +- .../_methods/align_samples/_stalign_impl/_tools.py | 2 +- src/squidpy/experimental/tl/_align/_api.py | 8 ++++---- src/squidpy/experimental/tl/_align/_io.py | 6 +++--- tests/experimental/_methods/test_core.py | 2 +- tests/experimental/_methods/test_stalign.py | 4 ++-- tests/experimental/tl/test_align.py | 4 ++-- tests/experimental/tl/test_align_by_landmarks.py | 2 +- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/squidpy/experimental/_methods/__init__.py b/src/squidpy/experimental/_methods/__init__.py index 5bf65cc3f..0e0839690 100644 --- a/src/squidpy/experimental/_methods/__init__.py +++ b/src/squidpy/experimental/_methods/__init__.py @@ -15,7 +15,7 @@ Example ------- >>> import numpy as np ->>> from squidpy.experimental._fit import FitResult, Registry +>>> from squidpy.experimental._methods import FitResult, Registry >>> demo = Registry("demo") >>> class Shift(FitResult): ... def __init__(self, delta): @@ -33,7 +33,7 @@ from __future__ import annotations -from squidpy.experimental._fit._registry import Registry -from squidpy.experimental._fit._result import FitResult +from squidpy.experimental._methods._registry import Registry +from squidpy.experimental._methods._result import FitResult __all__ = ["FitResult", "Registry"] diff --git a/src/squidpy/experimental/_methods/align_landmarks/__init__.py b/src/squidpy/experimental/_methods/align_landmarks/__init__.py index 36d355977..6b04a86f2 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/__init__.py +++ b/src/squidpy/experimental/_methods/align_landmarks/__init__.py @@ -1,14 +1,14 @@ """``align_landmarks`` family: closed-form alignment from paired landmarks. Importing this package registers the family's estimators into -:data:`~squidpy.experimental._fit._families.ALIGN_LANDMARKS`. Pure NumPy / +:data:`~squidpy.experimental._methods._families.ALIGN_LANDMARKS`. Pure NumPy / spatialdata / skimage -- no JAX. """ from __future__ import annotations -from squidpy.experimental._fit._families import ALIGN_LANDMARKS -from squidpy.experimental._fit.align_landmarks._landmark import ( +from squidpy.experimental._methods._families import ALIGN_LANDMARKS +from squidpy.experimental._methods.align_landmarks._landmark import ( AffineFitResult, fit_affine, fit_similarity, diff --git a/src/squidpy/experimental/_methods/align_samples/__init__.py b/src/squidpy/experimental/_methods/align_samples/__init__.py index 5c38b9cc3..e874c852e 100644 --- a/src/squidpy/experimental/_methods/align_samples/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/__init__.py @@ -1,13 +1,13 @@ """``align_samples`` family: align two samples' point clouds (STalign). Importing this package registers the family's estimators into -:data:`~squidpy.experimental._fit._families.ALIGN_SAMPLES`. It stays cheap -- +:data:`~squidpy.experimental._methods._families.ALIGN_SAMPLES`. It stays cheap -- JAX is pulled in lazily, only when an estimator's ``fit`` runs. """ from __future__ import annotations -from squidpy.experimental._fit._families import ALIGN_SAMPLES -from squidpy.experimental._fit.align_samples._stalign import StalignFitResult, fit_stalign +from squidpy.experimental._methods._families import ALIGN_SAMPLES +from squidpy.experimental._methods.align_samples._stalign import StalignFitResult, fit_stalign __all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignFitResult"] diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py index 958810ce7..61cb3d357 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py @@ -1,8 +1,8 @@ """Ported STalign JAX LDDMM solver (provenance: scverse/squidpy#1150). -Pure numerics only -- no :class:`~squidpy.experimental._fit.Estimator`. The +Pure numerics only -- no :class:`~squidpy.experimental._methods.Estimator`. The estimator adapter lives one level up in -:mod:`squidpy.experimental._fit.align_samples._stalign`. JAX is imported at +:mod:`squidpy.experimental._methods.align_samples._stalign`. JAX is imported at module load here, so this subpackage must only be imported lazily (the adapter does so inside ``fit``, after checking requirements). """ diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py index 0be6a63ec..aa173d1c4 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py @@ -2,7 +2,7 @@ Lifted from scverse/squidpy#1150 (Selman Özleyen). Container readers (``extract_points`` / ``extract_landmarks``) were dropped on the move into -:mod:`squidpy.experimental._fit`: reading coordinates out of an ``AnnData`` / +:mod:`squidpy.experimental._methods`: reading coordinates out of an ``AnnData`` / ``SpatialData`` is the caller's concern, so everything here operates purely on in-memory NumPy arrays. """ diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index 4501ac291..7e2c9d6f2 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -3,7 +3,7 @@ Lifted from scverse/squidpy#1150 (Selman Özleyen) with import paths adjusted and minor cleanups (config unpacking, lazy dtype resolution). The container write-back path (``STalignResult.transform_adata``) was dropped on the move -into :mod:`squidpy.experimental._fit`: results stay in memory and the caller +into :mod:`squidpy.experimental._methods`: results stay in memory and the caller owns write-back. """ diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index e1e18dc99..48cf5d062 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -1,4 +1,4 @@ -"""Public alignment functions built on the :mod:`squidpy.experimental._fit` core. +"""Public alignment functions built on the :mod:`squidpy.experimental._methods` core. These are thin orchestrators: resolve inputs to in-memory arrays, dispatch to a fit-core estimator, write the result back. All container I/O and write-back live @@ -14,8 +14,8 @@ from anndata import AnnData from spatialdata import SpatialData -from squidpy.experimental._fit.align_landmarks import ALIGN_LANDMARKS -from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES +from squidpy.experimental._methods.align_landmarks import ALIGN_LANDMARKS +from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, @@ -25,7 +25,7 @@ from squidpy.experimental.tl._align._validation import validate_on, validate_output_mode if TYPE_CHECKING: - from squidpy.experimental._fit import FitResult + from squidpy.experimental._methods import FitResult __all__ = ["align", "align_by_landmarks"] diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index ff3437065..c55ebfa22 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -2,7 +2,7 @@ This is the *only* layer that knows about the ``AnnData | SpatialData`` argument shapes and the ``output_mode`` write-back strategies. The fit estimators in -:mod:`squidpy.experimental._fit` operate on plain arrays and never see a +:mod:`squidpy.experimental._methods` operate on plain arrays and never see a container. """ @@ -17,8 +17,8 @@ from squidpy._validators import assert_key_in_sdata if TYPE_CHECKING: - from squidpy.experimental._fit import FitResult - from squidpy.experimental._fit.align_landmarks import AffineFitResult + from squidpy.experimental._methods import FitResult + from squidpy.experimental._methods.align_landmarks import AffineFitResult # --------------------------------------------------------------------------- diff --git a/tests/experimental/_methods/test_core.py b/tests/experimental/_methods/test_core.py index b6c25a290..284b588d4 100644 --- a/tests/experimental/_methods/test_core.py +++ b/tests/experimental/_methods/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental._fit import FitResult, Registry +from squidpy.experimental._methods import FitResult, Registry @dataclass diff --git a/tests/experimental/_methods/test_stalign.py b/tests/experimental/_methods/test_stalign.py index 382d2c4aa..a0a42af93 100644 --- a/tests/experimental/_methods/test_stalign.py +++ b/tests/experimental/_methods/test_stalign.py @@ -11,8 +11,8 @@ pytest.importorskip("jax") -from squidpy.experimental._fit.align_samples import ALIGN_SAMPLES, StalignFitResult, fit_stalign -from squidpy.experimental._fit.align_samples._stalign_impl._tools import ( +from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES, StalignFitResult, fit_stalign +from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 30a2051d5..a8d762ebf 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -12,8 +12,8 @@ pytest.importorskip("jax") -from squidpy.experimental._fit.align_samples import StalignFitResult -from squidpy.experimental._fit.align_samples._stalign_impl._tools import ( +from squidpy.experimental._methods.align_samples import StalignFitResult +from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index 05e3e472d..507f04e58 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -4,7 +4,7 @@ import pytest from anndata import AnnData -from squidpy.experimental._fit.align_landmarks import AffineFitResult +from squidpy.experimental._methods.align_landmarks import AffineFitResult from squidpy.experimental.tl import align_by_landmarks # square corners; query = ref shifted by (5, 7) -> a pure translation both models recover From 6587efed9395f9e77fb6ede2d14504666135936d Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:15:58 +0200 Subject: [PATCH 081/131] remove bloat code --- .../align_samples/_stalign_impl/_tools.py | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index 7e2c9d6f2..f31177c14 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -41,7 +41,6 @@ "STalignResult", "stalign_points", "stalign_preprocess", - "transform_points", ] @@ -107,14 +106,16 @@ def transform_points( point_order: PointOrder | None = None, ) -> JaxArray: """Transform arbitrary point arrays with the fitted map.""" - return transform_points( + order = self.point_order if point_order is None else point_order + points_rc = to_row_col(points, point_order=order) + transformed = transform_points_row_col( self.velocity_grid, - self.velocity, - self.affine, - points, + jnp.asarray(self.velocity), + jnp.asarray(self.affine), + jnp.asarray(points_rc, dtype=jax_dtype()), direction=direction, - point_order=self.point_order if point_order is None else point_order, ) + return jnp.asarray(from_row_col(np.asarray(transformed), point_order=order)) def stalign_preprocess( @@ -151,27 +152,6 @@ def stalign_preprocess( ) -def transform_points( - xv: tuple[JaxArray, JaxArray], - v: JaxArray, - A: JaxArray, - points: np.ndarray, - *, - direction: Literal["forward", "backward"] = "forward", - point_order: PointOrder = "row_col", -) -> JaxArray: - """Transform point arrays with a fitted STalign map.""" - points_rc = to_row_col(points, point_order=point_order) - transformed = transform_points_row_col( - xv, - jnp.asarray(v), - jnp.asarray(A), - jnp.asarray(points_rc, dtype=jax_dtype()), - direction=direction, - ) - return jnp.asarray(from_row_col(np.asarray(transformed), point_order=point_order)) - - def stalign_points( source_points: np.ndarray, target_points: np.ndarray, @@ -214,14 +194,15 @@ def stalign_points( points_target=None if target_landmarks is None else jnp.asarray(target_landmarks, dtype=dtype), **asdict(registration), ) - aligned_points = transform_points( + points_rc = to_row_col(source_points, point_order="row_col") + transformed = transform_points_row_col( result["xv"], - result["v"], - result["A"], - source_points, + jnp.asarray(result["v"]), + jnp.asarray(result["A"]), + jnp.asarray(points_rc, dtype=dtype), direction="forward", - point_order="row_col", ) + aligned_points = jnp.asarray(from_row_col(np.asarray(transformed), point_order="row_col")) return STalignResult( affine=result["A"], velocity=result["v"], From e45e247d6e848dfd7feda7c5beaf8060e18955a1 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:18:48 +0200 Subject: [PATCH 082/131] typr hints --- .../align_samples/_stalign_impl/__init__.py | 8 +- .../align_samples/_stalign_impl/_core.py | 103 +++++++++--------- 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py index 61cb3d357..26c72f7e2 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py @@ -1,10 +1,6 @@ -"""Ported STalign JAX LDDMM solver (provenance: scverse/squidpy#1150). +"""Ported STalign JAX LDDMM solver -Pure numerics only -- no :class:`~squidpy.experimental._methods.Estimator`. The -estimator adapter lives one level up in -:mod:`squidpy.experimental._methods.align_samples._stalign`. JAX is imported at -module load here, so this subpackage must only be imported lazily (the adapter -does so inside ``fit``, after checking requirements). +Pure numerics only and these functions are gated by the jax requirement. """ from __future__ import annotations diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py index 8ac139b6c..0b98d4255 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py @@ -20,7 +20,7 @@ def jax_dtype() -> jnp.dtype: return jnp.float64 if jax.config.x64_enabled else jnp.float32 -def _to_affine(linear: Any, translation: Any) -> Any: +def _to_affine(linear: jax.Array, translation: jax.Array) -> jax.Array: return jnp.array( [ [linear[0, 0], linear[0, 1], translation[0]], @@ -31,23 +31,24 @@ def _to_affine(linear: Any, translation: Any) -> Any: ) -def _grid_points(x: tuple[Any, Any]) -> Any: +def _grid_points(x: tuple[jax.Array, jax.Array]) -> jax.Array: yy, xx = jnp.meshgrid(x[0], x[1], indexing="ij") return jnp.stack((yy, xx)) def _interp( - x: tuple[Any, Any], - image: Any, - phii: Any, + x: tuple[jax.Array, jax.Array], + image: jax.Array, + phii: jax.Array, *, mode: str = "nearest", -) -> Any: +) -> jax.Array: """Interpolate a channels-first image on physical row-column coordinates.""" arr = jnp.asarray(image) coords = jnp.asarray(phii) if coords.shape[0] != 2: raise ValueError(f"Expected interpolation coordinates to have leading axis of size 2, found `{coords.shape}`.") + if arr.ndim == 2: arr = arr[None, ...] @@ -57,7 +58,7 @@ def _interp( col_idx = (coords[1] - x[1][0]) / col_step idx = jnp.stack((row_idx.reshape(-1), col_idx.reshape(-1))) - def _sample(channel: Any) -> Any: + def _sample(channel: jax.Array) -> jax.Array: values = jsp.ndimage.map_coordinates(channel, idx, order=1, mode=mode) return values.reshape(coords.shape[1:]) @@ -65,13 +66,13 @@ def _sample(channel: Any) -> Any: def transform_points_row_col( - xv: tuple[Any, Any], - velocity: Any, - affine: Any, - points: Any, + xv: tuple[jax.Array, jax.Array], + velocity: jax.Array, + affine: jax.Array, + points: np.ndarray | jax.Array, *, direction: Literal["forward", "backward"] = "forward", -) -> Any: +) -> jax.Array: pts = jnp.asarray(points) n_steps = velocity.shape[0] time_steps = range(n_steps) @@ -98,11 +99,11 @@ def transform_points_row_col( def _transform_grid_backward( - x_target: tuple[Any, Any], - xv: tuple[Any, Any], - velocity: Any, - affine: Any, -) -> Any: + x_target: tuple[jax.Array, jax.Array], + xv: tuple[jax.Array, jax.Array], + velocity: jax.Array, + affine: jax.Array, +) -> jax.Array: target_grid = _grid_points(x_target) affine_inv = jnp.linalg.inv(affine) source_grid = jnp.einsum("ij,jhw->ihw", affine_inv[:2, :2], target_grid) + affine_inv[:2, -1][:, None, None] @@ -114,7 +115,7 @@ def _transform_grid_backward( return source_grid -def _contrast_transform(source_image: Any, target_image: Any, weights: Any) -> Any: +def _contrast_transform(source_image: jax.Array, target_image: jax.Array, weights: jax.Array) -> jax.Array: flat_source = source_image.reshape(source_image.shape[0], -1) flat_target = target_image.reshape(target_image.shape[0], -1) flat_weights = weights.reshape(-1) @@ -128,7 +129,7 @@ def _contrast_transform(source_image: Any, target_image: Any, weights: Any) -> A return (coefficients.T @ design).reshape(target_image.shape) -def _build_velocity_grid(x_source: tuple[Any, Any], *, a: float, expand: float) -> tuple[Any, Any]: +def _build_velocity_grid(x_source: tuple[jax.Array, jax.Array], *, a: float, expand: float) -> tuple[jax.Array, jax.Array]: minimum = jnp.array([x_source[0][0], x_source[1][0]]) maximum = jnp.array([x_source[0][-1], x_source[1][-1]]) center = (minimum + maximum) / 2.0 @@ -141,11 +142,11 @@ def _build_velocity_grid(x_source: tuple[Any, Any], *, a: float, expand: float) def _build_regularizer( - xv: tuple[Any, Any], + xv: tuple[jax.Array, jax.Array], *, a: float, p: float, -) -> tuple[Any, Any, Any]: +) -> tuple[jax.Array, jax.Array, float | jax.Array]: dv = jnp.array([xv[0][1] - xv[0][0], xv[1][1] - xv[1][0]]) shape = (xv[0].shape[0], xv[1].shape[0]) fy = jnp.arange(shape[0], dtype=xv[0].dtype) / (shape[0] * dv[0]) @@ -160,21 +161,21 @@ def _build_regularizer( def _update_mixture_weights( - transformed_source: Any, - target_image: Any, - match_weights: Any, - artifact_weights: Any, - background_weights: Any, + transformed_source: jax.Array, + target_image: jax.Array, + match_weights: jax.Array, + artifact_weights: jax.Array, + background_weights: jax.Array, *, sigmaM: float, sigmaA: float, sigmaB: float, estimate_muA: bool, estimate_muB: bool, - muA: Any, - muB: Any, + muA: jax.Array, + muB: jax.Array, iteration: int, -) -> tuple[Any, Any, Any, Any, Any]: +) -> tuple[jax.Array, jax.Array, jax.Array, jax.Array, jax.Array]: if estimate_muA: muA = jnp.sum(artifact_weights * target_image, axis=(-1, -2)) / jnp.maximum(jnp.sum(artifact_weights), 1e-12) if estimate_muB: @@ -212,24 +213,24 @@ def _update_mixture_weights( def _lddmm_loss( - linear: Any, - translation: Any, - velocity: Any, + linear: jax.Array, + translation: jax.Array, + velocity: jax.Array, *, - x_source: tuple[Any, Any], - source_image: Any, - x_target: tuple[Any, Any], - target_image: Any, - xv: tuple[Any, Any], - match_weights: Any, - ll: Any, - dv_prod: Any, - points_source: Any, - points_target: Any, + x_source: tuple[jax.Array, jax.Array], + source_image: jax.Array, + x_target: tuple[jax.Array, jax.Array], + target_image: jax.Array, + xv: tuple[jax.Array, jax.Array], + match_weights: jax.Array, + ll: jax.Array, + dv_prod: float | jax.Array, + points_source: jax.Array, + points_target: jax.Array, sigmaM: float, sigmaR: float, sigmaP: float, -) -> tuple[Any, tuple[Any, Any, Any, Any, Any]]: +) -> tuple[jax.Array, tuple[jax.Array, jax.Array, jax.Array, jax.Array, jax.Array]]: affine = _to_affine(linear, translation) source_grid = _transform_grid_backward(x_target, xv, velocity, affine) warped_source = _interp(x_source, source_image, source_grid, mode="nearest") @@ -257,15 +258,15 @@ def _lddmm_loss( def lddmm( - xI: tuple[Any, Any], - I: Any, - xJ: tuple[Any, Any], - J: Any, + xI: tuple[np.ndarray | jax.Array, np.ndarray | jax.Array], + I: np.ndarray | jax.Array, + xJ: tuple[np.ndarray | jax.Array, np.ndarray | jax.Array], + J: np.ndarray | jax.Array, *, - L: Any, - T: Any, - points_source: Any | None = None, - points_target: Any | None = None, + L: np.ndarray | jax.Array, + T: np.ndarray | jax.Array, + points_source: np.ndarray | jax.Array | None = None, + points_target: np.ndarray | jax.Array | None = None, a: float = 500.0, p: float = 2.0, expand: float = 2.0, From beb1476f154e2cf6196743dfac66a666af1e0328 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:34:45 +0200 Subject: [PATCH 083/131] add tests --- .../align_samples/_stalign_impl/_helpers.py | 7 ++++--- tests/experimental/tl/test_align.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py index aa173d1c4..78d509ba1 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py @@ -154,7 +154,8 @@ def affine_from_points( translation = np.mean(target, axis=0) - np.mean(source, axis=0) return linear, translation - source_h = np.concatenate((source, np.ones((source.shape[0], 1), dtype=float)), axis=1) - target_h = np.concatenate((target, np.ones((target.shape[0], 1), dtype=float)), axis=1) - affine = np.linalg.lstsq(source_h, target_h, rcond=None)[0].T + from skimage.transform import estimate_transform + + model_obj = estimate_transform("affine", src=source, dst=target) + affine = np.asarray(model_obj.params) return affine[:2, :2], affine[:2, -1] diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index a8d762ebf..213d2bbd0 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -81,3 +81,22 @@ def test_missing_spatial_key() -> None: ref, query = _adata(), _adata() with pytest.raises(KeyError, match="missing.*not found"): align(ref, query, spatial_key="missing", config=_tiny()) + + +def test_align_with_landmarks() -> None: + ref, query = _adata(), _adata() + landmarks = ref.obsm["spatial"][:3] + + result = align( + ref, + query, + method="stalign", + output_mode="object", + landmarks_source=landmarks, + landmarks_target=landmarks, + config=_tiny(), + ) + + assert isinstance(result, StalignFitResult) + assert result.deltas.shape == query.obsm["spatial"].shape + assert "stalign_result" in result.metadata From 5eeb95d4a6516045e610844e88a4590f9472fef5 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:50:27 +0200 Subject: [PATCH 084/131] reduce bloat --- src/squidpy/experimental/_methods/__init__.py | 33 +--- src/squidpy/experimental/_methods/_result.py | 32 ---- .../_methods/align_landmarks/_landmark.py | 3 +- .../_methods/align_samples/__init__.py | 4 +- .../_methods/align_samples/_stalign.py | 95 +++------- .../align_samples/_stalign_impl/_helpers.py | 170 +++++++++--------- .../align_samples/_stalign_impl/_tools.py | 129 ++++++------- src/squidpy/experimental/tl/_align/_api.py | 11 +- src/squidpy/experimental/tl/_align/_io.py | 8 +- tests/experimental/_methods/test_core.py | 18 +- tests/experimental/_methods/test_stalign.py | 42 +++-- tests/experimental/tl/test_align.py | 11 +- 12 files changed, 235 insertions(+), 321 deletions(-) delete mode 100644 src/squidpy/experimental/_methods/_result.py diff --git a/src/squidpy/experimental/_methods/__init__.py b/src/squidpy/experimental/_methods/__init__.py index 0e0839690..ab5d10038 100644 --- a/src/squidpy/experimental/_methods/__init__.py +++ b/src/squidpy/experimental/_methods/__init__.py @@ -1,39 +1,20 @@ """In-memory model-fitting core for experimental squidpy tools. This subpackage factors the *fit* half of fitting-style tools (alignment, -imputation, ...) out of any particular container or write-back path: - -- :class:`FitResult` -- the fitted parameters plus an optional, pure in-memory - :meth:`~FitResult.transform`. Container write-back lives in the calling - function, not here. -- :class:`Registry` -- a flat ``name -> function`` map, one per family. +imputation, ...) out of any particular container or write-back path. Each +family registers its estimators into a flat :class:`Registry` (``name -> +function``). An estimator returns a plain result dataclass that carries the +fitted parameters and a ``transform`` method; turning that into mutations on an +:class:`~anndata.AnnData` / :class:`~spatialdata.SpatialData` is the calling +function's job, not the result's. Concrete estimators live in per-family subpackages (``align_samples``, ``align_landmarks``, ...) and pull their optional dependencies (e.g. JAX) in lazily, so importing this package stays cheap. - -Example -------- ->>> import numpy as np ->>> from squidpy.experimental._methods import FitResult, Registry ->>> demo = Registry("demo") ->>> class Shift(FitResult): -... def __init__(self, delta): -... self.metadata, self.delta = {}, delta -... -... def transform(self, x): -... return np.asarray(x) + self.delta ->>> @demo.register("mean_shift") -... def fit_mean_shift(ref, query): -... return Shift(ref.mean(0) - query.mean(0)) ->>> result = demo.get("mean_shift")(np.ones((3, 2)), np.zeros((3, 2))) ->>> result.transform(np.zeros((3, 2))).tolist() -[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]] """ from __future__ import annotations from squidpy.experimental._methods._registry import Registry -from squidpy.experimental._methods._result import FitResult -__all__ = ["FitResult", "Registry"] +__all__ = ["Registry"] diff --git a/src/squidpy/experimental/_methods/_result.py b/src/squidpy/experimental/_methods/_result.py deleted file mode 100644 index 861f3cbe4..000000000 --- a/src/squidpy/experimental/_methods/_result.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Result contract for fitted estimators.""" - -from __future__ import annotations - -from abc import ABC -from typing import Any - - -class FitResult(ABC): - """Base class for the output of :meth:`Estimator.fit`. - - A ``FitResult`` carries the *fitted parameters* of a model -- pointers or - parametric forms (an affine matrix, a displacement field, neighbour - weights), not bulk data -- together with free-form :attr:`metadata`. - - It is deliberately container-agnostic. Turning a result into mutations on - an :class:`~anndata.AnnData` / :class:`~spatialdata.SpatialData` - (write-back) is the caller's responsibility, not the result's: write-back - is a general squidpy concern with several unrelated shapes (set a field, - apply a transformation to a lazy element, ...) and does not belong on the - fit result. - """ - - metadata: dict[str, Any] - - def transform(self, x: Any) -> Any: - """Apply the fitted model to in-memory data ``x`` and return the result. - - Optional. Estimators whose fit does not yield a reusable map (or that - only produce side outputs) need not implement it. - """ - raise NotImplementedError(f"{type(self).__name__} does not implement `transform`.") diff --git a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py index 5da8f7ff4..dde043a9d 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py @@ -21,11 +21,10 @@ import numpy as np from squidpy.experimental._methods._families import ALIGN_LANDMARKS -from squidpy.experimental._methods._result import FitResult @dataclass -class AffineFitResult(FitResult): +class AffineFitResult: """A fitted ``(3, 3)`` homogeneous affine mapping query onto ref, in ``(x, y)``.""" matrix: np.ndarray diff --git a/src/squidpy/experimental/_methods/align_samples/__init__.py b/src/squidpy/experimental/_methods/align_samples/__init__.py index e874c852e..eb73741f0 100644 --- a/src/squidpy/experimental/_methods/align_samples/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/__init__.py @@ -8,6 +8,6 @@ from __future__ import annotations from squidpy.experimental._methods._families import ALIGN_SAMPLES -from squidpy.experimental._methods.align_samples._stalign import StalignFitResult, fit_stalign +from squidpy.experimental._methods.align_samples._stalign import fit_stalign -__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignFitResult"] +__all__ = ["ALIGN_SAMPLES", "fit_stalign"] diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py index e207a49f4..8b82bd813 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -1,67 +1,31 @@ """STalign estimator: JAX LDDMM point-cloud registration. The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see -:mod:`._stalign_impl`. This module only adds the thin :class:`Estimator` / -:class:`FitResult` adapter onto the :mod:`squidpy.experimental._methods` core. JAX -is imported lazily inside :meth:`StalignEstimator.fit`, so importing this module -is cheap and does not require JAX. +:mod:`._stalign_impl`. This module only adds the thin registry entry that swaps +``(x, y)`` inputs into the solver's row-column frame and returns the fitted +:class:`StalignResult`. JAX is imported lazily inside :func:`fit_stalign`, so +importing this module is cheap and does not require JAX. """ from __future__ import annotations -from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any -import numpy as np - from squidpy.experimental._methods._families import ALIGN_SAMPLES -from squidpy.experimental._methods._result import FitResult if TYPE_CHECKING: - from ._stalign_impl._tools import STalignConfig - - -@dataclass -class StalignFitResult(FitResult): - """Fitted STalign deformation, in ``(x, y)`` convention. - - ``deltas`` is the per-point displacement that maps the *query* points into - the *reference* frame; :meth:`transform` bakes it in. STalign produces a - diffeomorphic (non-affine) map, so ``affine`` is ``None`` here -- the full - map (velocity field, velocity grid, affine initialisation) is kept under - ``metadata['stalign_result']`` for power users who need to transform - arbitrary points via ``stalign_result.transform_points``. - """ - - deltas: np.ndarray - affine: np.ndarray | None = None - metadata: dict[str, Any] = field(default_factory=dict) - - def transform(self, x: np.ndarray) -> np.ndarray: - """Apply the fitted displacement to the ``(N, 2)`` ``(x, y)`` points it was fit on. - - ``deltas`` is per-point, so ``x`` must have the same shape as the query - cloud passed to :meth:`fit_stalign`. To map *arbitrary* points, - use ``metadata['stalign_result'].transform_points``. - """ - coords = np.asarray(x, dtype=float) - if coords.shape != self.deltas.shape: - raise ValueError( - f"`transform` expects coordinates of shape {self.deltas.shape} (the fitted query cloud), " - f"found {coords.shape}. Use `metadata['stalign_result'].transform_points` for arbitrary points." - ) - return coords + self.deltas + from ._stalign_impl._tools import STalignConfig, StalignResult @ALIGN_SAMPLES.register("stalign", requires=("jax",)) def fit_stalign( - ref: np.ndarray, - query: np.ndarray, + ref: Any, + query: Any, *, config: STalignConfig | None = None, - landmarks_source: np.ndarray | None = None, - landmarks_target: np.ndarray | None = None, -) -> StalignFitResult: + landmarks_source: Any | None = None, + landmarks_target: Any | None = None, +) -> StalignResult: """Fit a deformation mapping ``query`` onto ``ref``. Parameters @@ -80,38 +44,29 @@ def fit_stalign( Returns ------- - A :class:`StalignFitResult` whose ``deltas`` move ``query`` into the - reference frame. + A :class:`StalignResult` whose :meth:`~StalignResult.transform` maps + ``(x, y)`` points into the reference frame; ``aligned_points`` is the fitted + ``query`` already mapped. """ # Import the JAX-backed solver only after requirements pass, so callers # without JAX get the clean ImportError from check_requirements rather # than a confusing failure from a module-level `import jax`. + import jax.numpy as jnp + + from ._stalign_impl._helpers import validate_points from ._stalign_impl._tools import stalign_points - ref_xy = np.asarray(ref, dtype=float) - query_xy = np.asarray(query, dtype=float) - for arr_name, arr in (("ref", ref_xy), ("query", query_xy)): - if arr.ndim != 2 or arr.shape[1] != 2: - raise ValueError(f"Expected `{arr_name}` to be an (N, 2) array, found shape {arr.shape}.") + ref_xy = validate_points(ref, name="ref") + query_xy = validate_points(query, name="query") # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. - src_rc = query_xy[:, [1, 0]] - tgt_rc = ref_xy[:, [1, 0]] - lm_src_rc = None if landmarks_source is None else np.asarray(landmarks_source, dtype=float)[:, [1, 0]] - lm_tgt_rc = None if landmarks_target is None else np.asarray(landmarks_target, dtype=float)[:, [1, 0]] + lm_src = None if landmarks_source is None else jnp.asarray(landmarks_source)[:, ::-1] + lm_tgt = None if landmarks_target is None else jnp.asarray(landmarks_target)[:, ::-1] - result = stalign_points( - source_points=src_rc, - target_points=tgt_rc, + return stalign_points( + source_points=query_xy[:, ::-1], + target_points=ref_xy[:, ::-1], config=config, - landmarks_source=lm_src_rc, - landmarks_target=lm_tgt_rc, - ) - - aligned_xy = np.asarray(result.aligned_points)[:, [1, 0]] - deltas_xy = aligned_xy - query_xy - return StalignFitResult( - deltas=deltas_xy, - affine=None, - metadata={"method": "stalign", "stalign_result": result}, + landmarks_source=lm_src, + landmarks_target=lm_tgt, ) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py index 78d509ba1..3b2011b62 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py @@ -4,77 +4,62 @@ (``extract_points`` / ``extract_landmarks``) were dropped on the move into :mod:`squidpy.experimental._methods`: reading coordinates out of an ``AnnData`` / ``SpatialData`` is the caller's concern, so everything here operates purely on -in-memory NumPy arrays. +in-memory arrays. Rasterization runs on JAX so the point cloud never leaves the +device between preprocessing and the LDDMM solve. """ from __future__ import annotations -from typing import Literal +from typing import TYPE_CHECKING, Any +import jax.numpy as jnp import numpy as np -PointOrder = Literal["row_col", "xy"] +from ._core import jax_dtype + +if TYPE_CHECKING: + import jax + + JaxArray = jax.Array +else: # pragma: no cover - typing only + JaxArray = Any __all__ = [ - "PointOrder", "affine_from_points", - "from_row_col", "rasterize", - "to_row_col", + "validate_points", ] -def _validate_points(points: np.ndarray, *, name: str) -> np.ndarray: - arr = np.asarray(points, dtype=float) +def validate_points(points: Any, *, name: str) -> JaxArray: + """Coerce ``points`` to a finite ``(n, 2)`` JAX array.""" + arr = jnp.asarray(points, dtype=jax_dtype()) if arr.ndim != 2 or arr.shape[1] != 2: raise ValueError(f"Expected `{name}` to have shape `(n, 2)`, found `{arr.shape}`.") - if not np.all(np.isfinite(arr)): + if not bool(jnp.all(jnp.isfinite(arr))): raise ValueError(f"Expected `{name}` to contain only finite values.") return arr -def to_row_col(points: np.ndarray, *, point_order: PointOrder) -> np.ndarray: - """Convert coordinates to row-column order.""" - arr = _validate_points(points, name="points") - if point_order == "row_col": - return arr - if point_order == "xy": - return arr[:, [1, 0]] - raise ValueError(f"Unknown `point_order`: `{point_order}`.") - - -def from_row_col(points: np.ndarray, *, point_order: PointOrder) -> np.ndarray: - """Convert row-column coordinates to the requested order.""" - arr = _validate_points(points, name="points") - if point_order == "row_col": - return arr - if point_order == "xy": - return arr[:, [1, 0]] - raise ValueError(f"Unknown `point_order`: `{point_order}`.") - - -def _normalize(values: np.ndarray) -> np.ndarray: - values = np.asarray(values, dtype=float) - vmin = np.min(values) - vmax = np.max(values) - if np.isclose(vmin, vmax): - return np.ones_like(values, dtype=float) - return (values - vmin) / (vmax - vmin) - - def rasterize( - x: np.ndarray, - y: np.ndarray, + x: JaxArray, + y: JaxArray, *, - g: np.ndarray | None = None, + g: JaxArray | None = None, dx: float = 30.0, blur: float | list[float] = 1.0, expand: float = 1.1, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - """Rasterize a point cloud into a multi-scale density image.""" - x = np.asarray(x, dtype=float) - y = np.asarray(y, dtype=float) - if x.ndim != 1 or y.ndim != 1 or x.shape != y.shape: +) -> tuple[JaxArray, JaxArray, JaxArray]: + """Rasterize a point cloud into a multi-scale Gaussian density image. + + Each point splats a normalized Gaussian over a fixed ``(2r + 1)`` patch and + the patches are scatter-added onto the grid -- the JAX analogue of the + original per-point NumPy loop. Out-of-grid patch pixels are masked out. + """ + dtype = jax_dtype() + x = jnp.asarray(x, dtype=dtype).reshape(-1) + y = jnp.asarray(y, dtype=dtype).reshape(-1) + if x.shape != y.shape: raise ValueError("Expected `x` and `y` to be 1D arrays with the same length.") if x.size == 0: raise ValueError("Expected at least one point to rasterize.") @@ -83,66 +68,91 @@ def rasterize( if expand <= 0: raise ValueError("Expected `expand` to be positive.") - blur_values = np.atleast_1d(np.asarray(blur, dtype=float)) - if blur_values.ndim != 1 or np.any(blur_values <= 0): + blur_values = jnp.atleast_1d(jnp.asarray(blur, dtype=dtype)) + if blur_values.ndim != 1 or bool(jnp.any(blur_values <= 0)): raise ValueError("Expected `blur` to be a positive scalar or a 1D sequence of positive values.") if g is None: - weights = np.ones_like(x, dtype=float) + weights = jnp.ones_like(x) else: - weights = np.asarray(g, dtype=float) + weights = jnp.asarray(g, dtype=dtype).reshape(-1) if weights.shape != x.shape: raise ValueError("Expected `g` to have the same shape as `x` and `y`.") - if not np.allclose(weights, 1.0): + if not bool(jnp.allclose(weights, 1.0)): weights = _normalize(weights) - min_x = float(np.min(x)) - max_x = float(np.max(x)) - min_y = float(np.min(y)) - max_y = float(np.max(y)) + min_x = float(jnp.min(x)) + max_x = float(jnp.max(x)) + min_y = float(jnp.min(y)) + max_y = float(jnp.max(y)) center_x = (min_x + max_x) / 2.0 center_y = (min_y + max_y) / 2.0 half_x = (max_x - min_x) * expand / 2.0 half_y = (max_y - min_y) * expand / 2.0 - grid_x = np.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=float) - grid_y = np.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=float) - if grid_x.size < 2 or grid_y.size < 2: + grid_x = jnp.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=dtype) + grid_y = jnp.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=dtype) + n_cols = int(grid_x.shape[0]) + n_rows = int(grid_y.shape[0]) + if n_cols < 2 or n_rows < 2: raise ValueError("Rasterized grid is too small. Increase the point spread or lower `dx`.") - mesh_x, mesh_y = np.meshgrid(grid_x, grid_y) - out = np.zeros((len(blur_values), grid_y.size, grid_x.size), dtype=float) - radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) + radius = int(np.ceil(float(jnp.max(blur_values)) * 4.0)) + offsets = jnp.arange(-radius, radius + 1) + + # Nearest grid cell for every point, then its fixed (2r+1) patch indices. + col0 = jnp.round((x - grid_x[0]) / dx).astype(jnp.int32) # (N,) + row0 = jnp.round((y - grid_y[0]) / dx).astype(jnp.int32) # (N,) + patch_cols = col0[:, None] + offsets[None, :] # (N, K) + patch_rows = row0[:, None] + offsets[None, :] # (N, K) - for x_i, y_i, w_i in zip(x, y, weights, strict=False): - col = int(np.rint((x_i - grid_x[0]) / dx)) - row = int(np.rint((y_i - grid_y[0]) / dx)) + patch_x = grid_x[0] + patch_cols.astype(dtype) * dx # (N, K) + patch_y = grid_y[0] + patch_rows.astype(dtype) * dx # (N, K) - row0 = max(row - radius, 0) - row1 = min(row + radius, out.shape[1] - 1) - col0 = max(col - radius, 0) - col1 = min(col + radius, out.shape[2] - 1) + # (N, K, K) squared distance from each patch pixel to its source point. + dist2 = (patch_x[:, None, :] - x[:, None, None]) ** 2 + (patch_y[:, :, None] - y[:, None, None]) ** 2 - patch_x = mesh_x[row0 : row1 + 1, col0 : col1 + 1] - patch_y = mesh_y[row0 : row1 + 1, col0 : col1 + 1] - denom = 2.0 * (dx * blur_values * 2.0) ** 2 + denom = 2.0 * (dx * blur_values * 2.0) ** 2 # (B,) + kernels = jnp.exp(-dist2[None] / denom[:, None, None, None]) # (B, N, K, K) + kernels_sum = kernels.sum(axis=(2, 3), keepdims=True) + kernels = kernels / jnp.where(kernels_sum == 0.0, 1.0, kernels_sum) + kernels = kernels * weights[None, :, None, None] - kernels = np.exp(-((patch_x[..., None] - x_i) ** 2 + (patch_y[..., None] - y_i) ** 2) / denom) - kernels_sum = kernels.sum(axis=(0, 1), keepdims=True) - kernels /= np.where(kernels_sum == 0.0, 1.0, kernels_sum) - out[:, row0 : row1 + 1, col0 : col1 + 1] += np.moveaxis(kernels * w_i, -1, 0) + valid = ( + (patch_rows >= 0)[:, :, None] + & (patch_rows < n_rows)[:, :, None] + & (patch_cols >= 0)[:, None, :] + & (patch_cols < n_cols)[:, None, :] + ) # (N, K, K) + kernels = kernels * valid[None] + rows = jnp.broadcast_to(jnp.clip(patch_rows, 0, n_rows - 1)[:, :, None], dist2.shape) # (N, K, K) + cols = jnp.broadcast_to(jnp.clip(patch_cols, 0, n_cols - 1)[:, None, :], dist2.shape) # (N, K, K) + + out = jnp.zeros((blur_values.shape[0], n_rows, n_cols), dtype=dtype) + out = out.at[:, rows, cols].add(kernels) return grid_x, grid_y, out +def _normalize(values: JaxArray) -> JaxArray: + vmin = jnp.min(values) + vmax = jnp.max(values) + return jnp.where(jnp.isclose(vmin, vmax), jnp.ones_like(values), (values - vmin) / (vmax - vmin)) + + def affine_from_points( - points_source: np.ndarray, - points_target: np.ndarray, + points_source: JaxArray, + points_target: JaxArray, ) -> tuple[np.ndarray, np.ndarray]: - """Compute an affine initialization from corresponding landmarks.""" - source = _validate_points(points_source, name="points_source") - target = _validate_points(points_target, name="points_target") + """Compute an affine initialization from corresponding landmarks. + + Delegates to skimage's least-squares estimator, so this is the one place + that drops to NumPy on the CPU; the small ``(2, 2)`` / ``(2,)`` result is + lifted back to JAX by the caller. + """ + source = np.asarray(points_source, dtype=float) + target = np.asarray(points_target, dtype=float) if source.shape != target.shape: raise ValueError( f"Expected `points_source` and `points_target` to have the same shape, found " diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index f31177c14..532b44c5a 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -1,10 +1,11 @@ """Low-level point-cloud tools for experimental STalign. Lifted from scverse/squidpy#1150 (Selman Özleyen) with import paths adjusted -and minor cleanups (config unpacking, lazy dtype resolution). The container -write-back path (``STalignResult.transform_adata``) was dropped on the move -into :mod:`squidpy.experimental._methods`: results stay in memory and the caller -owns write-back. +and minor cleanups. The fitted map is returned as a single :class:`StalignResult` +container that keeps everything on JAX -- the affine, velocity field and +velocity grid stay device-side and :meth:`StalignResult.transform` applies them +to arbitrary points without round-tripping through NumPy. Container write-back +lives in the caller, not here. """ from __future__ import annotations @@ -13,16 +14,9 @@ from typing import TYPE_CHECKING, Any, Literal, TypeAlias import jax.numpy as jnp -import numpy as np from ._core import jax_dtype, lddmm, transform_points_row_col -from ._helpers import ( - PointOrder, - affine_from_points, - from_row_col, - rasterize, - to_row_col, -) +from ._helpers import affine_from_points, rasterize, validate_points if TYPE_CHECKING: import jax @@ -38,7 +32,7 @@ "STalignPreprocessConfig", "STalignPreprocessResult", "STalignRegistrationConfig", - "STalignResult", + "StalignResult", "stalign_points", "stalign_preprocess", ] @@ -84,50 +78,58 @@ class STalignConfig: @dataclass(slots=True) class STalignPreprocessResult: - source_grid: tuple[np.ndarray, np.ndarray] - source_image: np.ndarray - target_grid: tuple[np.ndarray, np.ndarray] - target_image: np.ndarray + source_grid: tuple[JaxArray, JaxArray] + source_image: JaxArray + target_grid: tuple[JaxArray, JaxArray] + target_image: JaxArray @dataclass(slots=True) -class STalignResult: +class StalignResult: + """A fitted STalign diffeomorphism, ready to transform arbitrary points. + + The map (``affine`` + diffeomorphic ``velocity`` field on ``velocity_grid``) + is held as JAX arrays in the solver's internal row-column frame. + :meth:`transform` takes care of the ``(x, y)`` boundary, so callers work in + ``(x, y)`` throughout. ``aligned_points`` is the fitted query cloud already + mapped into the reference frame (``transform`` of the query). + """ + affine: JaxArray velocity: JaxArray velocity_grid: tuple[JaxArray, JaxArray] aligned_points: JaxArray - point_order: PointOrder = "row_col" - def transform_points( + def transform( self, - points: np.ndarray, + points: JaxArray, *, direction: Literal["forward", "backward"] = "forward", - point_order: PointOrder | None = None, ) -> JaxArray: - """Transform arbitrary point arrays with the fitted map.""" - order = self.point_order if point_order is None else point_order - points_rc = to_row_col(points, point_order=order) - transformed = transform_points_row_col( + """Map ``(N, 2)`` ``(x, y)`` points with the fitted diffeomorphism.""" + pts = jnp.asarray(points, dtype=jax_dtype()) + if pts.ndim != 2 or pts.shape[1] != 2: + raise ValueError(f"Expected an (N, 2) `(x, y)` array, found shape {pts.shape}.") + transformed_rc = transform_points_row_col( self.velocity_grid, - jnp.asarray(self.velocity), - jnp.asarray(self.affine), - jnp.asarray(points_rc, dtype=jax_dtype()), + self.velocity, + self.affine, + pts[:, ::-1], direction=direction, ) - return jnp.asarray(from_row_col(np.asarray(transformed), point_order=order)) + return transformed_rc[:, ::-1] def stalign_preprocess( - source_points: np.ndarray, - target_points: np.ndarray, + source_points: JaxArray, + target_points: JaxArray, *, config: STalignPreprocessConfig | None = None, ) -> STalignPreprocessResult: - """Rasterize source and target point clouds for LDDMM registration.""" + """Rasterize source and target point clouds (row-col) for LDDMM registration.""" config = STalignPreprocessConfig() if config is None else config - source_points = to_row_col(source_points, point_order="row_col") - target_points = to_row_col(target_points, point_order="row_col") + source_points = validate_points(source_points, name="source_points") + target_points = validate_points(target_points, name="target_points") source_x, source_y, source_image = rasterize( source_points[:, 1], @@ -153,60 +155,63 @@ def stalign_preprocess( def stalign_points( - source_points: np.ndarray, - target_points: np.ndarray, + source_points: JaxArray, + target_points: JaxArray, *, preprocessed: STalignPreprocessResult | None = None, config: STalignConfig | None = None, - landmarks_source: np.ndarray | None = None, - landmarks_target: np.ndarray | None = None, -) -> STalignResult: - """Align source point cloud to target with a JAX LDDMM solver.""" + landmarks_source: JaxArray | None = None, + landmarks_target: JaxArray | None = None, +) -> StalignResult: + """Align a source point cloud onto a target with a JAX LDDMM solver. + + All point arrays are in the solver's row-column frame; the returned + :class:`StalignResult` speaks ``(x, y)``. + """ config = STalignConfig() if config is None else config registration = config.registration - source_points = to_row_col(source_points, point_order="row_col") - target_points = to_row_col(target_points, point_order="row_col") + source_points = validate_points(source_points, name="source_points") + target_points = validate_points(target_points, name="target_points") if preprocessed is None: preprocessed = stalign_preprocess(source_points, target_points, config=config.preprocess) if (landmarks_source is None) != (landmarks_target is None): raise ValueError("Expected both landmark arrays to be provided together.") + dtype = jax_dtype() if landmarks_source is None: - linear = np.eye(2, dtype=float) - translation = np.zeros(2, dtype=float) + linear = jnp.eye(2, dtype=dtype) + translation = jnp.zeros(2, dtype=dtype) source_landmarks = None target_landmarks = None else: - source_landmarks = to_row_col(landmarks_source, point_order="row_col") - target_landmarks = to_row_col(landmarks_target, point_order="row_col") - linear, translation = affine_from_points(source_landmarks, target_landmarks) + source_landmarks = validate_points(landmarks_source, name="landmarks_source") + target_landmarks = validate_points(landmarks_target, name="landmarks_target") + linear_np, translation_np = affine_from_points(source_landmarks, target_landmarks) + linear = jnp.asarray(linear_np, dtype=dtype) + translation = jnp.asarray(translation_np, dtype=dtype) - dtype = jax_dtype() result = lddmm( preprocessed.source_grid, preprocessed.source_image, preprocessed.target_grid, preprocessed.target_image, - L=jnp.asarray(linear, dtype=dtype), - T=jnp.asarray(translation, dtype=dtype), - points_source=None if source_landmarks is None else jnp.asarray(source_landmarks, dtype=dtype), - points_target=None if target_landmarks is None else jnp.asarray(target_landmarks, dtype=dtype), + L=linear, + T=translation, + points_source=source_landmarks, + points_target=target_landmarks, **asdict(registration), ) - points_rc = to_row_col(source_points, point_order="row_col") - transformed = transform_points_row_col( + transformed_rc = transform_points_row_col( result["xv"], - jnp.asarray(result["v"]), - jnp.asarray(result["A"]), - jnp.asarray(points_rc, dtype=dtype), + result["v"], + result["A"], + source_points, direction="forward", ) - aligned_points = jnp.asarray(from_row_col(np.asarray(transformed), point_order="row_col")) - return STalignResult( + return StalignResult( affine=result["A"], velocity=result["v"], velocity_grid=result["xv"], - aligned_points=aligned_points, - point_order="row_col", + aligned_points=transformed_rc[:, ::-1], ) diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 48cf5d062..8451bb9e4 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -25,7 +25,8 @@ from squidpy.experimental.tl._align._validation import validate_on, validate_output_mode if TYPE_CHECKING: - from squidpy.experimental._methods import FitResult + from squidpy.experimental._methods.align_landmarks import AffineFitResult + from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult __all__ = ["align", "align_by_landmarks"] @@ -42,7 +43,7 @@ def align( output_mode: Literal["object", "copy", "inplace"] = "object", key_added: str | None = None, **method_kwargs: Any, -) -> FitResult | AnnData | SpatialData | None: +) -> StalignResult | AnnData | SpatialData | None: """Align a query sample onto a reference sample. Parameters @@ -61,7 +62,7 @@ def align( spatial_key ``obsm`` key holding the ``(x, y)`` coordinates. Defaults to ``"spatial"``. output_mode - - ``"object"`` (default) -- return the raw :class:`FitResult`; nothing is written. + - ``"object"`` (default) -- return the raw :class:`StalignResult`; nothing is written. - ``"inplace"`` -- write the aligned coordinates into the query and return ``None``. - ``"copy"`` -- write into a copy of the query and return the copy. key_added @@ -105,7 +106,7 @@ def align_by_landmarks( spatial_key: str = "spatial", output_mode: Literal["object", "copy", "inplace"] = "object", key_added: str | None = None, -) -> FitResult | AnnData | SpatialData | None: +) -> AffineFitResult | AnnData | SpatialData | None: """Align by a closed-form fit on pre-paired landmarks. Parameters @@ -126,7 +127,7 @@ def align_by_landmarks( spatial_key ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. output_mode - See :func:`align`. ``"object"`` (default) returns the :class:`FitResult`. + See :func:`align`. ``"object"`` (default) returns the :class:`AffineFitResult`. key_added Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). """ diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index c55ebfa22..ff8b9aef1 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -17,8 +17,8 @@ from squidpy._validators import assert_key_in_sdata if TYPE_CHECKING: - from squidpy.experimental._methods import FitResult from squidpy.experimental._methods.align_landmarks import AffineFitResult + from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult # --------------------------------------------------------------------------- @@ -80,7 +80,7 @@ def get_coords(adata: AnnData, spatial_key: str) -> np.ndarray: def writeback_obs( - result: FitResult, + result: StalignResult | AffineFitResult, *, output_mode: str, query_adata: AnnData, @@ -88,13 +88,13 @@ def writeback_obs( element_key: str | None, spatial_key: str, key_added: str | None, -) -> FitResult | AnnData | SpatialData | None: +) -> StalignResult | AffineFitResult | AnnData | SpatialData | None: """Bake ``result.transform(coords)`` into the query ``obsm`` per ``output_mode``.""" if output_mode == "object": return result dest = _resolve_dest(query_adata, spatial_key=spatial_key, key_added=key_added) - new_coords = result.transform(get_coords(query_adata, spatial_key)) + new_coords = np.asarray(result.transform(get_coords(query_adata, spatial_key))) if container is None: target = query_adata if output_mode == "inplace" else query_adata.copy() diff --git a/tests/experimental/_methods/test_core.py b/tests/experimental/_methods/test_core.py index 284b588d4..a657e1bd1 100644 --- a/tests/experimental/_methods/test_core.py +++ b/tests/experimental/_methods/test_core.py @@ -6,11 +6,11 @@ import numpy as np import pytest -from squidpy.experimental._methods import FitResult, Registry +from squidpy.experimental._methods import Registry @dataclass -class _MeanShiftResult(FitResult): +class _MeanShiftResult: """Toy result: a constant per-axis offset baked into ``transform``.""" delta: np.ndarray @@ -32,21 +32,11 @@ def test_fit_then_transform_round_trip() -> None: result = fit_mean_shift(ref, query) - assert isinstance(result, FitResult) np.testing.assert_allclose(result.delta, [1.0, 1.0]) np.testing.assert_allclose(result.transform(query), query + 1.0) assert result.metadata == {"method": "mean_shift"} -def test_fit_result_transform_default_not_implemented() -> None: - @dataclass - class _Bare(FitResult): - metadata: dict[str, Any] = field(default_factory=dict) - - with pytest.raises(NotImplementedError, match="does not implement `transform`"): - _Bare().transform(np.zeros((2, 2))) - - def test_registry_register_get_keys() -> None: reg = Registry("demo") @@ -56,7 +46,7 @@ def _registered(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: assert reg.keys() == ("mean_shift",) assert reg.get("mean_shift") is _registered - assert isinstance(reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))), FitResult) + assert isinstance(reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))), _MeanShiftResult) def test_registry_unknown_key_lists_available() -> None: @@ -80,7 +70,7 @@ def test_check_requirements_passes_when_none() -> None: # By default, registering without requires parameter does not wrap/check. reg.register("mean_shift")(fit_mean_shift) result = reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))) - assert isinstance(result, FitResult) + assert isinstance(result, _MeanShiftResult) def test_check_requirements_raises_for_missing_dependency() -> None: diff --git a/tests/experimental/_methods/test_stalign.py b/tests/experimental/_methods/test_stalign.py index a0a42af93..5974b54d4 100644 --- a/tests/experimental/_methods/test_stalign.py +++ b/tests/experimental/_methods/test_stalign.py @@ -1,7 +1,7 @@ """Integration tests for the ported STalign estimator. Tiny synthetic fixtures with ``niter=1`` keep these fast; they verify wiring -and shapes (dispatch -> JAX LDDMM -> StalignFitResult), not solver quality. +and shapes (dispatch -> JAX LDDMM -> StalignResult), not solver quality. """ from __future__ import annotations @@ -11,11 +11,12 @@ pytest.importorskip("jax") -from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES, StalignFitResult, fit_stalign +from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES, fit_stalign from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, + StalignResult, ) @@ -44,34 +45,40 @@ def test_stalign_registered_in_align_family() -> None: assert ALIGN_SAMPLES.get("stalign") is fit_stalign -def test_stalign_fit_returns_displacement_result() -> None: +def test_stalign_fit_returns_diffeomorphism() -> None: ref, query = _points_xy(), _points_xy() result = fit_stalign(ref, query, config=_tiny_config()) - assert isinstance(result, StalignFitResult) - assert result.deltas.shape == query.shape - assert np.all(np.isfinite(result.deltas)) - assert result.affine is None - assert result.metadata["method"] == "stalign" - # Escape hatch: the full diffeomorphic map is preserved for power users. - assert "stalign_result" in result.metadata + assert isinstance(result, StalignResult) + assert result.aligned_points.shape == query.shape + assert np.all(np.isfinite(np.asarray(result.aligned_points))) + assert result.affine.shape == (3, 3) + assert result.velocity.ndim == 4 -def test_stalign_transform_bakes_in_deltas() -> None: +def test_stalign_transform_matches_aligned_points() -> None: ref, query = _points_xy(), _points_xy() result = fit_stalign(ref, query, config=_tiny_config()) - np.testing.assert_allclose(result.transform(query), query + result.deltas) + np.testing.assert_allclose(np.asarray(result.transform(query)), np.asarray(result.aligned_points), atol=1e-5) -def test_stalign_transform_rejects_wrong_shape() -> None: +def test_stalign_transform_accepts_arbitrary_points() -> None: ref, query = _points_xy(), _points_xy() result = fit_stalign(ref, query, config=_tiny_config()) - with pytest.raises(ValueError, match="expects coordinates of shape"): - result.transform(np.zeros((1, 2))) + out = result.transform(np.zeros((1, 2))) + assert np.asarray(out).shape == (1, 2) + + +def test_stalign_transform_rejects_non_2d() -> None: + ref, query = _points_xy(), _points_xy() + result = fit_stalign(ref, query, config=_tiny_config()) + + with pytest.raises(ValueError, match=r"Expected an \(N, 2\)"): + result.transform(np.zeros((5, 3))) def test_stalign_fit_with_landmarks() -> None: @@ -86,10 +93,9 @@ def test_stalign_fit_with_landmarks() -> None: landmarks_target=landmarks, ) - assert result.deltas.shape == query.shape - assert "stalign_result" in result.metadata + assert result.aligned_points.shape == query.shape def test_stalign_fit_rejects_non_2d_input() -> None: - with pytest.raises(ValueError, match=r"Expected `query` to be an \(N, 2\) array"): + with pytest.raises(ValueError, match=r"Expected `query` to have shape `\(n, 2\)`"): fit_stalign(_points_xy(), np.zeros((5, 3)), config=_tiny_config()) diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 213d2bbd0..22df2e675 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -12,11 +12,11 @@ pytest.importorskip("jax") -from squidpy.experimental._methods.align_samples import StalignFitResult from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( STalignConfig, STalignPreprocessConfig, STalignRegistrationConfig, + StalignResult, ) from squidpy.experimental.tl import align @@ -38,8 +38,8 @@ def _tiny() -> STalignConfig: def test_object_mode_returns_result_and_touches_nothing() -> None: ref, query = _adata(), _adata() result = align(ref, query, method="stalign", output_mode="object", config=_tiny()) - assert isinstance(result, StalignFitResult) - assert result.deltas.shape == query.obsm["spatial"].shape + assert isinstance(result, StalignResult) + assert result.aligned_points.shape == query.obsm["spatial"].shape assert "aligned_spatial" not in query.obsm @@ -97,6 +97,5 @@ def test_align_with_landmarks() -> None: config=_tiny(), ) - assert isinstance(result, StalignFitResult) - assert result.deltas.shape == query.obsm["spatial"].shape - assert "stalign_result" in result.metadata + assert isinstance(result, StalignResult) + assert result.aligned_points.shape == query.obsm["spatial"].shape From 3c2ed02d9e2e1d9e6f26631f81fc4a4e6c1b3983 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:53:43 +0200 Subject: [PATCH 085/131] reduce bloat --- .../_methods/align_samples/_stalign_impl/_helpers.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py index 3b2011b62..314195d30 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py @@ -145,12 +145,7 @@ def affine_from_points( points_source: JaxArray, points_target: JaxArray, ) -> tuple[np.ndarray, np.ndarray]: - """Compute an affine initialization from corresponding landmarks. - - Delegates to skimage's least-squares estimator, so this is the one place - that drops to NumPy on the CPU; the small ``(2, 2)`` / ``(2,)`` result is - lifted back to JAX by the caller. - """ + """Compute an affine initialization from corresponding landmarks.""" source = np.asarray(points_source, dtype=float) target = np.asarray(points_target, dtype=float) if source.shape != target.shape: From 1ea9996a6f43ccd71f2fb113088ba3fe00982c6e Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 10:57:06 +0200 Subject: [PATCH 086/131] remove useless file --- src/squidpy/experimental/tl/_align/_api.py | 8 +++++--- .../experimental/tl/_align/_validation.py | 16 ---------------- 2 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 src/squidpy/experimental/tl/_align/_validation.py diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 8451bb9e4..ffc3712d9 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -14,6 +14,7 @@ from anndata import AnnData from spatialdata import SpatialData +from squidpy._validators import assert_one_of from squidpy.experimental._methods.align_landmarks import ALIGN_LANDMARKS from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES from squidpy.experimental.tl._align._io import ( @@ -22,8 +23,9 @@ writeback_affine_sdata, writeback_obs, ) -from squidpy.experimental.tl._align._validation import validate_on, validate_output_mode +OUTPUT_MODES = ("object", "copy", "inplace") +ON_VALUES = ("obs", "image") if TYPE_CHECKING: from squidpy.experimental._methods.align_landmarks import AffineFitResult from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult @@ -73,8 +75,8 @@ def align( method_kwargs Forwarded to the estimator's ``fit`` (e.g. ``config=STalignConfig(...)``). """ - validate_on(on) - validate_output_mode(output_mode) + assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") + assert_one_of(on, ON_VALUES, name="on") if on == "image": raise NotImplementedError("`align(on='image')` is not implemented yet; use `on='obs'`.") diff --git a/src/squidpy/experimental/tl/_align/_validation.py b/src/squidpy/experimental/tl/_align/_validation.py deleted file mode 100644 index d501d8208..000000000 --- a/src/squidpy/experimental/tl/_align/_validation.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Argument validation for the public ``align`` / ``align_by_landmarks`` functions.""" - -from __future__ import annotations - -from squidpy._validators import assert_one_of - -OUTPUT_MODES = ("object", "copy", "inplace") -ON_VALUES = ("obs", "image") - - -def validate_output_mode(value: str) -> None: - assert_one_of(value, OUTPUT_MODES, name="output_mode") - - -def validate_on(value: str) -> None: - assert_one_of(value, ON_VALUES, name="on") From ee741abd80d4523729f659ad23cc7d72bdff1531 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:01:03 +0200 Subject: [PATCH 087/131] fix --- src/squidpy/experimental/tl/_align/_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index ffc3712d9..4b6c52930 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -133,7 +133,7 @@ def align_by_landmarks( key_added Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). """ - validate_output_mode(output_mode) + assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") result = ALIGN_LANDMARKS.get(method)( landmarks_ref, From ba91e4f4bab81c4bd0ebb59a86bd85d6e3f9e384 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:22:34 +0200 Subject: [PATCH 088/131] undo rasterize changes --- .../experimental/_methods/_registry.py | 1 - .../_methods/align_samples/_stalign.py | 9 +- .../align_samples/_stalign_impl/_helpers.py | 106 ++++++------------ .../align_samples/_stalign_impl/_tools.py | 76 +++---------- tests/experimental/_methods/test_stalign.py | 9 ++ 5 files changed, 61 insertions(+), 140 deletions(-) diff --git a/src/squidpy/experimental/_methods/_registry.py b/src/squidpy/experimental/_methods/_registry.py index 8026c14fe..c56f7cc7c 100644 --- a/src/squidpy/experimental/_methods/_registry.py +++ b/src/squidpy/experimental/_methods/_registry.py @@ -43,7 +43,6 @@ def wrapped(*args: Any, **kwargs: Any) -> Any: ) return func(*args, **kwargs) - wrapped.__requires__ = requires # type: ignore[attr-defined] self._registry[key] = wrapped return wrapped # type: ignore[return-value] else: diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py index 8b82bd813..a8a3c6acb 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -1,11 +1,4 @@ -"""STalign estimator: JAX LDDMM point-cloud registration. - -The JAX solver is lifted from scverse/squidpy#1150 (Selman Özleyen); see -:mod:`._stalign_impl`. This module only adds the thin registry entry that swaps -``(x, y)`` inputs into the solver's row-column frame and returns the fitted -:class:`StalignResult`. JAX is imported lazily inside :func:`fit_stalign`, so -importing this module is cheap and does not require JAX. -""" +"""STalign estimator: JAX LDDMM point-cloud registration.""" from __future__ import annotations diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py index 314195d30..9da0a2067 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py @@ -1,12 +1,4 @@ -"""Numeric helpers for STalign point-cloud registration. - -Lifted from scverse/squidpy#1150 (Selman Özleyen). Container readers -(``extract_points`` / ``extract_landmarks``) were dropped on the move into -:mod:`squidpy.experimental._methods`: reading coordinates out of an ``AnnData`` / -``SpatialData`` is the caller's concern, so everything here operates purely on -in-memory arrays. Rasterization runs on JAX so the point cloud never leaves the -device between preprocessing and the LDDMM solve. -""" +"""Numeric helpers for STalign point-cloud registration.""" from __future__ import annotations @@ -42,23 +34,20 @@ def validate_points(points: Any, *, name: str) -> JaxArray: def rasterize( - x: JaxArray, - y: JaxArray, + x: np.ndarray, + y: np.ndarray, *, - g: JaxArray | None = None, dx: float = 30.0, blur: float | list[float] = 1.0, expand: float = 1.1, -) -> tuple[JaxArray, JaxArray, JaxArray]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Rasterize a point cloud into a multi-scale Gaussian density image. Each point splats a normalized Gaussian over a fixed ``(2r + 1)`` patch and - the patches are scatter-added onto the grid -- the JAX analogue of the - original per-point NumPy loop. Out-of-grid patch pixels are masked out. + the patches are accumulated onto the grid. """ - dtype = jax_dtype() - x = jnp.asarray(x, dtype=dtype).reshape(-1) - y = jnp.asarray(y, dtype=dtype).reshape(-1) + x = np.asarray(x, dtype=float).reshape(-1) + y = np.asarray(y, dtype=float).reshape(-1) if x.shape != y.shape: raise ValueError("Expected `x` and `y` to be 1D arrays with the same length.") if x.size == 0: @@ -68,79 +57,50 @@ def rasterize( if expand <= 0: raise ValueError("Expected `expand` to be positive.") - blur_values = jnp.atleast_1d(jnp.asarray(blur, dtype=dtype)) - if blur_values.ndim != 1 or bool(jnp.any(blur_values <= 0)): + blur_values = np.atleast_1d(np.asarray(blur, dtype=float)) + if blur_values.ndim != 1 or np.any(blur_values <= 0): raise ValueError("Expected `blur` to be a positive scalar or a 1D sequence of positive values.") - if g is None: - weights = jnp.ones_like(x) - else: - weights = jnp.asarray(g, dtype=dtype).reshape(-1) - if weights.shape != x.shape: - raise ValueError("Expected `g` to have the same shape as `x` and `y`.") - if not bool(jnp.allclose(weights, 1.0)): - weights = _normalize(weights) - - min_x = float(jnp.min(x)) - max_x = float(jnp.max(x)) - min_y = float(jnp.min(y)) - max_y = float(jnp.max(y)) + min_x = float(np.min(x)) + max_x = float(np.max(x)) + min_y = float(np.min(y)) + max_y = float(np.max(y)) center_x = (min_x + max_x) / 2.0 center_y = (min_y + max_y) / 2.0 half_x = (max_x - min_x) * expand / 2.0 half_y = (max_y - min_y) * expand / 2.0 - grid_x = jnp.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=dtype) - grid_y = jnp.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=dtype) - n_cols = int(grid_x.shape[0]) - n_rows = int(grid_y.shape[0]) - if n_cols < 2 or n_rows < 2: + grid_x = np.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=float) + grid_y = np.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=float) + if grid_x.size < 2 or grid_y.size < 2: raise ValueError("Rasterized grid is too small. Increase the point spread or lower `dx`.") - radius = int(np.ceil(float(jnp.max(blur_values)) * 4.0)) - offsets = jnp.arange(-radius, radius + 1) - - # Nearest grid cell for every point, then its fixed (2r+1) patch indices. - col0 = jnp.round((x - grid_x[0]) / dx).astype(jnp.int32) # (N,) - row0 = jnp.round((y - grid_y[0]) / dx).astype(jnp.int32) # (N,) - patch_cols = col0[:, None] + offsets[None, :] # (N, K) - patch_rows = row0[:, None] + offsets[None, :] # (N, K) + mesh_x, mesh_y = np.meshgrid(grid_x, grid_y) + out = np.zeros((len(blur_values), grid_y.size, grid_x.size), dtype=float) + radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) + denom = 2.0 * (dx * blur_values * 2.0) ** 2 - patch_x = grid_x[0] + patch_cols.astype(dtype) * dx # (N, K) - patch_y = grid_y[0] + patch_rows.astype(dtype) * dx # (N, K) + for x_i, y_i in zip(x, y, strict=False): + col = int(np.rint((x_i - grid_x[0]) / dx)) + row = int(np.rint((y_i - grid_y[0]) / dx)) - # (N, K, K) squared distance from each patch pixel to its source point. - dist2 = (patch_x[:, None, :] - x[:, None, None]) ** 2 + (patch_y[:, :, None] - y[:, None, None]) ** 2 + row0 = max(row - radius, 0) + row1 = min(row + radius, out.shape[1] - 1) + col0 = max(col - radius, 0) + col1 = min(col + radius, out.shape[2] - 1) - denom = 2.0 * (dx * blur_values * 2.0) ** 2 # (B,) - kernels = jnp.exp(-dist2[None] / denom[:, None, None, None]) # (B, N, K, K) - kernels_sum = kernels.sum(axis=(2, 3), keepdims=True) - kernels = kernels / jnp.where(kernels_sum == 0.0, 1.0, kernels_sum) - kernels = kernels * weights[None, :, None, None] + patch_x = mesh_x[row0 : row1 + 1, col0 : col1 + 1] + patch_y = mesh_y[row0 : row1 + 1, col0 : col1 + 1] - valid = ( - (patch_rows >= 0)[:, :, None] - & (patch_rows < n_rows)[:, :, None] - & (patch_cols >= 0)[:, None, :] - & (patch_cols < n_cols)[:, None, :] - ) # (N, K, K) - kernels = kernels * valid[None] + kernels = np.exp(-((patch_x[..., None] - x_i) ** 2 + (patch_y[..., None] - y_i) ** 2) / denom) + kernels_sum = kernels.sum(axis=(0, 1), keepdims=True) + kernels /= np.where(kernels_sum == 0.0, 1.0, kernels_sum) + out[:, row0 : row1 + 1, col0 : col1 + 1] += np.moveaxis(kernels, -1, 0) - rows = jnp.broadcast_to(jnp.clip(patch_rows, 0, n_rows - 1)[:, :, None], dist2.shape) # (N, K, K) - cols = jnp.broadcast_to(jnp.clip(patch_cols, 0, n_cols - 1)[:, None, :], dist2.shape) # (N, K, K) - - out = jnp.zeros((blur_values.shape[0], n_rows, n_cols), dtype=dtype) - out = out.at[:, rows, cols].add(kernels) return grid_x, grid_y, out -def _normalize(values: JaxArray) -> JaxArray: - vmin = jnp.min(values) - vmax = jnp.max(values) - return jnp.where(jnp.isclose(vmin, vmax), jnp.ones_like(values), (values - vmin) / (vmax - vmin)) - - def affine_from_points( points_source: JaxArray, points_target: JaxArray, diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index 532b44c5a..b0b2425cf 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -1,11 +1,7 @@ -"""Low-level point-cloud tools for experimental STalign. - -Lifted from scverse/squidpy#1150 (Selman Özleyen) with import paths adjusted -and minor cleanups. The fitted map is returned as a single :class:`StalignResult` -container that keeps everything on JAX -- the affine, velocity field and -velocity grid stay device-side and :meth:`StalignResult.transform` applies them -to arbitrary points without round-tripping through NumPy. Container write-back -lives in the caller, not here. +"""Point-cloud tools for experimental STalign. + +The fitted map is returned as a single :class:`StalignResult`; container +write-back lives in the caller, not here. """ from __future__ import annotations @@ -30,11 +26,9 @@ __all__ = [ "STalignConfig", "STalignPreprocessConfig", - "STalignPreprocessResult", "STalignRegistrationConfig", "StalignResult", "stalign_points", - "stalign_preprocess", ] @@ -76,23 +70,12 @@ class STalignConfig: registration: STalignRegistrationConfig = field(default_factory=STalignRegistrationConfig) -@dataclass(slots=True) -class STalignPreprocessResult: - source_grid: tuple[JaxArray, JaxArray] - source_image: JaxArray - target_grid: tuple[JaxArray, JaxArray] - target_image: JaxArray - - @dataclass(slots=True) class StalignResult: """A fitted STalign diffeomorphism, ready to transform arbitrary points. - The map (``affine`` + diffeomorphic ``velocity`` field on ``velocity_grid``) - is held as JAX arrays in the solver's internal row-column frame. - :meth:`transform` takes care of the ``(x, y)`` boundary, so callers work in - ``(x, y)`` throughout. ``aligned_points`` is the fitted query cloud already - mapped into the reference frame (``transform`` of the query). + :meth:`transform` works in ``(x, y)``; ``aligned_points`` is the fitted query + cloud already mapped into the reference frame. """ affine: JaxArray @@ -120,45 +103,22 @@ def transform( return transformed_rc[:, ::-1] -def stalign_preprocess( - source_points: JaxArray, - target_points: JaxArray, - *, - config: STalignPreprocessConfig | None = None, -) -> STalignPreprocessResult: - """Rasterize source and target point clouds (row-col) for LDDMM registration.""" - config = STalignPreprocessConfig() if config is None else config - source_points = validate_points(source_points, name="source_points") - target_points = validate_points(target_points, name="target_points") - - source_x, source_y, source_image = rasterize( - source_points[:, 1], - source_points[:, 0], - dx=config.dx, - blur=config.blur, - expand=config.expand, - ) - target_x, target_y, target_image = rasterize( - target_points[:, 1], - target_points[:, 0], +def _rasterize_cloud(points_rc: JaxArray, config: STalignPreprocessConfig) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: + """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" + grid_x, grid_y, image = rasterize( + points_rc[:, 1], + points_rc[:, 0], dx=config.dx, blur=config.blur, expand=config.expand, ) - - return STalignPreprocessResult( - source_grid=(source_y, source_x), - source_image=source_image, - target_grid=(target_y, target_x), - target_image=target_image, - ) + return (grid_y, grid_x), image def stalign_points( source_points: JaxArray, target_points: JaxArray, *, - preprocessed: STalignPreprocessResult | None = None, config: STalignConfig | None = None, landmarks_source: JaxArray | None = None, landmarks_target: JaxArray | None = None, @@ -172,8 +132,8 @@ def stalign_points( registration = config.registration source_points = validate_points(source_points, name="source_points") target_points = validate_points(target_points, name="target_points") - if preprocessed is None: - preprocessed = stalign_preprocess(source_points, target_points, config=config.preprocess) + source_grid, source_image = _rasterize_cloud(source_points, config.preprocess) + target_grid, target_image = _rasterize_cloud(target_points, config.preprocess) if (landmarks_source is None) != (landmarks_target is None): raise ValueError("Expected both landmark arrays to be provided together.") @@ -192,10 +152,10 @@ def stalign_points( translation = jnp.asarray(translation_np, dtype=dtype) result = lddmm( - preprocessed.source_grid, - preprocessed.source_image, - preprocessed.target_grid, - preprocessed.target_image, + source_grid, + source_image, + target_grid, + target_image, L=linear, T=translation, points_source=source_landmarks, diff --git a/tests/experimental/_methods/test_stalign.py b/tests/experimental/_methods/test_stalign.py index 5974b54d4..960328733 100644 --- a/tests/experimental/_methods/test_stalign.py +++ b/tests/experimental/_methods/test_stalign.py @@ -73,6 +73,15 @@ def test_stalign_transform_accepts_arbitrary_points() -> None: assert np.asarray(out).shape == (1, 2) +def test_stalign_transform_backward_inverts_forward() -> None: + ref, query = _points_xy(), _points_xy() + result = fit_stalign(ref, query, config=_tiny_config()) + + forward = result.transform(query, direction="forward") + roundtrip = result.transform(forward, direction="backward") + np.testing.assert_allclose(np.asarray(roundtrip), query, atol=1e-3) + + def test_stalign_transform_rejects_non_2d() -> None: ref, query = _points_xy(), _points_xy() result = fit_stalign(ref, query, config=_tiny_config()) From 1d65f4afaca7281336552594a59355ea3dbcdba4 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:23:36 +0200 Subject: [PATCH 089/131] doc landmark --- .../_methods/align_landmarks/_landmark.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py index dde043a9d..b3c3c1d98 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py @@ -1,16 +1,4 @@ -"""Closed-form landmark alignment estimators. - -Ported from scverse/squidpy#1162 (``fit_landmark_affine``). Two pure models, no -JAX: - -- ``"similarity"`` (4 DOF: rotation + uniform scale + translation) via - :func:`spatialdata.transformations.get_transformation_between_landmarks`. -- ``"affine"`` (6 DOF) via :func:`skimage.transform.estimate_transform`. - -Both consume **pre-paired** ``(N, 2)`` ``(x, y)`` landmark arrays (row ``i`` of -the query matches row ``i`` of the reference); ``N`` must be at least 3. No -automatic correspondence matching is performed. -""" +"""Closed-form landmark alignment estimators.""" from __future__ import annotations From a3da05a2d5e1de3888b8709af9ff74d117fb587f Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:23:59 +0200 Subject: [PATCH 090/131] remove bloat --- .../_methods/align_samples/_stalign_impl/_core.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py index 0b98d4255..39996a099 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py @@ -1,7 +1,4 @@ -"""Core JAX implementation for experimental STalign point registration. - -Lifted byte-for-byte from scverse/squidpy#1150 (Selman Özleyen). -""" +"""Core JAX implementation for experimental STalign point registration.""" from __future__ import annotations From 373ca02a62696ae3872edfeea1f41a6574428807 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:25:19 +0200 Subject: [PATCH 091/131] docs --- src/squidpy/experimental/_methods/__init__.py | 15 +-------------- src/squidpy/experimental/_methods/_families.py | 9 +-------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/squidpy/experimental/_methods/__init__.py b/src/squidpy/experimental/_methods/__init__.py index ab5d10038..2769ea99f 100644 --- a/src/squidpy/experimental/_methods/__init__.py +++ b/src/squidpy/experimental/_methods/__init__.py @@ -1,17 +1,4 @@ -"""In-memory model-fitting core for experimental squidpy tools. - -This subpackage factors the *fit* half of fitting-style tools (alignment, -imputation, ...) out of any particular container or write-back path. Each -family registers its estimators into a flat :class:`Registry` (``name -> -function``). An estimator returns a plain result dataclass that carries the -fitted parameters and a ``transform`` method; turning that into mutations on an -:class:`~anndata.AnnData` / :class:`~spatialdata.SpatialData` is the calling -function's job, not the result's. - -Concrete estimators live in per-family subpackages (``align_samples``, -``align_landmarks``, ...) and pull their optional dependencies (e.g. JAX) in -lazily, so importing this package stays cheap. -""" +"""In-memory model-fitting core for experimental methods.""" from __future__ import annotations diff --git a/src/squidpy/experimental/_methods/_families.py b/src/squidpy/experimental/_methods/_families.py index 2ef6b01a5..dadef5033 100644 --- a/src/squidpy/experimental/_methods/_families.py +++ b/src/squidpy/experimental/_methods/_families.py @@ -1,11 +1,4 @@ -"""Estimator family registries. - -A *family* is the set of interchangeable algorithms behind one public function, -selected by ``method=`` and sharing one ``fit`` contract. One :class:`Registry` -per family. Registries live in this small module so the estimator modules can -register into them without importing their package ``__init__`` (which imports -the estimator modules in turn -- that would be circular). -""" +"""Estimator family registries.""" from __future__ import annotations From aaf48c7eef1673ca537176fed81919542052fb11 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:28:20 +0200 Subject: [PATCH 092/131] doc debloatr --- .../experimental/_methods/align_landmarks/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_landmarks/__init__.py b/src/squidpy/experimental/_methods/align_landmarks/__init__.py index 6b04a86f2..37ee80aa0 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/__init__.py +++ b/src/squidpy/experimental/_methods/align_landmarks/__init__.py @@ -1,9 +1,4 @@ -"""``align_landmarks`` family: closed-form alignment from paired landmarks. - -Importing this package registers the family's estimators into -:data:`~squidpy.experimental._methods._families.ALIGN_LANDMARKS`. Pure NumPy / -spatialdata / skimage -- no JAX. -""" +"""``align_landmarks`` family: closed-form alignment from paired landmarks.""" from __future__ import annotations From 4387a61080bbd0f012ee2a1ac905b6744a1845ab Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 8 Jun 2026 11:37:21 +0200 Subject: [PATCH 093/131] expose result classes --- .../align_samples/_stalign_impl/_tools.py | 16 +++++++++++----- src/squidpy/experimental/tl/__init__.py | 13 +++++++++++-- tests/experimental/tl/test_align.py | 6 ++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index b0b2425cf..b5a6848bc 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -9,11 +9,6 @@ from dataclasses import asdict, dataclass, field from typing import TYPE_CHECKING, Any, Literal, TypeAlias -import jax.numpy as jnp - -from ._core import jax_dtype, lddmm, transform_points_row_col -from ._helpers import affine_from_points, rasterize, validate_points - if TYPE_CHECKING: import jax @@ -90,6 +85,10 @@ def transform( direction: Literal["forward", "backward"] = "forward", ) -> JaxArray: """Map ``(N, 2)`` ``(x, y)`` points with the fitted diffeomorphism.""" + import jax.numpy as jnp + + from ._core import jax_dtype, transform_points_row_col + pts = jnp.asarray(points, dtype=jax_dtype()) if pts.ndim != 2 or pts.shape[1] != 2: raise ValueError(f"Expected an (N, 2) `(x, y)` array, found shape {pts.shape}.") @@ -105,6 +104,8 @@ def transform( def _rasterize_cloud(points_rc: JaxArray, config: STalignPreprocessConfig) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" + from ._helpers import rasterize + grid_x, grid_y, image = rasterize( points_rc[:, 1], points_rc[:, 0], @@ -128,6 +129,11 @@ def stalign_points( All point arrays are in the solver's row-column frame; the returned :class:`StalignResult` speaks ``(x, y)``. """ + import jax.numpy as jnp + + from ._core import jax_dtype, lddmm, transform_points_row_col + from ._helpers import affine_from_points, validate_points + config = STalignConfig() if config is None else config registration = config.registration source_points = validate_points(source_points, name="source_points") diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 772cf72e8..f44dcfc8b 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -1,7 +1,16 @@ from __future__ import annotations +from squidpy.experimental._methods.align_landmarks import AffineFitResult +from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult + from ._align import align, align_by_landmarks from ._tiling_qc import TilingQCParams, calculate_tiling_qc -__all__ = ["align", "align_by_landmarks", "calculate_tiling_qc", "TilingQCParams"] - +__all__ = [ + "align", + "align_by_landmarks", + "calculate_tiling_qc", + "TilingQCParams", + "AffineFitResult", + "StalignResult", +] diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 22df2e675..b4bd135cd 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -83,6 +83,12 @@ def test_missing_spatial_key() -> None: align(ref, query, spatial_key="missing", config=_tiny()) +def test_public_exports_are_reachable() -> None: + import squidpy.experimental.tl as tl + + assert tl.StalignResult is StalignResult + + def test_align_with_landmarks() -> None: ref, query = _adata(), _adata() landmarks = ref.obsm["spatial"][:3] From 85df8fcf4857af57cabe1de364b635444bd97f5a Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 11 Jun 2026 10:25:48 +0200 Subject: [PATCH 094/131] refactoring, testing, typing and bug fix on copy mode --- src/squidpy/experimental/_methods/__init__.py | 3 +- .../experimental/_methods/_families.py | 5 +- .../experimental/_methods/_protocols.py | 59 ++++ .../experimental/_methods/_registry.py | 27 +- .../_methods/align_landmarks/_landmark.py | 4 +- .../_methods/align_samples/__init__.py | 3 +- .../_methods/align_samples/_stalign.py | 12 +- src/squidpy/experimental/tl/__init__.py | 4 +- src/squidpy/experimental/tl/_align/_api.py | 14 +- src/squidpy/experimental/tl/_align/_io.py | 22 +- tests/experimental/tl/test_align.py | 24 ++ .../tl/test_align_by_landmarks.py | 34 ++ tests/experimental/tl/test_align_io.py | 297 ++++++++++++++++++ 13 files changed, 476 insertions(+), 32 deletions(-) create mode 100644 src/squidpy/experimental/_methods/_protocols.py create mode 100644 tests/experimental/tl/test_align_io.py diff --git a/src/squidpy/experimental/_methods/__init__.py b/src/squidpy/experimental/_methods/__init__.py index 2769ea99f..1fa74d939 100644 --- a/src/squidpy/experimental/_methods/__init__.py +++ b/src/squidpy/experimental/_methods/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations +from squidpy.experimental._methods._protocols import AlignLandmarksFn, AlignResult, AlignSamplesFn from squidpy.experimental._methods._registry import Registry -__all__ = ["Registry"] +__all__ = ["Registry", "AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] diff --git a/src/squidpy/experimental/_methods/_families.py b/src/squidpy/experimental/_methods/_families.py index dadef5033..6f3610f8d 100644 --- a/src/squidpy/experimental/_methods/_families.py +++ b/src/squidpy/experimental/_methods/_families.py @@ -2,12 +2,13 @@ from __future__ import annotations +from squidpy.experimental._methods._protocols import AlignLandmarksFn, AlignSamplesFn from squidpy.experimental._methods._registry import Registry #: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. #: Consumed by ``squidpy.experimental.tl.align``. -ALIGN_SAMPLES = Registry("align_samples") +ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") #: Closed-form landmark alignment estimators -- paired landmarks in, affine out. #: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. -ALIGN_LANDMARKS = Registry("align_landmarks") +ALIGN_LANDMARKS: Registry[AlignLandmarksFn] = Registry("align_landmarks") diff --git a/src/squidpy/experimental/_methods/_protocols.py b/src/squidpy/experimental/_methods/_protocols.py new file mode 100644 index 000000000..6f2366a65 --- /dev/null +++ b/src/squidpy/experimental/_methods/_protocols.py @@ -0,0 +1,59 @@ +"""Structural contracts shared across the alignment estimator families. + +These :class:`~typing.Protocol` types are what the public API and the registries +are typed against, so the orchestration layer never names a concrete estimator +result (e.g. ``StalignResult``). A new estimator only has to satisfy +:class:`AlignResult` -- a ``transform`` that maps points into the reference +frame -- to plug into :func:`squidpy.experimental.tl.align`. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable + +import numpy.typing as npt + +from squidpy._utils import NDArrayA + +if TYPE_CHECKING: + from squidpy.experimental._methods.align_landmarks._landmark import AffineFitResult + +__all__ = ["AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] + + +@runtime_checkable +class AlignResult(Protocol): + """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. + + This is the only thing the public ``align*`` functions require of an + estimator's result, so ``output_mode="object"`` is agnostic to the method + that produced it. + """ + + def transform(self, points: npt.ArrayLike, /) -> NDArrayA: + """Map an ``(N, 2)`` ``(x, y)`` array into the reference frame.""" + ... + + +class AlignSamplesFn(Protocol): + """Calling convention for ``align_samples`` estimators. + + Two point clouds in (passed by keyword as ``ref`` / ``query`` so the + direction can never be silently swapped), one :class:`AlignResult` out. + Solver-specific options arrive through ``**kwargs``. + """ + + def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... + + +class AlignLandmarksFn(Protocol): + """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" + + def __call__( + self, + landmarks_ref: npt.ArrayLike, + landmarks_query: npt.ArrayLike, + *, + source_cs: str | None = ..., + target_cs: str | None = ..., + ) -> AffineFitResult: ... diff --git a/src/squidpy/experimental/_methods/_registry.py b/src/squidpy/experimental/_methods/_registry.py index c56f7cc7c..75d7c1ab0 100644 --- a/src/squidpy/experimental/_methods/_registry.py +++ b/src/squidpy/experimental/_methods/_registry.py @@ -5,31 +5,41 @@ import functools import importlib.util from collections.abc import Callable -from typing import Any, TypeVar +from typing import Any, Generic, TypeVar +#: The calling convention a family's registry advertises (returned by :meth:`Registry.get`). F = TypeVar("F", bound=Callable[..., Any]) +#: The concrete function being registered. Kept separate from ``F`` so an estimator may +#: declare specific keyword parameters (e.g. ``config=``) without having to structurally +#: match the family's open-ended ``**kwargs`` calling convention. +RegisteredT = TypeVar("RegisteredT", bound=Callable[..., Any]) -class Registry: +class Registry(Generic[F]): """A flat ``name -> function`` registry for one *family* of methods. One :class:`Registry` is created per family (e.g. ``align``, ``impute``), so keys are plain method names -- there is no ``(method, mode)`` compound key, because the family already pins the rest. + + The type parameter ``F`` is the family's calling convention (a callable + :class:`~typing.Protocol`); :meth:`get` returns it, so dispatch sites are + typed against the family contract rather than ``Callable[..., Any]``. """ def __init__(self, name: str) -> None: self.name = name - self._registry: dict[str, Callable[..., Any]] = {} + self._registry: dict[str, F] = {} - def register(self, key: str, *, requires: tuple[str, ...] = ()) -> Callable[[F], F]: + def register(self, key: str, *, requires: tuple[str, ...] = ()) -> Callable[[RegisteredT], RegisteredT]: """Return a decorator registering a method/function under ``key``.""" - def decorator(func: F) -> F: + def decorator(func: RegisteredT) -> RegisteredT: if key in self._registry: raise ValueError(f"Method {key!r} is already registered in the {self.name!r} registry.") if requires: + @functools.wraps(func) def wrapped(*args: Any, **kwargs: Any) -> Any: missing = [pkg for pkg in requires if importlib.util.find_spec(pkg) is None] @@ -43,15 +53,15 @@ def wrapped(*args: Any, **kwargs: Any) -> Any: ) return func(*args, **kwargs) - self._registry[key] = wrapped + self._registry[key] = wrapped # type: ignore[assignment] return wrapped # type: ignore[return-value] else: - self._registry[key] = func + self._registry[key] = func # type: ignore[assignment] return func return decorator - def get(self, key: str) -> Callable[..., Any]: + def get(self, key: str) -> F: """Return the function registered under ``key``.""" try: return self._registry[key] @@ -61,4 +71,3 @@ def get(self, key: str) -> Callable[..., Any]: def keys(self) -> tuple[str, ...]: """Return the registered method names.""" return tuple(self._registry) - diff --git a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py index b3c3c1d98..714309f1b 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/_methods/align_landmarks/_landmark.py @@ -7,7 +7,9 @@ from typing import Any import numpy as np +import numpy.typing as npt +from squidpy._utils import NDArrayA from squidpy.experimental._methods._families import ALIGN_LANDMARKS @@ -24,7 +26,7 @@ def __post_init__(self) -> None: if self.matrix.shape != (3, 3): raise ValueError(f"Expected a (3, 3) homogeneous matrix, found shape {self.matrix.shape}.") - def transform(self, x: np.ndarray) -> np.ndarray: + def transform(self, x: npt.ArrayLike) -> NDArrayA: """Apply the affine to an ``(N, 2)`` ``(x, y)`` coordinate array.""" coords = np.asarray(x, dtype=float) if coords.ndim != 2 or coords.shape[1] != 2: diff --git a/src/squidpy/experimental/_methods/align_samples/__init__.py b/src/squidpy/experimental/_methods/align_samples/__init__.py index eb73741f0..933ea7d06 100644 --- a/src/squidpy/experimental/_methods/align_samples/__init__.py +++ b/src/squidpy/experimental/_methods/align_samples/__init__.py @@ -9,5 +9,6 @@ from squidpy.experimental._methods._families import ALIGN_SAMPLES from squidpy.experimental._methods.align_samples._stalign import fit_stalign +from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult -__all__ = ["ALIGN_SAMPLES", "fit_stalign"] +__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py index a8a3c6acb..59490b9c8 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING + +import numpy.typing as npt from squidpy.experimental._methods._families import ALIGN_SAMPLES @@ -12,12 +14,12 @@ @ALIGN_SAMPLES.register("stalign", requires=("jax",)) def fit_stalign( - ref: Any, - query: Any, + ref: npt.ArrayLike, + query: npt.ArrayLike, *, config: STalignConfig | None = None, - landmarks_source: Any | None = None, - landmarks_target: Any | None = None, + landmarks_source: npt.ArrayLike | None = None, + landmarks_target: npt.ArrayLike | None = None, ) -> StalignResult: """Fit a deformation mapping ``query`` onto ``ref``. diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index f44dcfc8b..3b6f1013d 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -1,7 +1,8 @@ from __future__ import annotations +from squidpy.experimental._methods import AlignResult from squidpy.experimental._methods.align_landmarks import AffineFitResult -from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult +from squidpy.experimental._methods.align_samples import StalignResult from ._align import align, align_by_landmarks from ._tiling_qc import TilingQCParams, calculate_tiling_qc @@ -11,6 +12,7 @@ "align_by_landmarks", "calculate_tiling_qc", "TilingQCParams", + "AlignResult", "AffineFitResult", "StalignResult", ] diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 4b6c52930..a89d82270 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -27,8 +27,7 @@ OUTPUT_MODES = ("object", "copy", "inplace") ON_VALUES = ("obs", "image") if TYPE_CHECKING: - from squidpy.experimental._methods.align_landmarks import AffineFitResult - from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult + from squidpy.experimental._methods import AlignResult __all__ = ["align", "align_by_landmarks"] @@ -45,7 +44,7 @@ def align( output_mode: Literal["object", "copy", "inplace"] = "object", key_added: str | None = None, **method_kwargs: Any, -) -> StalignResult | AnnData | SpatialData | None: +) -> AlignResult | AnnData | SpatialData | None: """Align a query sample onto a reference sample. Parameters @@ -64,7 +63,7 @@ def align( spatial_key ``obsm`` key holding the ``(x, y)`` coordinates. Defaults to ``"spatial"``. output_mode - - ``"object"`` (default) -- return the raw :class:`StalignResult`; nothing is written. + - ``"object"`` (default) -- return the fitted :class:`~squidpy.experimental._methods.AlignResult`; nothing is written. - ``"inplace"`` -- write the aligned coordinates into the query and return ``None``. - ``"copy"`` -- write into a copy of the query and return the copy. key_added @@ -84,7 +83,7 @@ def align( ref_xy = get_coords(ref_adata, spatial_key) query_xy = get_coords(query_adata, spatial_key) - result = ALIGN_SAMPLES.get(method)(ref_xy, query_xy, **method_kwargs) + result = ALIGN_SAMPLES.get(method)(ref=ref_xy, query=query_xy, **method_kwargs) return writeback_obs( result, @@ -108,7 +107,7 @@ def align_by_landmarks( spatial_key: str = "spatial", output_mode: Literal["object", "copy", "inplace"] = "object", key_added: str | None = None, -) -> AffineFitResult | AnnData | SpatialData | None: +) -> AlignResult | AnnData | SpatialData | None: """Align by a closed-form fit on pre-paired landmarks. Parameters @@ -129,7 +128,8 @@ def align_by_landmarks( spatial_key ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. output_mode - See :func:`align`. ``"object"`` (default) returns the :class:`AffineFitResult`. + See :func:`align`. ``"object"`` (default) returns the fitted + :class:`~squidpy.experimental._methods.align_landmarks.AffineFitResult`. key_added Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). """ diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index ff8b9aef1..84f0b5b08 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -17,8 +17,8 @@ from squidpy._validators import assert_key_in_sdata if TYPE_CHECKING: + from squidpy.experimental._methods import AlignResult from squidpy.experimental._methods.align_landmarks import AffineFitResult - from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult # --------------------------------------------------------------------------- @@ -80,7 +80,7 @@ def get_coords(adata: AnnData, spatial_key: str) -> np.ndarray: def writeback_obs( - result: StalignResult | AffineFitResult, + result: AlignResult, *, output_mode: str, query_adata: AnnData, @@ -88,7 +88,7 @@ def writeback_obs( element_key: str | None, spatial_key: str, key_added: str | None, -) -> StalignResult | AffineFitResult | AnnData | SpatialData | None: +) -> AlignResult | AnnData | SpatialData | None: """Bake ``result.transform(coords)`` into the query ``obsm`` per ``output_mode``.""" if output_mode == "object": return result @@ -102,7 +102,13 @@ def writeback_obs( return None if output_mode == "inplace" else target sdata = container if output_mode == "inplace" else shallow_copy_sdata(container) - sdata.tables[element_key].obsm[dest] = new_coords + target_table = sdata.tables[element_key] + if output_mode == "copy": + # `shallow_copy_sdata` shares table objects with the original; copy the single + # table we mutate so `output_mode="copy"` truly leaves the input untouched. + target_table = target_table.copy() + sdata.tables[element_key] = target_table + target_table.obsm[dest] = new_coords return None if output_mode == "inplace" else sdata @@ -119,6 +125,7 @@ def writeback_affine_sdata( Non-destructive: it adds a transformation into ``target_cs`` so the whole coordinate system inherits the alignment. Nothing is materialised. """ + from spatialdata import deepcopy as sd_deepcopy from spatialdata.transformations import Affine, get_transformation, set_transformation if moving_cs is None or target_cs is None: @@ -127,11 +134,16 @@ def writeback_affine_sdata( out = sdata if output_mode == "inplace" else shallow_copy_sdata(sdata) sd_affine = Affine(np.asarray(result.matrix), input_axes=("x", "y"), output_axes=("x", "y")) touched = False - for _etype, _name, element in out.gen_elements(): + for etype, name, element in list(out.gen_elements()): if isinstance(element, AnnData): continue if moving_cs not in get_transformation(element, get_all=True): continue + if output_mode == "copy": + # `shallow_copy_sdata` shares element objects with the original; deep-copy each + # element we register a transform on so `output_mode="copy"` leaves the input untouched. + element = sd_deepcopy(element) + getattr(out, etype)[name] = element set_transformation(element, sd_affine, to_coordinate_system=target_cs) touched = True if not touched: diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index b4bd135cd..6f0469855 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -89,6 +89,30 @@ def test_public_exports_are_reachable() -> None: assert tl.StalignResult is StalignResult +def _sdata_tables(**tables: AnnData): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import TableModel + + return sd.SpatialData(tables={name: TableModel.parse(adata) for name, adata in tables.items()}) + + +def test_sdata_object_mode() -> None: + sdata = _sdata_tables(ref=_adata(), query=_adata()) + result = align(sdata, method="stalign", ref_key="ref", query_key="query", output_mode="object", config=_tiny()) + assert isinstance(result, StalignResult) + assert "aligned_spatial" not in sdata.tables["query"].obsm + + +def test_sdata_copy_leaves_original_untouched() -> None: + sd = pytest.importorskip("spatialdata") + + sdata = _sdata_tables(ref=_adata(), query=_adata()) + out = align(sdata, ref_key="ref", query_key="query", output_mode="copy", key_added="aligned", config=_tiny()) + assert isinstance(out, sd.SpatialData) and out is not sdata + assert "aligned" in out.tables["query"].obsm + assert "aligned" not in sdata.tables["query"].obsm + + def test_align_with_landmarks() -> None: ref, query = _adata(), _adata() landmarks = ref.obsm["spatial"][:3] diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index 507f04e58..e54d93bf7 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -87,6 +87,40 @@ def test_unknown_method_lists_available() -> None: align_by_landmarks(_REF, _QUERY, method="nope", output_mode="object") +def test_non_finite_landmarks_rejected() -> None: + bad = _QUERY.copy() + bad[0, 0] = np.nan + with pytest.raises(ValueError, match="must contain only finite values"): + align_by_landmarks(_REF, bad, method="affine", output_mode="object") + + +def test_bad_data_type_raises() -> None: + with pytest.raises(TypeError, match="must be AnnData or SpatialData"): + align_by_landmarks(_REF, _QUERY, method="affine", data=object(), output_mode="inplace") # type: ignore[arg-type] + + +def test_spatialdata_copy_leaves_original_untouched() -> None: + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Identity, get_transformation + + pts = PointsModel.parse(_QUERY, transformations={"query_cs": Identity()}) + sdata = sd.SpatialData(points={"pts": pts}) + + out = align_by_landmarks( + _REF, + _QUERY, + method="affine", + data=sdata, + cs_name_query="query_cs", + cs_name_ref="ref_cs", + output_mode="copy", + ) + assert out is not sdata + assert "ref_cs" in get_transformation(out.points["pts"], get_all=True) + assert "ref_cs" not in get_transformation(sdata.points["pts"], get_all=True) + + def test_spatialdata_registers_transformation() -> None: sd = pytest.importorskip("spatialdata") from spatialdata.models import PointsModel diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py new file mode 100644 index 000000000..409364471 --- /dev/null +++ b/tests/experimental/tl/test_align_io.py @@ -0,0 +1,297 @@ +"""Unit tests for the I/O layer of the public ``align`` functions. + +These exercise input resolution and write-back directly -- no estimator, no JAX -- +so they cover the ``AnnData`` *and* ``SpatialData`` branches and every error guard +in :mod:`squidpy.experimental.tl._align._io` cheaply. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +import numpy as np +import pytest +from anndata import AnnData + +from squidpy.experimental._methods.align_landmarks import AffineFitResult +from squidpy.experimental.tl._align._io import ( + get_coords, + resolve_obs_pair, + shallow_copy_sdata, + writeback_affine_sdata, + writeback_obs, +) + +_PTS = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) + + +@dataclass +class _ShiftResult: + """Minimal :class:`AlignResult`: a constant offset baked into ``transform``.""" + + delta: float = 100.0 + + def transform(self, points: np.ndarray) -> np.ndarray: + return np.asarray(points, dtype=float) + self.delta + + +def _adata(coords: np.ndarray = _PTS, *, key: str = "spatial") -> AnnData: + adata = AnnData(np.zeros((coords.shape[0], 1))) + adata.obsm[key] = coords.copy() + return adata + + +def _sdata_tables(**tables: AnnData): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import TableModel + + return sd.SpatialData(tables={name: TableModel.parse(adata) for name, adata in tables.items()}) + + +def _sdata_points(cs: str = "qcs"): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Identity + + pts = PointsModel.parse(_PTS, transformations={cs: Identity()}) + return sd.SpatialData(points={"pts": pts}) + + +# --------------------------------------------------------------------------- +# resolve_obs_pair +# --------------------------------------------------------------------------- + + +def test_resolve_anndata_pair() -> None: + ref, query = _adata(), _adata() + r_adata, q_adata, container, element_key = resolve_obs_pair(ref, query, None, None) + assert r_adata is ref and q_adata is query + assert container is None and element_key is None + + +def test_resolve_anndata_requires_query() -> None: + with pytest.raises(ValueError, match="`data_query` is required when `data_ref` is an AnnData"): + resolve_obs_pair(_adata(), None, None, None) + + +def test_resolve_anndata_rejects_mixed_query() -> None: + with pytest.raises(TypeError, match="Mixed AnnData/SpatialData"): + resolve_obs_pair(_adata(), _sdata_points(), None, None) + + +def test_resolve_anndata_rejects_keys() -> None: + with pytest.raises(ValueError, match="only valid for SpatialData"): + resolve_obs_pair(_adata(), _adata(), "ref", None) + + +def test_resolve_bad_ref_type() -> None: + with pytest.raises(TypeError, match="must be AnnData or SpatialData"): + resolve_obs_pair(object(), _adata(), None, None) # type: ignore[arg-type] + + +def test_resolve_sdata_pair() -> None: + pytest.importorskip("spatialdata") + ref_sd = _sdata_tables(ref=_adata()) + query_sd = _sdata_tables(query=_adata(_PTS + 5)) + r_adata, q_adata, container, element_key = resolve_obs_pair(ref_sd, query_sd, "ref", "query") + assert r_adata is ref_sd.tables["ref"] + assert q_adata is query_sd.tables["query"] + assert container is query_sd + assert element_key == "query" + + +def test_resolve_sdata_single_two_tables() -> None: + pytest.importorskip("spatialdata") + both = _sdata_tables(ref=_adata(), query=_adata(_PTS + 5)) + r_adata, q_adata, container, element_key = resolve_obs_pair(both, None, "ref", "query") + assert r_adata is both.tables["ref"] + assert q_adata is both.tables["query"] + assert container is both + assert element_key == "query" + + +def test_resolve_sdata_requires_keys() -> None: + pytest.importorskip("spatialdata") + both = _sdata_tables(ref=_adata(), query=_adata()) + with pytest.raises(ValueError, match="`ref_key` and `query_key` are required"): + resolve_obs_pair(both, None, None, None) + + +def test_resolve_sdata_rejects_mixed_query() -> None: + pytest.importorskip("spatialdata") + with pytest.raises(TypeError, match="Mixed AnnData/SpatialData"): + resolve_obs_pair(_sdata_tables(ref=_adata()), _adata(), "ref", "query") + + +def test_resolve_sdata_missing_key() -> None: + pytest.importorskip("spatialdata") + both = _sdata_tables(ref=_adata()) + with pytest.raises(KeyError, match="nope"): + resolve_obs_pair(both, None, "nope", "ref") + + +# --------------------------------------------------------------------------- +# get_coords +# --------------------------------------------------------------------------- + + +def test_get_coords_missing_key() -> None: + with pytest.raises(KeyError, match="missing.*not found"): + get_coords(_adata(), "missing") + + +def test_get_coords_rejects_non_2d() -> None: + adata = _adata() + adata.obsm["bad"] = np.zeros((_PTS.shape[0], 3)) + with pytest.raises(ValueError, match=r"must be an \(N, 2\) array"): + get_coords(adata, "bad") + + +# --------------------------------------------------------------------------- +# writeback_obs -- AnnData +# --------------------------------------------------------------------------- + + +def test_writeback_obs_object_mode_returns_result() -> None: + result = _ShiftResult() + out = writeback_obs( + result, + output_mode="object", + query_adata=_adata(), + container=None, + element_key=None, + spatial_key="spatial", + key_added=None, + ) + assert out is result + + +def test_writeback_obs_anndata_inplace() -> None: + query = _adata() + out = writeback_obs( + _ShiftResult(), + output_mode="inplace", + query_adata=query, + container=None, + element_key=None, + spatial_key="spatial", + key_added="aligned", + ) + assert out is None + np.testing.assert_allclose(query.obsm["aligned"], _PTS + 100.0) + + +def test_writeback_obs_anndata_copy_leaves_original_untouched() -> None: + query = _adata() + out = writeback_obs( + _ShiftResult(), + output_mode="copy", + query_adata=query, + container=None, + element_key=None, + spatial_key="spatial", + key_added="aligned", + ) + assert isinstance(out, AnnData) and out is not query + assert "aligned" in out.obsm + assert "aligned" not in query.obsm + + +# --------------------------------------------------------------------------- +# writeback_obs -- SpatialData +# --------------------------------------------------------------------------- + + +def test_writeback_obs_sdata_inplace() -> None: + pytest.importorskip("spatialdata") + sdata = _sdata_tables(query=_adata()) + out = writeback_obs( + _ShiftResult(), + output_mode="inplace", + query_adata=sdata.tables["query"], + container=sdata, + element_key="query", + spatial_key="spatial", + key_added="aligned", + ) + assert out is None + np.testing.assert_allclose(sdata.tables["query"].obsm["aligned"], _PTS + 100.0) + + +def test_writeback_obs_sdata_copy_leaves_original_untouched() -> None: + pytest.importorskip("spatialdata") + sdata = _sdata_tables(query=_adata()) + out = writeback_obs( + _ShiftResult(), + output_mode="copy", + query_adata=sdata.tables["query"], + container=sdata, + element_key="query", + spatial_key="spatial", + key_added="aligned", + ) + assert out is not sdata + assert "aligned" in out.tables["query"].obsm + # regression: copy must not leak the new key back into the input container + assert "aligned" not in sdata.tables["query"].obsm + + +# --------------------------------------------------------------------------- +# writeback_affine_sdata +# --------------------------------------------------------------------------- + + +def test_writeback_affine_inplace_registers_transform() -> None: + pytest.importorskip("spatialdata") + from spatialdata.transformations import get_transformation + + sdata = _sdata_points() + out = writeback_affine_sdata( + AffineFitResult(matrix=np.eye(3)), sdata, output_mode="inplace", moving_cs="qcs", target_cs="tcs" + ) + assert out is None + assert "tcs" in get_transformation(sdata.points["pts"], get_all=True) + + +def test_writeback_affine_copy_leaves_original_untouched() -> None: + pytest.importorskip("spatialdata") + from spatialdata.transformations import get_transformation + + sdata = _sdata_points() + out = writeback_affine_sdata( + AffineFitResult(matrix=np.eye(3)), sdata, output_mode="copy", moving_cs="qcs", target_cs="tcs" + ) + assert out is not sdata + assert "tcs" in get_transformation(out.points["pts"], get_all=True) + # regression: copy must not register the transform on the input container + assert "tcs" not in get_transformation(sdata.points["pts"], get_all=True) + + +def test_writeback_affine_requires_cs_names() -> None: + pytest.importorskip("spatialdata") + with pytest.raises(ValueError, match="`cs_name_query` and `cs_name_ref` are required"): + writeback_affine_sdata( + AffineFitResult(matrix=np.eye(3)), _sdata_points(), output_mode="inplace", moving_cs=None, target_cs="tcs" + ) + + +def test_writeback_affine_no_matching_cs() -> None: + pytest.importorskip("spatialdata") + sdata = _sdata_points(cs="qcs") + with pytest.raises(KeyError, match="No elements .* registered to coordinate system 'other'"): + writeback_affine_sdata( + AffineFitResult(matrix=np.eye(3)), sdata, output_mode="inplace", moving_cs="other", target_cs="tcs" + ) + + +# --------------------------------------------------------------------------- +# shallow_copy_sdata +# --------------------------------------------------------------------------- + + +def test_shallow_copy_sdata_preserves_elements() -> None: + pytest.importorskip("spatialdata") + sdata = _sdata_tables(ref=_adata(), query=_adata(_PTS + 5)) + copy = shallow_copy_sdata(sdata) + assert copy is not sdata + assert set(copy.tables) == {"ref", "query"} From 599186fe8e53df7e69b62734003508775faa10cf Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 11 Jun 2026 10:26:06 +0200 Subject: [PATCH 095/131] landmark is positional only now --- src/squidpy/experimental/tl/_align/_api.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index a89d82270..53bf306c2 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -129,15 +129,16 @@ def align_by_landmarks( ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. output_mode See :func:`align`. ``"object"`` (default) returns the fitted - :class:`~squidpy.experimental._methods.align_landmarks.AffineFitResult`. + :class:`~squidpy.experimental._methods.AlignResult` (an + :class:`~squidpy.experimental._methods.align_landmarks.AffineFitResult`). key_added Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). """ assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") result = ALIGN_LANDMARKS.get(method)( - landmarks_ref, - landmarks_query, + landmarks_ref=landmarks_ref, + landmarks_query=landmarks_query, source_cs=cs_name_query, target_cs=cs_name_ref, ) From 8cfc7709fb3956382d7580259a67c556267849df Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Thu, 11 Jun 2026 11:14:24 +0200 Subject: [PATCH 096/131] expose api --- docs/api.md | 3 +++ src/squidpy/experimental/tl/__init__.py | 9 +++++---- tests/experimental/tl/test_align.py | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/api.md b/docs/api.md index 1bf4fdf92..d9da2508d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -149,6 +149,9 @@ See the {doc}`extensibility guide ` for how to implement a custo experimental.tl.calculate_tiling_qc experimental.tl.TilingQCParams + experimental.tl.align + experimental.tl.align_by_landmarks + experimental.tl.AlignResult experimental.pl.tiling_qc experimental.im.fit_stain_reference experimental.im.apply_stain_normalization diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 3b6f1013d..803c04039 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -1,8 +1,11 @@ from __future__ import annotations +# `AlignResult` is the only result type on the public surface: it is the estimator +# contract (a `transform` mapping points into the reference frame) and the declared +# return of `align` / `align_by_landmarks`. The concrete results (`StalignResult`, +# `AffineFitResult`) stay in their home modules under `squidpy.experimental._methods` +# for callers that need raw fields -- the public API stays method-agnostic. from squidpy.experimental._methods import AlignResult -from squidpy.experimental._methods.align_landmarks import AffineFitResult -from squidpy.experimental._methods.align_samples import StalignResult from ._align import align, align_by_landmarks from ._tiling_qc import TilingQCParams, calculate_tiling_qc @@ -13,6 +16,4 @@ "calculate_tiling_qc", "TilingQCParams", "AlignResult", - "AffineFitResult", - "StalignResult", ] diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 6f0469855..7136be25b 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -83,10 +83,20 @@ def test_missing_spatial_key() -> None: align(ref, query, spatial_key="missing", config=_tiny()) -def test_public_exports_are_reachable() -> None: +def test_public_surface_is_align_result_only() -> None: import squidpy.experimental.tl as tl - assert tl.StalignResult is StalignResult + # `AlignResult` is the only result type exposed; concretes stay in their home modules. + assert "AlignResult" in tl.__all__ + assert not hasattr(tl, "StalignResult") + assert not hasattr(tl, "AffineFitResult") + + +def test_object_result_satisfies_align_result_protocol() -> None: + from squidpy.experimental.tl import AlignResult + + result = align(_adata(), _adata(), method="stalign", output_mode="object", config=_tiny()) + assert isinstance(result, AlignResult) def _sdata_tables(**tables: AnnData): From f0868c1d52fe4adedfb2d9328f713592ed940ea3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:09:09 +0000 Subject: [PATCH 097/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_methods/align_samples/_stalign_impl/_core.py | 4 +++- .../_methods/align_samples/_stalign_impl/_tools.py | 4 +++- src/squidpy/experimental/tl/__init__.py | 4 ++-- tests/experimental/_methods/test_core.py | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py index 39996a099..0eb3ef6b9 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py @@ -126,7 +126,9 @@ def _contrast_transform(source_image: jax.Array, target_image: jax.Array, weight return (coefficients.T @ design).reshape(target_image.shape) -def _build_velocity_grid(x_source: tuple[jax.Array, jax.Array], *, a: float, expand: float) -> tuple[jax.Array, jax.Array]: +def _build_velocity_grid( + x_source: tuple[jax.Array, jax.Array], *, a: float, expand: float +) -> tuple[jax.Array, jax.Array]: minimum = jnp.array([x_source[0][0], x_source[1][0]]) maximum = jnp.array([x_source[0][-1], x_source[1][-1]]) center = (minimum + maximum) / 2.0 diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py index b5a6848bc..c5a46b834 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py @@ -102,7 +102,9 @@ def transform( return transformed_rc[:, ::-1] -def _rasterize_cloud(points_rc: JaxArray, config: STalignPreprocessConfig) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: +def _rasterize_cloud( + points_rc: JaxArray, config: STalignPreprocessConfig +) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" from ._helpers import rasterize diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 660dae962..a59cd06c4 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -19,6 +19,6 @@ "AlignResult", "StitchParams", "TilingQCParams", - "assign_stitch_groups", - "calculate_tiling_qc", + "assign_stitch_groups", + "calculate_tiling_qc", ] diff --git a/tests/experimental/_methods/test_core.py b/tests/experimental/_methods/test_core.py index a657e1bd1..3481e97f8 100644 --- a/tests/experimental/_methods/test_core.py +++ b/tests/experimental/_methods/test_core.py @@ -81,6 +81,7 @@ def _needs_ghost(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: return fit_mean_shift(ref, query) with pytest.raises( - ImportError, match=r"Method 'needs_ghost' requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]" + ImportError, + match=r"Method 'needs_ghost' requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]", ): reg.get("needs_ghost")(np.ones((2, 2)), np.zeros((2, 2))) From e6b4075080dafa0632bbe428504c3df34976229e Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 19 Jun 2026 18:17:20 +0200 Subject: [PATCH 098/131] lower bound jax --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ac4dfc47a..242c57af4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,7 @@ dependencies = [ "zarr>=3", ] optional-dependencies.jax = [ - "jax", + "jax>=0.4.0", ] optional-dependencies.leiden = [ "leidenalg", From bff9092d3915d66e23a15b18cf2752d91b20ba68 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:19:08 +0000 Subject: [PATCH 099/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 242c57af4..4eb391589 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,7 @@ dependencies = [ "zarr>=3", ] optional-dependencies.jax = [ - "jax>=0.4.0", + "jax>=0.4", ] optional-dependencies.leiden = [ "leidenalg", From 81a16f2afe9af52fe02ab05ac92cf7a2bb443251 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 19 Jun 2026 18:23:07 +0200 Subject: [PATCH 100/131] align expose --- src/squidpy/experimental/tl/__init__.py | 2 -- src/squidpy/experimental/tl/_align/_api.py | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index a59cd06c4..4a425715c 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -18,7 +18,5 @@ "TilingQCParams", "AlignResult", "StitchParams", - "TilingQCParams", "assign_stitch_groups", - "calculate_tiling_qc", ] diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 53bf306c2..40c54693d 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -63,7 +63,7 @@ def align( spatial_key ``obsm`` key holding the ``(x, y)`` coordinates. Defaults to ``"spatial"``. output_mode - - ``"object"`` (default) -- return the fitted :class:`~squidpy.experimental._methods.AlignResult`; nothing is written. + - ``"object"`` (default) -- return the fitted :class:`~squidpy.experimental.tl.AlignResult`; nothing is written. - ``"inplace"`` -- write the aligned coordinates into the query and return ``None``. - ``"copy"`` -- write into a copy of the query and return the copy. key_added @@ -129,8 +129,7 @@ def align_by_landmarks( ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. output_mode See :func:`align`. ``"object"`` (default) returns the fitted - :class:`~squidpy.experimental._methods.AlignResult` (an - :class:`~squidpy.experimental._methods.align_landmarks.AffineFitResult`). + :class:`~squidpy.experimental.tl.AlignResult`. key_added Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). """ From f5ccd4654176b2ab4f8538744fc9ae02306e8108 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Sat, 20 Jun 2026 16:07:53 +0200 Subject: [PATCH 101/131] api refactor --- docs/api.md | 18 ++++ .../_methods/align_samples/_stalign.py | 87 +++++++++++++++---- src/squidpy/experimental/tl/_align/_api.py | 55 ++++++++++-- tests/experimental/_methods/test_stalign.py | 44 ++++------ tests/experimental/tl/test_align.py | 39 ++++----- 5 files changed, 168 insertions(+), 75 deletions(-) diff --git a/docs/api.md b/docs/api.md index c6d296b39..5c74638cb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -140,6 +140,24 @@ See the {doc}`extensibility guide ` for how to implement a custo gr.neighbors.GridBuilder ``` +The ``method=`` argument of {func}`~squidpy.experimental.tl.align` and +{func}`~squidpy.experimental.tl.align_by_landmarks` dispatches to a registered +fitting function. The method-specific arguments are documented on each function +below; the fitted maps are returned (with ``output_mode="object"``) as the +result types listed alongside. + +```{eval-rst} +.. currentmodule:: squidpy +.. autosummary:: + :toctree: api + + experimental._methods.align_samples._stalign.fit_stalign + experimental._methods.align_landmarks._landmark.fit_similarity + experimental._methods.align_landmarks._landmark.fit_affine + experimental._methods.align_samples._stalign_impl._tools.StalignResult + experimental._methods.align_landmarks._landmark.AffineFitResult +``` + ## Experimental ```{eval-rst} .. module:: squidpy.experimental diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/_methods/align_samples/_stalign.py index 59490b9c8..ac5b98cd8 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/_methods/align_samples/_stalign.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence from typing import TYPE_CHECKING import numpy.typing as npt @@ -9,7 +10,7 @@ from squidpy.experimental._methods._families import ALIGN_SAMPLES if TYPE_CHECKING: - from ._stalign_impl._tools import STalignConfig, StalignResult + from ._stalign_impl._tools import StalignResult @ALIGN_SAMPLES.register("stalign", requires=("jax",)) @@ -17,25 +18,54 @@ def fit_stalign( ref: npt.ArrayLike, query: npt.ArrayLike, *, - config: STalignConfig | None = None, landmarks_source: npt.ArrayLike | None = None, landmarks_target: npt.ArrayLike | None = None, + # rasterization + dx: float = 30.0, + blur: float | Sequence[float] = (2.0, 1.0, 0.5), + raster_expand: float = 1.1, + # LDDMM registration + a: float = 500.0, + p: float = 2.0, + expand: float = 2.0, + nt: int = 3, + niter: int = 5000, + diffeo_start: int = 0, + epL: float = 2e-8, + epT: float = 2e-1, + epV: float = 2e3, + sigmaM: float = 1.0, + sigmaB: float = 2.0, + sigmaA: float = 5.0, + sigmaR: float = 5e5, + sigmaP: float = 2e1, ) -> StalignResult: """Fit a deformation mapping ``query`` onto ``ref``. Parameters ---------- - ref - ``(N, 2)`` reference point cloud in ``(x, y)`` order. - query - ``(M, 2)`` query point cloud in ``(x, y)`` order, to be aligned to - ``ref``. Both are plain in-memory arrays; extracting them from an - ``AnnData`` / ``SpatialData`` is the caller's responsibility. - config - Optional :class:`STalignConfig` of solver hyperparameters. + ref, query + ``(N, 2)`` / ``(M, 2)`` reference and query point clouds in ``(x, y)`` + order; the query is aligned onto the reference. Both are plain in-memory + arrays -- extracting them from an ``AnnData`` / ``SpatialData`` is the + caller's responsibility. landmarks_source, landmarks_target - Optional corresponding ``(x, y)`` landmark arrays used to - initialise the affine. Must be provided together. + Optional corresponding ``(x, y)`` landmark arrays used to initialise the + affine. Must be provided together. + dx, blur, raster_expand + Rasterization of the point clouds into density images: grid spacing, + Gaussian blur scale(s), and field-of-view padding factor. + a, p, expand, nt, niter, diffeo_start + LDDMM controls: kernel width ``a``, regularisation power ``p``, + velocity-grid padding ``expand``, number of integration time steps + ``nt``, iterations ``niter``, and the iteration at which the + diffeomorphic (non-affine) part starts updating ``diffeo_start``. + epL, epT, epV + Gradient-descent step sizes for the linear part, translation, and + velocity field. + sigmaM, sigmaB, sigmaA, sigmaR, sigmaP + Noise scales for the matching, background, artifact, regularisation, and + landmark-point terms of the objective. Returns ------- @@ -43,13 +73,38 @@ def fit_stalign( ``(x, y)`` points into the reference frame; ``aligned_points`` is the fitted ``query`` already mapped. """ - # Import the JAX-backed solver only after requirements pass, so callers - # without JAX get the clean ImportError from check_requirements rather - # than a confusing failure from a module-level `import jax`. + # Import the JAX-backed solver only after the registry's requirements check + # passes, so callers without JAX get a clean ImportError rather than a + # confusing failure from a module-level `import jax`. import jax.numpy as jnp from ._stalign_impl._helpers import validate_points - from ._stalign_impl._tools import stalign_points + from ._stalign_impl._tools import ( + STalignConfig, + STalignPreprocessConfig, + STalignRegistrationConfig, + stalign_points, + ) + + config = STalignConfig( + preprocess=STalignPreprocessConfig(dx=dx, blur=blur, expand=raster_expand), + registration=STalignRegistrationConfig( + a=a, + p=p, + expand=expand, + nt=nt, + niter=niter, + diffeo_start=diffeo_start, + epL=epL, + epT=epT, + epV=epV, + sigmaM=sigmaM, + sigmaB=sigmaB, + sigmaA=sigmaA, + sigmaR=sigmaR, + sigmaP=sigmaP, + ), + ) ref_xy = validate_points(ref, name="ref") query_xy = validate_points(query, name="query") diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 40c54693d..e175bf22b 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -7,8 +7,8 @@ from __future__ import annotations -from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Literal +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal, TypeVar import numpy as np from anndata import AnnData @@ -24,14 +24,44 @@ writeback_obs, ) +if TYPE_CHECKING: + from squidpy.experimental._methods import AlignResult, Registry + OUTPUT_MODES = ("object", "copy", "inplace") ON_VALUES = ("obs", "image") -if TYPE_CHECKING: - from squidpy.experimental._methods import AlignResult __all__ = ["align", "align_by_landmarks"] +F = TypeVar("F", bound="Callable[..., Any]") + +def _methods_rst(registry: Registry, indent: str = " " * 8) -> str: + """Render a registry's methods as a reST list linking to each implementation.""" + items = [ + f"- ``{key}`` -- :func:`~{(fn := registry.get(key)).__module__}.{fn.__name__}`" for key in registry.keys() + ] + return ("\n" + indent).join(items) + + +def _document_methods(**registries: Registry) -> Callable[[F], F]: + """Fill ``{}`` docstring placeholders with each registry's method list. + + First-party and deterministic -- the registries are fully populated by import + time, so this only templates known content (nothing from optional packages). + ``str.replace`` (not ``str.format``) leaves other ``{...}`` in the docstring + untouched. + """ + + def decorator(fn: F) -> F: + if fn.__doc__: + for token, registry in registries.items(): + fn.__doc__ = fn.__doc__.replace("{" + token + "}", _methods_rst(registry)) + return fn + + return decorator + + +@_document_methods(align_samples_methods=ALIGN_SAMPLES) def align( data_ref: AnnData | SpatialData, data_query: AnnData | SpatialData | None = None, @@ -54,7 +84,10 @@ def align( or ``data_ref`` a SpatialData with ``data_query=None`` to align two of its own tables (selected by ``ref_key`` / ``query_key``). method - Estimator in the ``align_samples`` family. ``"stalign"`` (JAX LDDMM). + Fitting method in the ``align_samples`` family. See each implementation + for its method-specific arguments: + + {align_samples_methods} on ``"obs"`` aligns the ``obsm`` point clouds. ``"image"`` is reserved and currently raises :class:`NotImplementedError`. @@ -72,7 +105,10 @@ def align( ``key_added`` was not given explicitly, a :class:`ValueError` is raised (pass ``key_added`` to overwrite intentionally). method_kwargs - Forwarded to the estimator's ``fit`` (e.g. ``config=STalignConfig(...)``). + Method-specific solver arguments, forwarded flat to the chosen + ``method``'s implementation: + + {align_samples_methods} """ assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") assert_one_of(on, ON_VALUES, name="on") @@ -96,6 +132,7 @@ def align( ) +@_document_methods(align_landmarks_methods=ALIGN_LANDMARKS) def align_by_landmarks( landmarks_ref: np.ndarray | Sequence[tuple[float, float]], landmarks_query: np.ndarray | Sequence[tuple[float, float]], @@ -116,8 +153,10 @@ def align_by_landmarks( Equal-length ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``), paired by row order. No automatic correspondence matching is performed. method - Estimator in the ``align_landmarks`` family: ``"similarity"`` (4 DOF) or - ``"affine"`` (6 DOF). + Fitting method in the ``align_landmarks`` family. See each implementation + for its method-specific arguments: + + {align_landmarks_methods} data Target to write the alignment into. Required for ``output_mode`` other than ``"object"``. diff --git a/tests/experimental/_methods/test_stalign.py b/tests/experimental/_methods/test_stalign.py index 960328733..ef343d2b8 100644 --- a/tests/experimental/_methods/test_stalign.py +++ b/tests/experimental/_methods/test_stalign.py @@ -12,12 +12,10 @@ pytest.importorskip("jax") from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES, fit_stalign -from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( - STalignConfig, - STalignPreprocessConfig, - STalignRegistrationConfig, - StalignResult, -) +from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult + +# Flat solver kwargs (assembled into the config internally) -- smallest possible solve. +_TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} def _points_xy() -> np.ndarray: @@ -32,14 +30,6 @@ def _points_xy() -> np.ndarray: ) -def _tiny_config() -> STalignConfig: - """Single-iteration LDDMM hyperparameters - the smallest possible solve.""" - return STalignConfig( - preprocess=STalignPreprocessConfig(dx=0.5, blur=1.0), - registration=STalignRegistrationConfig(a=1.0, expand=1.0, nt=1, niter=1, epV=1.0), - ) - - def test_stalign_registered_in_align_family() -> None: assert "stalign" in ALIGN_SAMPLES.keys() assert ALIGN_SAMPLES.get("stalign") is fit_stalign @@ -48,7 +38,7 @@ def test_stalign_registered_in_align_family() -> None: def test_stalign_fit_returns_diffeomorphism() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, **_TINY) assert isinstance(result, StalignResult) assert result.aligned_points.shape == query.shape @@ -60,14 +50,14 @@ def test_stalign_fit_returns_diffeomorphism() -> None: def test_stalign_transform_matches_aligned_points() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, **_TINY) np.testing.assert_allclose(np.asarray(result.transform(query)), np.asarray(result.aligned_points), atol=1e-5) def test_stalign_transform_accepts_arbitrary_points() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, **_TINY) out = result.transform(np.zeros((1, 2))) assert np.asarray(out).shape == (1, 2) @@ -75,7 +65,7 @@ def test_stalign_transform_accepts_arbitrary_points() -> None: def test_stalign_transform_backward_inverts_forward() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, **_TINY) forward = result.transform(query, direction="forward") roundtrip = result.transform(forward, direction="backward") @@ -84,7 +74,7 @@ def test_stalign_transform_backward_inverts_forward() -> None: def test_stalign_transform_rejects_non_2d() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, config=_tiny_config()) + result = fit_stalign(ref, query, **_TINY) with pytest.raises(ValueError, match=r"Expected an \(N, 2\)"): result.transform(np.zeros((5, 3))) @@ -94,17 +84,17 @@ def test_stalign_fit_with_landmarks() -> None: ref, query = _points_xy(), _points_xy() landmarks = ref[:3] - result = fit_stalign( - ref, - query, - config=_tiny_config(), - landmarks_source=landmarks, - landmarks_target=landmarks, - ) + result = fit_stalign(ref, query, landmarks_source=landmarks, landmarks_target=landmarks, **_TINY) assert result.aligned_points.shape == query.shape def test_stalign_fit_rejects_non_2d_input() -> None: with pytest.raises(ValueError, match=r"Expected `query` to have shape `\(n, 2\)`"): - fit_stalign(_points_xy(), np.zeros((5, 3)), config=_tiny_config()) + fit_stalign(_points_xy(), np.zeros((5, 3)), **_TINY) + + +def test_stalign_rejects_unknown_kwarg() -> None: + ref, query = _points_xy(), _points_xy() + with pytest.raises(TypeError, match="unexpected keyword argument"): + fit_stalign(ref, query, not_a_real_param=1.0, **_TINY) diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 7136be25b..e9894135d 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -12,14 +12,12 @@ pytest.importorskip("jax") -from squidpy.experimental._methods.align_samples._stalign_impl._tools import ( - STalignConfig, - STalignPreprocessConfig, - STalignRegistrationConfig, - StalignResult, -) +from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult from squidpy.experimental.tl import align +# Flat solver kwargs (assembled into the config internally) -- smallest possible solve. +_TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} + def _adata(*, key: str = "spatial") -> AnnData: pts = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) @@ -28,16 +26,9 @@ def _adata(*, key: str = "spatial") -> AnnData: return adata -def _tiny() -> STalignConfig: - return STalignConfig( - preprocess=STalignPreprocessConfig(dx=0.5, blur=1.0), - registration=STalignRegistrationConfig(a=1.0, expand=1.0, nt=1, niter=1, epV=1.0), - ) - - def test_object_mode_returns_result_and_touches_nothing() -> None: ref, query = _adata(), _adata() - result = align(ref, query, method="stalign", output_mode="object", config=_tiny()) + result = align(ref, query, method="stalign", output_mode="object", **_TINY) assert isinstance(result, StalignResult) assert result.aligned_points.shape == query.obsm["spatial"].shape assert "aligned_spatial" not in query.obsm @@ -45,20 +36,20 @@ def test_object_mode_returns_result_and_touches_nothing() -> None: def test_inplace_writes_explicit_key() -> None: ref, query = _adata(), _adata() - out = align(ref, query, output_mode="inplace", key_added="spatial_aligned", config=_tiny()) + out = align(ref, query, output_mode="inplace", key_added="spatial_aligned", **_TINY) assert out is None assert query.obsm["spatial_aligned"].shape == query.obsm["spatial"].shape def test_inplace_default_key() -> None: ref, query = _adata(), _adata() - align(ref, query, output_mode="inplace", config=_tiny()) + align(ref, query, output_mode="inplace", **_TINY) assert "aligned_spatial" in query.obsm def test_copy_leaves_original_untouched() -> None: ref, query = _adata(), _adata() - out = align(ref, query, output_mode="copy", key_added="aligned", config=_tiny()) + out = align(ref, query, output_mode="copy", key_added="aligned", **_TINY) assert isinstance(out, AnnData) and out is not query assert "aligned" in out.obsm assert "aligned" not in query.obsm @@ -68,19 +59,19 @@ def test_existing_default_key_requires_explicit_key_added() -> None: ref, query = _adata(), _adata() query.obsm["aligned_spatial"] = np.zeros_like(query.obsm["spatial"]) with pytest.raises(ValueError, match="aligned_spatial.*already exists"): - align(ref, query, output_mode="inplace", config=_tiny()) + align(ref, query, output_mode="inplace", **_TINY) def test_image_not_implemented() -> None: ref, query = _adata(), _adata() with pytest.raises(NotImplementedError, match="on='image'"): - align(ref, query, on="image", config=_tiny()) + align(ref, query, on="image", **_TINY) def test_missing_spatial_key() -> None: ref, query = _adata(), _adata() with pytest.raises(KeyError, match="missing.*not found"): - align(ref, query, spatial_key="missing", config=_tiny()) + align(ref, query, spatial_key="missing", **_TINY) def test_public_surface_is_align_result_only() -> None: @@ -95,7 +86,7 @@ def test_public_surface_is_align_result_only() -> None: def test_object_result_satisfies_align_result_protocol() -> None: from squidpy.experimental.tl import AlignResult - result = align(_adata(), _adata(), method="stalign", output_mode="object", config=_tiny()) + result = align(_adata(), _adata(), method="stalign", output_mode="object", **_TINY) assert isinstance(result, AlignResult) @@ -108,7 +99,7 @@ def _sdata_tables(**tables: AnnData): def test_sdata_object_mode() -> None: sdata = _sdata_tables(ref=_adata(), query=_adata()) - result = align(sdata, method="stalign", ref_key="ref", query_key="query", output_mode="object", config=_tiny()) + result = align(sdata, method="stalign", ref_key="ref", query_key="query", output_mode="object", **_TINY) assert isinstance(result, StalignResult) assert "aligned_spatial" not in sdata.tables["query"].obsm @@ -117,7 +108,7 @@ def test_sdata_copy_leaves_original_untouched() -> None: sd = pytest.importorskip("spatialdata") sdata = _sdata_tables(ref=_adata(), query=_adata()) - out = align(sdata, ref_key="ref", query_key="query", output_mode="copy", key_added="aligned", config=_tiny()) + out = align(sdata, ref_key="ref", query_key="query", output_mode="copy", key_added="aligned", **_TINY) assert isinstance(out, sd.SpatialData) and out is not sdata assert "aligned" in out.tables["query"].obsm assert "aligned" not in sdata.tables["query"].obsm @@ -134,7 +125,7 @@ def test_align_with_landmarks() -> None: output_mode="object", landmarks_source=landmarks, landmarks_target=landmarks, - config=_tiny(), + **_TINY, ) assert isinstance(result, StalignResult) From 461f5ee4328b42cbc765605eeb2139a36e62800d Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Sat, 20 Jun 2026 16:16:11 +0200 Subject: [PATCH 102/131] expose methods in a nice place --- docs/api.md | 10 +++++----- .../_methods/align_samples/__init__.py | 14 -------------- .../{_methods => method_registry}/__init__.py | 4 ++-- .../{_methods => method_registry}/_families.py | 4 ++-- .../{_methods => method_registry}/_protocols.py | 2 +- .../{_methods => method_registry}/_registry.py | 0 .../align_landmarks/__init__.py | 4 ++-- .../align_landmarks/_landmark.py | 2 +- .../method_registry/align_samples/__init__.py | 14 ++++++++++++++ .../align_samples/_stalign.py | 2 +- .../align_samples/_stalign_impl/__init__.py | 0 .../align_samples/_stalign_impl/_core.py | 0 .../align_samples/_stalign_impl/_helpers.py | 0 .../align_samples/_stalign_impl/_tools.py | 0 src/squidpy/experimental/tl/__init__.py | 4 ++-- src/squidpy/experimental/tl/_align/_api.py | 8 ++++---- src/squidpy/experimental/tl/_align/_io.py | 6 +++--- .../{_methods => method_registry}/__init__.py | 0 .../{_methods => method_registry}/test_core.py | 2 +- .../{_methods => method_registry}/test_stalign.py | 4 ++-- tests/experimental/tl/test_align.py | 2 +- tests/experimental/tl/test_align_by_landmarks.py | 2 +- tests/experimental/tl/test_align_io.py | 2 +- 23 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 src/squidpy/experimental/_methods/align_samples/__init__.py rename src/squidpy/experimental/{_methods => method_registry}/__init__.py (50%) rename src/squidpy/experimental/{_methods => method_registry}/_families.py (75%) rename src/squidpy/experimental/{_methods => method_registry}/_protocols.py (95%) rename src/squidpy/experimental/{_methods => method_registry}/_registry.py (100%) rename src/squidpy/experimental/{_methods => method_registry}/align_landmarks/__init__.py (64%) rename src/squidpy/experimental/{_methods => method_registry}/align_landmarks/_landmark.py (98%) create mode 100644 src/squidpy/experimental/method_registry/align_samples/__init__.py rename src/squidpy/experimental/{_methods => method_registry}/align_samples/_stalign.py (98%) rename src/squidpy/experimental/{_methods => method_registry}/align_samples/_stalign_impl/__init__.py (100%) rename src/squidpy/experimental/{_methods => method_registry}/align_samples/_stalign_impl/_core.py (100%) rename src/squidpy/experimental/{_methods => method_registry}/align_samples/_stalign_impl/_helpers.py (100%) rename src/squidpy/experimental/{_methods => method_registry}/align_samples/_stalign_impl/_tools.py (100%) rename tests/experimental/{_methods => method_registry}/__init__.py (100%) rename tests/experimental/{_methods => method_registry}/test_core.py (98%) rename tests/experimental/{_methods => method_registry}/test_stalign.py (94%) diff --git a/docs/api.md b/docs/api.md index 5c74638cb..ad48c98b4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -151,11 +151,11 @@ result types listed alongside. .. autosummary:: :toctree: api - experimental._methods.align_samples._stalign.fit_stalign - experimental._methods.align_landmarks._landmark.fit_similarity - experimental._methods.align_landmarks._landmark.fit_affine - experimental._methods.align_samples._stalign_impl._tools.StalignResult - experimental._methods.align_landmarks._landmark.AffineFitResult + experimental.method_registry.align_samples._stalign.fit_stalign + experimental.method_registry.align_landmarks._landmark.fit_similarity + experimental.method_registry.align_landmarks._landmark.fit_affine + experimental.method_registry.align_samples._stalign_impl._tools.StalignResult + experimental.method_registry.align_landmarks._landmark.AffineFitResult ``` ## Experimental diff --git a/src/squidpy/experimental/_methods/align_samples/__init__.py b/src/squidpy/experimental/_methods/align_samples/__init__.py deleted file mode 100644 index 933ea7d06..000000000 --- a/src/squidpy/experimental/_methods/align_samples/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -"""``align_samples`` family: align two samples' point clouds (STalign). - -Importing this package registers the family's estimators into -:data:`~squidpy.experimental._methods._families.ALIGN_SAMPLES`. It stays cheap -- -JAX is pulled in lazily, only when an estimator's ``fit`` runs. -""" - -from __future__ import annotations - -from squidpy.experimental._methods._families import ALIGN_SAMPLES -from squidpy.experimental._methods.align_samples._stalign import fit_stalign -from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult - -__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/_methods/__init__.py b/src/squidpy/experimental/method_registry/__init__.py similarity index 50% rename from src/squidpy/experimental/_methods/__init__.py rename to src/squidpy/experimental/method_registry/__init__.py index 1fa74d939..f03ead204 100644 --- a/src/squidpy/experimental/_methods/__init__.py +++ b/src/squidpy/experimental/method_registry/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from squidpy.experimental._methods._protocols import AlignLandmarksFn, AlignResult, AlignSamplesFn -from squidpy.experimental._methods._registry import Registry +from squidpy.experimental.method_registry._protocols import AlignLandmarksFn, AlignResult, AlignSamplesFn +from squidpy.experimental.method_registry._registry import Registry __all__ = ["Registry", "AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] diff --git a/src/squidpy/experimental/_methods/_families.py b/src/squidpy/experimental/method_registry/_families.py similarity index 75% rename from src/squidpy/experimental/_methods/_families.py rename to src/squidpy/experimental/method_registry/_families.py index 6f3610f8d..a80a61e47 100644 --- a/src/squidpy/experimental/_methods/_families.py +++ b/src/squidpy/experimental/method_registry/_families.py @@ -2,8 +2,8 @@ from __future__ import annotations -from squidpy.experimental._methods._protocols import AlignLandmarksFn, AlignSamplesFn -from squidpy.experimental._methods._registry import Registry +from squidpy.experimental.method_registry._protocols import AlignLandmarksFn, AlignSamplesFn +from squidpy.experimental.method_registry._registry import Registry #: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. #: Consumed by ``squidpy.experimental.tl.align``. diff --git a/src/squidpy/experimental/_methods/_protocols.py b/src/squidpy/experimental/method_registry/_protocols.py similarity index 95% rename from src/squidpy/experimental/_methods/_protocols.py rename to src/squidpy/experimental/method_registry/_protocols.py index 6f2366a65..ef034fcac 100644 --- a/src/squidpy/experimental/_methods/_protocols.py +++ b/src/squidpy/experimental/method_registry/_protocols.py @@ -16,7 +16,7 @@ from squidpy._utils import NDArrayA if TYPE_CHECKING: - from squidpy.experimental._methods.align_landmarks._landmark import AffineFitResult + from squidpy.experimental.method_registry.align_landmarks._landmark import AffineFitResult __all__ = ["AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] diff --git a/src/squidpy/experimental/_methods/_registry.py b/src/squidpy/experimental/method_registry/_registry.py similarity index 100% rename from src/squidpy/experimental/_methods/_registry.py rename to src/squidpy/experimental/method_registry/_registry.py diff --git a/src/squidpy/experimental/_methods/align_landmarks/__init__.py b/src/squidpy/experimental/method_registry/align_landmarks/__init__.py similarity index 64% rename from src/squidpy/experimental/_methods/align_landmarks/__init__.py rename to src/squidpy/experimental/method_registry/align_landmarks/__init__.py index 37ee80aa0..506185b83 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/__init__.py +++ b/src/squidpy/experimental/method_registry/align_landmarks/__init__.py @@ -2,8 +2,8 @@ from __future__ import annotations -from squidpy.experimental._methods._families import ALIGN_LANDMARKS -from squidpy.experimental._methods.align_landmarks._landmark import ( +from squidpy.experimental.method_registry._families import ALIGN_LANDMARKS +from squidpy.experimental.method_registry.align_landmarks._landmark import ( AffineFitResult, fit_affine, fit_similarity, diff --git a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py similarity index 98% rename from src/squidpy/experimental/_methods/align_landmarks/_landmark.py rename to src/squidpy/experimental/method_registry/align_landmarks/_landmark.py index 714309f1b..b28aef46f 100644 --- a/src/squidpy/experimental/_methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py @@ -10,7 +10,7 @@ import numpy.typing as npt from squidpy._utils import NDArrayA -from squidpy.experimental._methods._families import ALIGN_LANDMARKS +from squidpy.experimental.method_registry._families import ALIGN_LANDMARKS @dataclass diff --git a/src/squidpy/experimental/method_registry/align_samples/__init__.py b/src/squidpy/experimental/method_registry/align_samples/__init__.py new file mode 100644 index 000000000..5ae764762 --- /dev/null +++ b/src/squidpy/experimental/method_registry/align_samples/__init__.py @@ -0,0 +1,14 @@ +"""``align_samples`` family: align two samples' point clouds (STalign). + +Importing this package registers the family's estimators into +:data:`~squidpy.experimental.method_registry._families.ALIGN_SAMPLES`. It stays cheap -- +JAX is pulled in lazily, only when an estimator's ``fit`` runs. +""" + +from __future__ import annotations + +from squidpy.experimental.method_registry._families import ALIGN_SAMPLES +from squidpy.experimental.method_registry.align_samples._stalign import fit_stalign +from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult + +__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign.py b/src/squidpy/experimental/method_registry/align_samples/_stalign.py similarity index 98% rename from src/squidpy/experimental/_methods/align_samples/_stalign.py rename to src/squidpy/experimental/method_registry/align_samples/_stalign.py index ac5b98cd8..b9b849daa 100644 --- a/src/squidpy/experimental/_methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/method_registry/align_samples/_stalign.py @@ -7,7 +7,7 @@ import numpy.typing as npt -from squidpy.experimental._methods._families import ALIGN_SAMPLES +from squidpy.experimental.method_registry._families import ALIGN_SAMPLES if TYPE_CHECKING: from ._stalign_impl._tools import StalignResult diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/__init__.py similarity index 100% rename from src/squidpy/experimental/_methods/align_samples/_stalign_impl/__init__.py rename to src/squidpy/experimental/method_registry/align_samples/_stalign_impl/__init__.py diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_core.py similarity index 100% rename from src/squidpy/experimental/_methods/align_samples/_stalign_impl/_core.py rename to src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_core.py diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_helpers.py similarity index 100% rename from src/squidpy/experimental/_methods/align_samples/_stalign_impl/_helpers.py rename to src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_helpers.py diff --git a/src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py similarity index 100% rename from src/squidpy/experimental/_methods/align_samples/_stalign_impl/_tools.py rename to src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 4a425715c..abad050c7 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -3,9 +3,9 @@ # `AlignResult` is the only result type on the public surface: it is the estimator # contract (a `transform` mapping points into the reference frame) and the declared # return of `align` / `align_by_landmarks`. The concrete results (`StalignResult`, -# `AffineFitResult`) stay in their home modules under `squidpy.experimental._methods` +# `AffineFitResult`) stay in their home modules under `squidpy.experimental.method_registry` # for callers that need raw fields -- the public API stays method-agnostic. -from squidpy.experimental._methods import AlignResult +from squidpy.experimental.method_registry import AlignResult from ._align import align, align_by_landmarks from ._tiling_qc import TilingQCParams, calculate_tiling_qc diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index e175bf22b..5874db418 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -1,4 +1,4 @@ -"""Public alignment functions built on the :mod:`squidpy.experimental._methods` core. +"""Public alignment functions built on the :mod:`squidpy.experimental.method_registry` core. These are thin orchestrators: resolve inputs to in-memory arrays, dispatch to a fit-core estimator, write the result back. All container I/O and write-back live @@ -15,8 +15,8 @@ from spatialdata import SpatialData from squidpy._validators import assert_one_of -from squidpy.experimental._methods.align_landmarks import ALIGN_LANDMARKS -from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES +from squidpy.experimental.method_registry.align_landmarks import ALIGN_LANDMARKS +from squidpy.experimental.method_registry.align_samples import ALIGN_SAMPLES from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, @@ -25,7 +25,7 @@ ) if TYPE_CHECKING: - from squidpy.experimental._methods import AlignResult, Registry + from squidpy.experimental.method_registry import AlignResult, Registry OUTPUT_MODES = ("object", "copy", "inplace") ON_VALUES = ("obs", "image") diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index 84f0b5b08..a1c35cb68 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -2,7 +2,7 @@ This is the *only* layer that knows about the ``AnnData | SpatialData`` argument shapes and the ``output_mode`` write-back strategies. The fit estimators in -:mod:`squidpy.experimental._methods` operate on plain arrays and never see a +:mod:`squidpy.experimental.method_registry` operate on plain arrays and never see a container. """ @@ -17,8 +17,8 @@ from squidpy._validators import assert_key_in_sdata if TYPE_CHECKING: - from squidpy.experimental._methods import AlignResult - from squidpy.experimental._methods.align_landmarks import AffineFitResult + from squidpy.experimental.method_registry import AlignResult + from squidpy.experimental.method_registry.align_landmarks import AffineFitResult # --------------------------------------------------------------------------- diff --git a/tests/experimental/_methods/__init__.py b/tests/experimental/method_registry/__init__.py similarity index 100% rename from tests/experimental/_methods/__init__.py rename to tests/experimental/method_registry/__init__.py diff --git a/tests/experimental/_methods/test_core.py b/tests/experimental/method_registry/test_core.py similarity index 98% rename from tests/experimental/_methods/test_core.py rename to tests/experimental/method_registry/test_core.py index 3481e97f8..4493028d8 100644 --- a/tests/experimental/_methods/test_core.py +++ b/tests/experimental/method_registry/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental._methods import Registry +from squidpy.experimental.method_registry import Registry @dataclass diff --git a/tests/experimental/_methods/test_stalign.py b/tests/experimental/method_registry/test_stalign.py similarity index 94% rename from tests/experimental/_methods/test_stalign.py rename to tests/experimental/method_registry/test_stalign.py index ef343d2b8..21ae72a44 100644 --- a/tests/experimental/_methods/test_stalign.py +++ b/tests/experimental/method_registry/test_stalign.py @@ -11,8 +11,8 @@ pytest.importorskip("jax") -from squidpy.experimental._methods.align_samples import ALIGN_SAMPLES, fit_stalign -from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult +from squidpy.experimental.method_registry.align_samples import ALIGN_SAMPLES, fit_stalign +from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. _TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index e9894135d..3622c88ad 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -12,7 +12,7 @@ pytest.importorskip("jax") -from squidpy.experimental._methods.align_samples._stalign_impl._tools import StalignResult +from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult from squidpy.experimental.tl import align # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index e54d93bf7..9a8baed3e 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -4,7 +4,7 @@ import pytest from anndata import AnnData -from squidpy.experimental._methods.align_landmarks import AffineFitResult +from squidpy.experimental.method_registry.align_landmarks import AffineFitResult from squidpy.experimental.tl import align_by_landmarks # square corners; query = ref shifted by (5, 7) -> a pure translation both models recover diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index 409364471..91d1ba668 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -13,7 +13,7 @@ import pytest from anndata import AnnData -from squidpy.experimental._methods.align_landmarks import AffineFitResult +from squidpy.experimental.method_registry.align_landmarks import AffineFitResult from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, From f6b907e12d76d42df0253e72c1f61950f3d2ea65 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:15:46 +0000 Subject: [PATCH 103/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/experimental/tl/_align/_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 5874db418..bb0780433 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -37,9 +37,7 @@ def _methods_rst(registry: Registry, indent: str = " " * 8) -> str: """Render a registry's methods as a reST list linking to each implementation.""" - items = [ - f"- ``{key}`` -- :func:`~{(fn := registry.get(key)).__module__}.{fn.__name__}`" for key in registry.keys() - ] + items = [f"- ``{key}`` -- :func:`~{(fn := registry.get(key)).__module__}.{fn.__name__}`" for key in registry.keys()] return ("\n" + indent).join(items) From 265ad0b377881fae0cf1562bcaca4a72ec4d3100 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Sat, 20 Jun 2026 16:26:44 +0200 Subject: [PATCH 104/131] reduce bloat --- .../method_registry/align_samples/_stalign.py | 86 +++++++----- .../align_samples/_stalign_impl/_tools.py | 130 +----------------- 2 files changed, 56 insertions(+), 160 deletions(-) diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign.py b/src/squidpy/experimental/method_registry/align_samples/_stalign.py index b9b849daa..45084d666 100644 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign.py +++ b/src/squidpy/experimental/method_registry/align_samples/_stalign.py @@ -78,45 +78,57 @@ def fit_stalign( # confusing failure from a module-level `import jax`. import jax.numpy as jnp - from ._stalign_impl._helpers import validate_points - from ._stalign_impl._tools import ( - STalignConfig, - STalignPreprocessConfig, - STalignRegistrationConfig, - stalign_points, - ) - - config = STalignConfig( - preprocess=STalignPreprocessConfig(dx=dx, blur=blur, expand=raster_expand), - registration=STalignRegistrationConfig( - a=a, - p=p, - expand=expand, - nt=nt, - niter=niter, - diffeo_start=diffeo_start, - epL=epL, - epT=epT, - epV=epV, - sigmaM=sigmaM, - sigmaB=sigmaB, - sigmaA=sigmaA, - sigmaR=sigmaR, - sigmaP=sigmaP, - ), - ) + from ._stalign_impl._core import jax_dtype, lddmm, transform_points_row_col + from ._stalign_impl._helpers import affine_from_points, validate_points + from ._stalign_impl._tools import StalignResult, _rasterize_cloud - ref_xy = validate_points(ref, name="ref") - query_xy = validate_points(query, name="query") + if (landmarks_source is None) != (landmarks_target is None): + raise ValueError("Expected both landmark arrays to be provided together.") # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. - lm_src = None if landmarks_source is None else jnp.asarray(landmarks_source)[:, ::-1] - lm_tgt = None if landmarks_target is None else jnp.asarray(landmarks_target)[:, ::-1] + source_rc = validate_points(query, name="query")[:, ::-1] + target_rc = validate_points(ref, name="ref")[:, ::-1] + source_grid, source_image = _rasterize_cloud(source_rc, dx=dx, blur=blur, expand=raster_expand) + target_grid, target_image = _rasterize_cloud(target_rc, dx=dx, blur=blur, expand=raster_expand) - return stalign_points( - source_points=query_xy[:, ::-1], - target_points=ref_xy[:, ::-1], - config=config, - landmarks_source=lm_src, - landmarks_target=lm_tgt, + dtype = jax_dtype() + if landmarks_source is None: + linear, translation = jnp.eye(2, dtype=dtype), jnp.zeros(2, dtype=dtype) + src_lm = tgt_lm = None + else: + src_lm = validate_points(landmarks_source, name="landmarks_source")[:, ::-1] + tgt_lm = validate_points(landmarks_target, name="landmarks_target")[:, ::-1] + linear_np, translation_np = affine_from_points(src_lm, tgt_lm) + linear, translation = jnp.asarray(linear_np, dtype=dtype), jnp.asarray(translation_np, dtype=dtype) + + result = lddmm( + source_grid, + source_image, + target_grid, + target_image, + L=linear, + T=translation, + points_source=src_lm, + points_target=tgt_lm, + a=a, + p=p, + expand=expand, + nt=nt, + niter=niter, + diffeo_start=diffeo_start, + epL=epL, + epT=epT, + epV=epV, + sigmaM=sigmaM, + sigmaB=sigmaB, + sigmaA=sigmaA, + sigmaR=sigmaR, + sigmaP=sigmaP, + ) + aligned_rc = transform_points_row_col(result["xv"], result["v"], result["A"], source_rc, direction="forward") + return StalignResult( + affine=result["A"], + velocity=result["v"], + velocity_grid=result["xv"], + aligned_points=aligned_rc[:, ::-1], ) diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py index c5a46b834..fbd236767 100644 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py @@ -1,12 +1,13 @@ -"""Point-cloud tools for experimental STalign. +"""Result type and rasterization helper for experimental STalign. The fitted map is returned as a single :class:`StalignResult`; container -write-back lives in the caller, not here. +write-back lives in the caller, not here. The solver orchestration (rasterize -> +LDDMM) lives in the estimator adapter :func:`...align_samples._stalign.fit_stalign`. """ from __future__ import annotations -from dataclasses import asdict, dataclass, field +from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Literal, TypeAlias if TYPE_CHECKING: @@ -18,51 +19,7 @@ BlurScales: TypeAlias = float | tuple[float, ...] | list[float] -__all__ = [ - "STalignConfig", - "STalignPreprocessConfig", - "STalignRegistrationConfig", - "StalignResult", - "stalign_points", -] - - -@dataclass(slots=True) -class STalignPreprocessConfig: - dx: float = 30.0 - blur: BlurScales = (2.0, 1.0, 0.5) - expand: float = 1.1 - - -@dataclass(slots=True) -class STalignRegistrationConfig: - """LDDMM registration hyperparameters. - - Field names (``sigmaM``, ``epL``, etc.) preserve the conventions from - the STalign paper and reference implementation to keep them - recognisable when cross-referencing the literature. - """ - - a: float = 500.0 - p: float = 2.0 - expand: float = 2.0 - nt: int = 3 - niter: int = 5000 - diffeo_start: int = 0 - epL: float = 2e-8 - epT: float = 2e-1 - epV: float = 2e3 - sigmaM: float = 1.0 - sigmaB: float = 2.0 - sigmaA: float = 5.0 - sigmaR: float = 5e5 - sigmaP: float = 2e1 - - -@dataclass(slots=True) -class STalignConfig: - preprocess: STalignPreprocessConfig = field(default_factory=STalignPreprocessConfig) - registration: STalignRegistrationConfig = field(default_factory=STalignRegistrationConfig) +__all__ = ["StalignResult"] @dataclass(slots=True) @@ -103,83 +60,10 @@ def transform( def _rasterize_cloud( - points_rc: JaxArray, config: STalignPreprocessConfig + points_rc: JaxArray, *, dx: float, blur: BlurScales, expand: float ) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" from ._helpers import rasterize - grid_x, grid_y, image = rasterize( - points_rc[:, 1], - points_rc[:, 0], - dx=config.dx, - blur=config.blur, - expand=config.expand, - ) + grid_x, grid_y, image = rasterize(points_rc[:, 1], points_rc[:, 0], dx=dx, blur=blur, expand=expand) return (grid_y, grid_x), image - - -def stalign_points( - source_points: JaxArray, - target_points: JaxArray, - *, - config: STalignConfig | None = None, - landmarks_source: JaxArray | None = None, - landmarks_target: JaxArray | None = None, -) -> StalignResult: - """Align a source point cloud onto a target with a JAX LDDMM solver. - - All point arrays are in the solver's row-column frame; the returned - :class:`StalignResult` speaks ``(x, y)``. - """ - import jax.numpy as jnp - - from ._core import jax_dtype, lddmm, transform_points_row_col - from ._helpers import affine_from_points, validate_points - - config = STalignConfig() if config is None else config - registration = config.registration - source_points = validate_points(source_points, name="source_points") - target_points = validate_points(target_points, name="target_points") - source_grid, source_image = _rasterize_cloud(source_points, config.preprocess) - target_grid, target_image = _rasterize_cloud(target_points, config.preprocess) - - if (landmarks_source is None) != (landmarks_target is None): - raise ValueError("Expected both landmark arrays to be provided together.") - - dtype = jax_dtype() - if landmarks_source is None: - linear = jnp.eye(2, dtype=dtype) - translation = jnp.zeros(2, dtype=dtype) - source_landmarks = None - target_landmarks = None - else: - source_landmarks = validate_points(landmarks_source, name="landmarks_source") - target_landmarks = validate_points(landmarks_target, name="landmarks_target") - linear_np, translation_np = affine_from_points(source_landmarks, target_landmarks) - linear = jnp.asarray(linear_np, dtype=dtype) - translation = jnp.asarray(translation_np, dtype=dtype) - - result = lddmm( - source_grid, - source_image, - target_grid, - target_image, - L=linear, - T=translation, - points_source=source_landmarks, - points_target=target_landmarks, - **asdict(registration), - ) - transformed_rc = transform_points_row_col( - result["xv"], - result["v"], - result["A"], - source_points, - direction="forward", - ) - return StalignResult( - affine=result["A"], - velocity=result["v"], - velocity_grid=result["xv"], - aligned_points=transformed_rc[:, ::-1], - ) From 035e6e9d4fb2fd41b1f440f1f51eb25502d6eb2c Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Sat, 20 Jun 2026 16:30:44 +0200 Subject: [PATCH 105/131] format --- .../method_registry/align_samples/_stalign_impl/_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py index fbd236767..4afef2284 100644 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py +++ b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py @@ -8,7 +8,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Literal, TypeAlias +from typing import TYPE_CHECKING, Any, Literal if TYPE_CHECKING: import jax @@ -17,7 +17,7 @@ else: # pragma: no cover - typing only JaxArray = Any -BlurScales: TypeAlias = float | tuple[float, ...] | list[float] +type BlurScales = float | tuple[float, ...] | list[float] __all__ = ["StalignResult"] From 6e12e46c43211dd30eb35c117b37a8892488b5d8 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 10:27:47 +0200 Subject: [PATCH 106/131] cleanup --- .../experimental/method_registry/__init__.py | 2 +- .../experimental/method_registry/_families.py | 59 ++++++++++++++++++- .../method_registry/_protocols.py | 59 ------------------- .../align_landmarks/_landmark.py | 32 +++++----- src/squidpy/experimental/tl/_align/_api.py | 10 ++-- 5 files changed, 78 insertions(+), 84 deletions(-) delete mode 100644 src/squidpy/experimental/method_registry/_protocols.py diff --git a/src/squidpy/experimental/method_registry/__init__.py b/src/squidpy/experimental/method_registry/__init__.py index f03ead204..1807536d2 100644 --- a/src/squidpy/experimental/method_registry/__init__.py +++ b/src/squidpy/experimental/method_registry/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from squidpy.experimental.method_registry._protocols import AlignLandmarksFn, AlignResult, AlignSamplesFn +from squidpy.experimental.method_registry._families import AlignLandmarksFn, AlignResult, AlignSamplesFn from squidpy.experimental.method_registry._registry import Registry __all__ = ["Registry", "AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] diff --git a/src/squidpy/experimental/method_registry/_families.py b/src/squidpy/experimental/method_registry/_families.py index a80a61e47..0057ae272 100644 --- a/src/squidpy/experimental/method_registry/_families.py +++ b/src/squidpy/experimental/method_registry/_families.py @@ -1,10 +1,65 @@ -"""Estimator family registries.""" +"""Estimator family registries and the structural contracts they advertise. + +The :class:`~typing.Protocol` types are what the public API and the registries +are typed against, so the orchestration layer never names a concrete estimator +result (e.g. ``StalignResult``). A new estimator only has to satisfy +:class:`AlignResult` -- a ``transform`` that maps points into the reference +frame -- to plug into :func:`squidpy.experimental.tl.align`. +""" from __future__ import annotations -from squidpy.experimental.method_registry._protocols import AlignLandmarksFn, AlignSamplesFn +from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable + +import numpy.typing as npt + +from squidpy._utils import NDArrayA from squidpy.experimental.method_registry._registry import Registry +if TYPE_CHECKING: + from squidpy.experimental.method_registry.align_landmarks._landmark import AffineFitResult + +__all__ = ["AlignResult", "AlignSamplesFn", "AlignLandmarksFn", "ALIGN_SAMPLES", "ALIGN_LANDMARKS"] + + +@runtime_checkable +class AlignResult(Protocol): + """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. + + This is the only thing the public ``align*`` functions require of an + estimator's result, so ``output_mode="object"`` is agnostic to the method + that produced it. + """ + + def transform(self, points: npt.ArrayLike, /) -> NDArrayA: + """Map an ``(N, 2)`` ``(x, y)`` array into the reference frame.""" + ... + + +class AlignSamplesFn(Protocol): + """Calling convention for ``align_samples`` estimators. + + Two point clouds in (passed by keyword as ``ref`` / ``query`` so the + direction can never be silently swapped), one :class:`AlignResult` out. + Solver-specific options arrive through ``**kwargs``. + """ + + def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... + + +class AlignLandmarksFn(Protocol): + """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" + + def __call__( + self, + ref: npt.ArrayLike, + query: npt.ArrayLike, + *, + source_cs: str | None = ..., + target_cs: str | None = ..., + ) -> AffineFitResult: ... + + #: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. #: Consumed by ``squidpy.experimental.tl.align``. ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") diff --git a/src/squidpy/experimental/method_registry/_protocols.py b/src/squidpy/experimental/method_registry/_protocols.py deleted file mode 100644 index ef034fcac..000000000 --- a/src/squidpy/experimental/method_registry/_protocols.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Structural contracts shared across the alignment estimator families. - -These :class:`~typing.Protocol` types are what the public API and the registries -are typed against, so the orchestration layer never names a concrete estimator -result (e.g. ``StalignResult``). A new estimator only has to satisfy -:class:`AlignResult` -- a ``transform`` that maps points into the reference -frame -- to plug into :func:`squidpy.experimental.tl.align`. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable - -import numpy.typing as npt - -from squidpy._utils import NDArrayA - -if TYPE_CHECKING: - from squidpy.experimental.method_registry.align_landmarks._landmark import AffineFitResult - -__all__ = ["AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] - - -@runtime_checkable -class AlignResult(Protocol): - """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. - - This is the only thing the public ``align*`` functions require of an - estimator's result, so ``output_mode="object"`` is agnostic to the method - that produced it. - """ - - def transform(self, points: npt.ArrayLike, /) -> NDArrayA: - """Map an ``(N, 2)`` ``(x, y)`` array into the reference frame.""" - ... - - -class AlignSamplesFn(Protocol): - """Calling convention for ``align_samples`` estimators. - - Two point clouds in (passed by keyword as ``ref`` / ``query`` so the - direction can never be silently swapped), one :class:`AlignResult` out. - Solver-specific options arrive through ``**kwargs``. - """ - - def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... - - -class AlignLandmarksFn(Protocol): - """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" - - def __call__( - self, - landmarks_ref: npt.ArrayLike, - landmarks_query: npt.ArrayLike, - *, - source_cs: str | None = ..., - target_cs: str | None = ..., - ) -> AffineFitResult: ... diff --git a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py index b28aef46f..39c8ca4de 100644 --- a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py @@ -35,20 +35,18 @@ def transform(self, x: npt.ArrayLike) -> NDArrayA: def _fit_landmark_relation( - landmarks_ref: np.ndarray, - landmarks_query: np.ndarray, + ref: np.ndarray, + query: np.ndarray, *, method: str, solve_fn: Callable[[np.ndarray, np.ndarray], np.ndarray], source_cs: str | None = None, target_cs: str | None = None, ) -> AffineFitResult: - ref = _validate_landmarks(landmarks_ref, name="landmarks_ref") - query = _validate_landmarks(landmarks_query, name="landmarks_query") + ref = _validate_landmarks(ref, name="ref") + query = _validate_landmarks(query, name="query") if ref.shape != query.shape: - raise ValueError( - f"`landmarks_ref` and `landmarks_query` must have the same shape; got {ref.shape} and {query.shape}." - ) + raise ValueError(f"`ref` and `query` must have the same shape; got {ref.shape} and {query.shape}.") if ref.shape[0] < 3: raise ValueError(f"`{method}` needs at least 3 landmark pairs, got {ref.shape[0]}.") @@ -63,8 +61,8 @@ def _fit_landmark_relation( @ALIGN_LANDMARKS.register("similarity") def fit_similarity( - landmarks_ref: np.ndarray, - landmarks_query: np.ndarray, + ref: np.ndarray, + query: np.ndarray, *, source_cs: str | None = None, target_cs: str | None = None, @@ -73,15 +71,15 @@ def fit_similarity( Parameters ---------- - landmarks_ref, landmarks_query + ref, query Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). source_cs, target_cs Optional coordinate-system labels stamped onto the result for traceability; they do not affect the fit. """ return _fit_landmark_relation( - landmarks_ref, - landmarks_query, + ref, + query, method="similarity", solve_fn=_fit_similarity, source_cs=source_cs, @@ -91,8 +89,8 @@ def fit_similarity( @ALIGN_LANDMARKS.register("affine") def fit_affine( - landmarks_ref: np.ndarray, - landmarks_query: np.ndarray, + ref: np.ndarray, + query: np.ndarray, *, source_cs: str | None = None, target_cs: str | None = None, @@ -101,15 +99,15 @@ def fit_affine( Parameters ---------- - landmarks_ref, landmarks_query + ref, query Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). source_cs, target_cs Optional coordinate-system labels stamped onto the result for traceability; they do not affect the fit. """ return _fit_landmark_relation( - landmarks_ref, - landmarks_query, + ref, + query, method="affine", solve_fn=_fit_affine, source_cs=source_cs, diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index bb0780433..8c37bcae9 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -132,8 +132,8 @@ def align( @_document_methods(align_landmarks_methods=ALIGN_LANDMARKS) def align_by_landmarks( - landmarks_ref: np.ndarray | Sequence[tuple[float, float]], - landmarks_query: np.ndarray | Sequence[tuple[float, float]], + ref: np.ndarray | Sequence[tuple[float, float]], + query: np.ndarray | Sequence[tuple[float, float]], *, method: Literal["similarity", "affine"] = "similarity", data: AnnData | SpatialData | None = None, @@ -147,7 +147,7 @@ def align_by_landmarks( Parameters ---------- - landmarks_ref, landmarks_query + ref, query Equal-length ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``), paired by row order. No automatic correspondence matching is performed. method @@ -173,8 +173,8 @@ def align_by_landmarks( assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") result = ALIGN_LANDMARKS.get(method)( - landmarks_ref=landmarks_ref, - landmarks_query=landmarks_query, + ref=ref, + query=query, source_cs=cs_name_query, target_cs=cs_name_ref, ) From cbe72b5b0597c2e6cbe516501a5764bc88afe6bc Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 10:57:09 +0200 Subject: [PATCH 107/131] file orga --- .../experimental/method_registry/__init__.py | 30 ++++++- .../experimental/method_registry/_families.py | 69 ---------------- .../align_landmarks/__init__.py | 12 ++- .../align_landmarks/_landmark.py | 2 +- .../method_registry/align_samples/__init__.py | 14 ---- .../align_samples/_stalign_impl/_tools.py | 69 ---------------- .../method_registry/methods/__init__.py | 12 +++ .../methods/align_samples/__init__.py | 14 ++++ .../{ => methods}/align_samples/_stalign.py | 61 ++++++++++++-- .../align_samples/_stalign_impl/__init__.py | 0 .../align_samples/_stalign_impl/_core.py | 0 .../align_samples/_stalign_impl/_helpers.py | 9 +++ .../{_registry.py => registry/__init__.py} | 79 ++++++++++++++++++- src/squidpy/experimental/tl/_align/_api.py | 3 +- 14 files changed, 201 insertions(+), 173 deletions(-) delete mode 100644 src/squidpy/experimental/method_registry/_families.py delete mode 100644 src/squidpy/experimental/method_registry/align_samples/__init__.py delete mode 100644 src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py create mode 100644 src/squidpy/experimental/method_registry/methods/__init__.py create mode 100644 src/squidpy/experimental/method_registry/methods/align_samples/__init__.py rename src/squidpy/experimental/method_registry/{ => methods}/align_samples/_stalign.py (69%) rename src/squidpy/experimental/method_registry/{ => methods}/align_samples/_stalign_impl/__init__.py (100%) rename src/squidpy/experimental/method_registry/{ => methods}/align_samples/_stalign_impl/_core.py (100%) rename src/squidpy/experimental/method_registry/{ => methods}/align_samples/_stalign_impl/_helpers.py (91%) rename src/squidpy/experimental/method_registry/{_registry.py => registry/__init__.py} (52%) diff --git a/src/squidpy/experimental/method_registry/__init__.py b/src/squidpy/experimental/method_registry/__init__.py index 1807536d2..9516ec449 100644 --- a/src/squidpy/experimental/method_registry/__init__.py +++ b/src/squidpy/experimental/method_registry/__init__.py @@ -1,8 +1,30 @@ -"""In-memory model-fitting core for experimental methods.""" +"""In-memory model-fitting core for experimental methods. + +The :mod:`.registry` subpackage holds the registry machinery and the family +registries; :mod:`.methods` holds the estimator implementations. Importing this +package imports :mod:`.methods` so the estimators register themselves into the +(public) family registries. +""" from __future__ import annotations -from squidpy.experimental.method_registry._families import AlignLandmarksFn, AlignResult, AlignSamplesFn -from squidpy.experimental.method_registry._registry import Registry +from squidpy.experimental.method_registry.registry import ( + ALIGN_LANDMARKS, + ALIGN_SAMPLES, + AlignLandmarksFn, + AlignResult, + AlignSamplesFn, + Registry, +) + +# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. +from squidpy.experimental.method_registry import methods # noqa: F401 -__all__ = ["Registry", "AlignResult", "AlignSamplesFn", "AlignLandmarksFn"] +__all__ = [ + "Registry", + "AlignResult", + "AlignSamplesFn", + "AlignLandmarksFn", + "ALIGN_SAMPLES", + "ALIGN_LANDMARKS", +] diff --git a/src/squidpy/experimental/method_registry/_families.py b/src/squidpy/experimental/method_registry/_families.py deleted file mode 100644 index 0057ae272..000000000 --- a/src/squidpy/experimental/method_registry/_families.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Estimator family registries and the structural contracts they advertise. - -The :class:`~typing.Protocol` types are what the public API and the registries -are typed against, so the orchestration layer never names a concrete estimator -result (e.g. ``StalignResult``). A new estimator only has to satisfy -:class:`AlignResult` -- a ``transform`` that maps points into the reference -frame -- to plug into :func:`squidpy.experimental.tl.align`. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable - -import numpy.typing as npt - -from squidpy._utils import NDArrayA -from squidpy.experimental.method_registry._registry import Registry - -if TYPE_CHECKING: - from squidpy.experimental.method_registry.align_landmarks._landmark import AffineFitResult - -__all__ = ["AlignResult", "AlignSamplesFn", "AlignLandmarksFn", "ALIGN_SAMPLES", "ALIGN_LANDMARKS"] - - -@runtime_checkable -class AlignResult(Protocol): - """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. - - This is the only thing the public ``align*`` functions require of an - estimator's result, so ``output_mode="object"`` is agnostic to the method - that produced it. - """ - - def transform(self, points: npt.ArrayLike, /) -> NDArrayA: - """Map an ``(N, 2)`` ``(x, y)`` array into the reference frame.""" - ... - - -class AlignSamplesFn(Protocol): - """Calling convention for ``align_samples`` estimators. - - Two point clouds in (passed by keyword as ``ref`` / ``query`` so the - direction can never be silently swapped), one :class:`AlignResult` out. - Solver-specific options arrive through ``**kwargs``. - """ - - def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... - - -class AlignLandmarksFn(Protocol): - """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" - - def __call__( - self, - ref: npt.ArrayLike, - query: npt.ArrayLike, - *, - source_cs: str | None = ..., - target_cs: str | None = ..., - ) -> AffineFitResult: ... - - -#: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. -#: Consumed by ``squidpy.experimental.tl.align``. -ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") - -#: Closed-form landmark alignment estimators -- paired landmarks in, affine out. -#: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. -ALIGN_LANDMARKS: Registry[AlignLandmarksFn] = Registry("align_landmarks") diff --git a/src/squidpy/experimental/method_registry/align_landmarks/__init__.py b/src/squidpy/experimental/method_registry/align_landmarks/__init__.py index 506185b83..c049ec09d 100644 --- a/src/squidpy/experimental/method_registry/align_landmarks/__init__.py +++ b/src/squidpy/experimental/method_registry/align_landmarks/__init__.py @@ -1,16 +1,20 @@ -"""``align_landmarks`` family: closed-form alignment from paired landmarks.""" +"""``align_landmarks`` family: closed-form alignment from paired landmarks. + +Importing this package registers the family's estimators into +:data:`~squidpy.experimental.method_registry.registry.ALIGN_LANDMARKS`. Only the +implementations are re-exported here; the registry itself lives in (and is public +from) :mod:`squidpy.experimental.method_registry`. +""" from __future__ import annotations -from squidpy.experimental.method_registry._families import ALIGN_LANDMARKS -from squidpy.experimental.method_registry.align_landmarks._landmark import ( +from squidpy.experimental.method_registry.methods.align_landmarks._landmark import ( AffineFitResult, fit_affine, fit_similarity, ) __all__ = [ - "ALIGN_LANDMARKS", "AffineFitResult", "fit_affine", "fit_similarity", diff --git a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py index 39c8ca4de..b1fce233d 100644 --- a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py @@ -10,7 +10,7 @@ import numpy.typing as npt from squidpy._utils import NDArrayA -from squidpy.experimental.method_registry._families import ALIGN_LANDMARKS +from squidpy.experimental.method_registry.registry import ALIGN_LANDMARKS @dataclass diff --git a/src/squidpy/experimental/method_registry/align_samples/__init__.py b/src/squidpy/experimental/method_registry/align_samples/__init__.py deleted file mode 100644 index 5ae764762..000000000 --- a/src/squidpy/experimental/method_registry/align_samples/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -"""``align_samples`` family: align two samples' point clouds (STalign). - -Importing this package registers the family's estimators into -:data:`~squidpy.experimental.method_registry._families.ALIGN_SAMPLES`. It stays cheap -- -JAX is pulled in lazily, only when an estimator's ``fit`` runs. -""" - -from __future__ import annotations - -from squidpy.experimental.method_registry._families import ALIGN_SAMPLES -from squidpy.experimental.method_registry.align_samples._stalign import fit_stalign -from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult - -__all__ = ["ALIGN_SAMPLES", "fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py b/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py deleted file mode 100644 index 4afef2284..000000000 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_tools.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Result type and rasterization helper for experimental STalign. - -The fitted map is returned as a single :class:`StalignResult`; container -write-back lives in the caller, not here. The solver orchestration (rasterize -> -LDDMM) lives in the estimator adapter :func:`...align_samples._stalign.fit_stalign`. -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Literal - -if TYPE_CHECKING: - import jax - - JaxArray = jax.Array -else: # pragma: no cover - typing only - JaxArray = Any - -type BlurScales = float | tuple[float, ...] | list[float] - -__all__ = ["StalignResult"] - - -@dataclass(slots=True) -class StalignResult: - """A fitted STalign diffeomorphism, ready to transform arbitrary points. - - :meth:`transform` works in ``(x, y)``; ``aligned_points`` is the fitted query - cloud already mapped into the reference frame. - """ - - affine: JaxArray - velocity: JaxArray - velocity_grid: tuple[JaxArray, JaxArray] - aligned_points: JaxArray - - def transform( - self, - points: JaxArray, - *, - direction: Literal["forward", "backward"] = "forward", - ) -> JaxArray: - """Map ``(N, 2)`` ``(x, y)`` points with the fitted diffeomorphism.""" - import jax.numpy as jnp - - from ._core import jax_dtype, transform_points_row_col - - pts = jnp.asarray(points, dtype=jax_dtype()) - if pts.ndim != 2 or pts.shape[1] != 2: - raise ValueError(f"Expected an (N, 2) `(x, y)` array, found shape {pts.shape}.") - transformed_rc = transform_points_row_col( - self.velocity_grid, - self.velocity, - self.affine, - pts[:, ::-1], - direction=direction, - ) - return transformed_rc[:, ::-1] - - -def _rasterize_cloud( - points_rc: JaxArray, *, dx: float, blur: BlurScales, expand: float -) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: - """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" - from ._helpers import rasterize - - grid_x, grid_y, image = rasterize(points_rc[:, 1], points_rc[:, 0], dx=dx, blur=blur, expand=expand) - return (grid_y, grid_x), image diff --git a/src/squidpy/experimental/method_registry/methods/__init__.py b/src/squidpy/experimental/method_registry/methods/__init__.py new file mode 100644 index 000000000..21af5d1cf --- /dev/null +++ b/src/squidpy/experimental/method_registry/methods/__init__.py @@ -0,0 +1,12 @@ +"""Estimator implementations, one subpackage per method family. + +Importing this package imports each family subpackage, whose modules register +their estimators into the family registries in +:mod:`squidpy.experimental.method_registry.registry`. Each subpackage stays +cheap to import -- heavy/optional dependencies (e.g. JAX) are pulled in lazily, +only when an estimator actually runs. +""" + +from __future__ import annotations + +from squidpy.experimental.method_registry.methods import align_landmarks, align_samples # noqa: F401 diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/__init__.py b/src/squidpy/experimental/method_registry/methods/align_samples/__init__.py new file mode 100644 index 000000000..f0120e440 --- /dev/null +++ b/src/squidpy/experimental/method_registry/methods/align_samples/__init__.py @@ -0,0 +1,14 @@ +"""``align_samples`` family: align two samples' point clouds (STalign). + +Importing this package registers the family's estimators into +:data:`~squidpy.experimental.method_registry.registry.ALIGN_SAMPLES`. It stays +cheap -- JAX is pulled in lazily, only when an estimator's ``fit`` runs. Only the +implementations are re-exported here; the registry itself lives in (and is public +from) :mod:`squidpy.experimental.method_registry`. +""" + +from __future__ import annotations + +from squidpy.experimental.method_registry.methods.align_samples._stalign import StalignResult, fit_stalign + +__all__ = ["fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign.py b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py similarity index 69% rename from src/squidpy/experimental/method_registry/align_samples/_stalign.py rename to src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py index 45084d666..d3853020f 100644 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign.py +++ b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py @@ -1,16 +1,62 @@ -"""STalign estimator: JAX LDDMM point-cloud registration.""" +"""STalign estimator: JAX LDDMM point-cloud registration. + +Holds both the estimator adapter :func:`fit_stalign` and its result type +:class:`StalignResult`; the pure numerics live under :mod:`._stalign_impl`. +""" from __future__ import annotations from collections.abc import Sequence -from typing import TYPE_CHECKING +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Literal import numpy.typing as npt -from squidpy.experimental.method_registry._families import ALIGN_SAMPLES +from squidpy.experimental.method_registry.registry import ALIGN_SAMPLES if TYPE_CHECKING: - from ._stalign_impl._tools import StalignResult + import jax + + JaxArray = jax.Array +else: # pragma: no cover - typing only + JaxArray = Any + + +@dataclass(slots=True) +class StalignResult: + """A fitted STalign diffeomorphism, ready to transform arbitrary points. + + :meth:`transform` works in ``(x, y)``; ``aligned_points`` is the fitted query + cloud already mapped into the reference frame. + """ + + affine: JaxArray + velocity: JaxArray + velocity_grid: tuple[JaxArray, JaxArray] + aligned_points: JaxArray + + def transform( + self, + points: JaxArray, + *, + direction: Literal["forward", "backward"] = "forward", + ) -> JaxArray: + """Map ``(N, 2)`` ``(x, y)`` points with the fitted diffeomorphism.""" + import jax.numpy as jnp + + from ._stalign_impl._core import jax_dtype, transform_points_row_col + + pts = jnp.asarray(points, dtype=jax_dtype()) + if pts.ndim != 2 or pts.shape[1] != 2: + raise ValueError(f"Expected an (N, 2) `(x, y)` array, found shape {pts.shape}.") + transformed_rc = transform_points_row_col( + self.velocity_grid, + self.velocity, + self.affine, + pts[:, ::-1], + direction=direction, + ) + return transformed_rc[:, ::-1] @ALIGN_SAMPLES.register("stalign", requires=("jax",)) @@ -79,8 +125,7 @@ def fit_stalign( import jax.numpy as jnp from ._stalign_impl._core import jax_dtype, lddmm, transform_points_row_col - from ._stalign_impl._helpers import affine_from_points, validate_points - from ._stalign_impl._tools import StalignResult, _rasterize_cloud + from ._stalign_impl._helpers import affine_from_points, rasterize_cloud, validate_points if (landmarks_source is None) != (landmarks_target is None): raise ValueError("Expected both landmark arrays to be provided together.") @@ -88,8 +133,8 @@ def fit_stalign( # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. source_rc = validate_points(query, name="query")[:, ::-1] target_rc = validate_points(ref, name="ref")[:, ::-1] - source_grid, source_image = _rasterize_cloud(source_rc, dx=dx, blur=blur, expand=raster_expand) - target_grid, target_image = _rasterize_cloud(target_rc, dx=dx, blur=blur, expand=raster_expand) + source_grid, source_image = rasterize_cloud(source_rc, dx=dx, blur=blur, expand=raster_expand) + target_grid, target_image = rasterize_cloud(target_rc, dx=dx, blur=blur, expand=raster_expand) dtype = jax_dtype() if landmarks_source is None: diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/__init__.py similarity index 100% rename from src/squidpy/experimental/method_registry/align_samples/_stalign_impl/__init__.py rename to src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/__init__.py diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_core.py similarity index 100% rename from src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_core.py rename to src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_core.py diff --git a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_helpers.py similarity index 91% rename from src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_helpers.py rename to src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_helpers.py index 9da0a2067..82c89ae58 100644 --- a/src/squidpy/experimental/method_registry/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_helpers.py @@ -19,10 +19,19 @@ __all__ = [ "affine_from_points", "rasterize", + "rasterize_cloud", "validate_points", ] +def rasterize_cloud( + points_rc: JaxArray, *, dx: float, blur: float | list[float], expand: float +) -> tuple[tuple[JaxArray, JaxArray], JaxArray]: + """Rasterize a row-col cloud into a ``((grid_y, grid_x), image)`` density.""" + grid_x, grid_y, image = rasterize(points_rc[:, 1], points_rc[:, 0], dx=dx, blur=blur, expand=expand) + return (grid_y, grid_x), image + + def validate_points(points: Any, *, name: str) -> JaxArray: """Coerce ``points`` to a finite ``(n, 2)`` JAX array.""" arr = jnp.asarray(points, dtype=jax_dtype()) diff --git a/src/squidpy/experimental/method_registry/_registry.py b/src/squidpy/experimental/method_registry/registry/__init__.py similarity index 52% rename from src/squidpy/experimental/method_registry/_registry.py rename to src/squidpy/experimental/method_registry/registry/__init__.py index 75d7c1ab0..cb9bc49b6 100644 --- a/src/squidpy/experimental/method_registry/_registry.py +++ b/src/squidpy/experimental/method_registry/registry/__init__.py @@ -1,11 +1,39 @@ -"""A flat registry mapping method names to fitting functions.""" +"""The registry machinery and the family registries it powers. + +This module holds three things that belong together: + +* :class:`Registry` -- a flat ``name -> function`` map, one per method *family*. +* The structural :class:`~typing.Protocol` contracts each family advertises, so + the public API and the registries are typed against a contract rather than a + concrete estimator result (e.g. ``StalignResult``). A new estimator only has to + satisfy :class:`AlignResult` -- a ``transform`` that maps points into the + reference frame -- to plug into :func:`squidpy.experimental.tl.align`. +* The family registry instances (:data:`ALIGN_SAMPLES`, :data:`ALIGN_LANDMARKS`) + the estimator implementations register into. +""" from __future__ import annotations import functools import importlib.util from collections.abc import Callable -from typing import Any, Generic, TypeVar +from typing import TYPE_CHECKING, Any, Generic, Protocol, TypeVar, runtime_checkable + +import numpy.typing as npt + +from squidpy._utils import NDArrayA + +if TYPE_CHECKING: + from squidpy.experimental.method_registry.methods.align_landmarks._landmark import AffineFitResult + +__all__ = [ + "Registry", + "AlignResult", + "AlignSamplesFn", + "AlignLandmarksFn", + "ALIGN_SAMPLES", + "ALIGN_LANDMARKS", +] #: The calling convention a family's registry advertises (returned by :meth:`Registry.get`). F = TypeVar("F", bound=Callable[..., Any]) @@ -71,3 +99,50 @@ def get(self, key: str) -> F: def keys(self) -> tuple[str, ...]: """Return the registered method names.""" return tuple(self._registry) + + +@runtime_checkable +class AlignResult(Protocol): + """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. + + This is the only thing the public ``align*`` functions require of an + estimator's result, so ``output_mode="object"`` is agnostic to the method + that produced it. + """ + + def transform(self, points: npt.ArrayLike, /) -> NDArrayA: + """Map an ``(N, 2)`` ``(x, y)`` array into the reference frame.""" + ... + + +class AlignSamplesFn(Protocol): + """Calling convention for ``align_samples`` estimators. + + Two point clouds in (passed by keyword as ``ref`` / ``query`` so the + direction can never be silently swapped), one :class:`AlignResult` out. + Solver-specific options arrive through ``**kwargs``. + """ + + def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... + + +class AlignLandmarksFn(Protocol): + """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" + + def __call__( + self, + ref: npt.ArrayLike, + query: npt.ArrayLike, + *, + source_cs: str | None = ..., + target_cs: str | None = ..., + ) -> AffineFitResult: ... + + +#: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. +#: Consumed by ``squidpy.experimental.tl.align``. +ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") + +#: Closed-form landmark alignment estimators -- paired landmarks in, affine out. +#: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. +ALIGN_LANDMARKS: Registry[AlignLandmarksFn] = Registry("align_landmarks") diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 8c37bcae9..537a6eb89 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -15,8 +15,7 @@ from spatialdata import SpatialData from squidpy._validators import assert_one_of -from squidpy.experimental.method_registry.align_landmarks import ALIGN_LANDMARKS -from squidpy.experimental.method_registry.align_samples import ALIGN_SAMPLES +from squidpy.experimental.method_registry import ALIGN_LANDMARKS, ALIGN_SAMPLES from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, From 4e5909c8358184deccf557cb57b96171308724e3 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 10:57:14 +0200 Subject: [PATCH 108/131] file orga --- docs/api.md | 10 +++--- .../experimental/method_registry/__init__.py | 30 ----------------- .../method_registry/methods/__init__.py | 12 ------- src/squidpy/experimental/methods/__init__.py | 33 +++++++++++++++++++ .../align_landmarks/__init__.py | 6 ++-- .../align_landmarks/_landmark.py | 2 +- .../methods/align_samples/__init__.py | 6 ++-- .../methods/align_samples/_stalign.py | 2 +- .../align_samples/_stalign_impl/__init__.py | 0 .../align_samples/_stalign_impl/_core.py | 0 .../align_samples/_stalign_impl/_helpers.py | 0 .../registry/__init__.py | 2 +- src/squidpy/experimental/tl/__init__.py | 4 +-- src/squidpy/experimental/tl/_align/_api.py | 6 ++-- src/squidpy/experimental/tl/_align/_io.py | 6 ++-- 15 files changed, 55 insertions(+), 64 deletions(-) delete mode 100644 src/squidpy/experimental/method_registry/__init__.py delete mode 100644 src/squidpy/experimental/method_registry/methods/__init__.py create mode 100644 src/squidpy/experimental/methods/__init__.py rename src/squidpy/experimental/{method_registry => methods}/align_landmarks/__init__.py (64%) rename src/squidpy/experimental/{method_registry => methods}/align_landmarks/_landmark.py (98%) rename src/squidpy/experimental/{method_registry => }/methods/align_samples/__init__.py (61%) rename src/squidpy/experimental/{method_registry => }/methods/align_samples/_stalign.py (98%) rename src/squidpy/experimental/{method_registry => }/methods/align_samples/_stalign_impl/__init__.py (100%) rename src/squidpy/experimental/{method_registry => }/methods/align_samples/_stalign_impl/_core.py (100%) rename src/squidpy/experimental/{method_registry => }/methods/align_samples/_stalign_impl/_helpers.py (100%) rename src/squidpy/experimental/{method_registry => methods}/registry/__init__.py (98%) diff --git a/docs/api.md b/docs/api.md index ad48c98b4..6908ee9fd 100644 --- a/docs/api.md +++ b/docs/api.md @@ -151,11 +151,11 @@ result types listed alongside. .. autosummary:: :toctree: api - experimental.method_registry.align_samples._stalign.fit_stalign - experimental.method_registry.align_landmarks._landmark.fit_similarity - experimental.method_registry.align_landmarks._landmark.fit_affine - experimental.method_registry.align_samples._stalign_impl._tools.StalignResult - experimental.method_registry.align_landmarks._landmark.AffineFitResult + experimental.methods.align_samples._stalign.fit_stalign + experimental.methods.align_landmarks._landmark.fit_similarity + experimental.methods.align_landmarks._landmark.fit_affine + experimental.methods.align_samples._stalign.StalignResult + experimental.methods.align_landmarks._landmark.AffineFitResult ``` ## Experimental diff --git a/src/squidpy/experimental/method_registry/__init__.py b/src/squidpy/experimental/method_registry/__init__.py deleted file mode 100644 index 9516ec449..000000000 --- a/src/squidpy/experimental/method_registry/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -"""In-memory model-fitting core for experimental methods. - -The :mod:`.registry` subpackage holds the registry machinery and the family -registries; :mod:`.methods` holds the estimator implementations. Importing this -package imports :mod:`.methods` so the estimators register themselves into the -(public) family registries. -""" - -from __future__ import annotations - -from squidpy.experimental.method_registry.registry import ( - ALIGN_LANDMARKS, - ALIGN_SAMPLES, - AlignLandmarksFn, - AlignResult, - AlignSamplesFn, - Registry, -) - -# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. -from squidpy.experimental.method_registry import methods # noqa: F401 - -__all__ = [ - "Registry", - "AlignResult", - "AlignSamplesFn", - "AlignLandmarksFn", - "ALIGN_SAMPLES", - "ALIGN_LANDMARKS", -] diff --git a/src/squidpy/experimental/method_registry/methods/__init__.py b/src/squidpy/experimental/method_registry/methods/__init__.py deleted file mode 100644 index 21af5d1cf..000000000 --- a/src/squidpy/experimental/method_registry/methods/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Estimator implementations, one subpackage per method family. - -Importing this package imports each family subpackage, whose modules register -their estimators into the family registries in -:mod:`squidpy.experimental.method_registry.registry`. Each subpackage stays -cheap to import -- heavy/optional dependencies (e.g. JAX) are pulled in lazily, -only when an estimator actually runs. -""" - -from __future__ import annotations - -from squidpy.experimental.method_registry.methods import align_landmarks, align_samples # noqa: F401 diff --git a/src/squidpy/experimental/methods/__init__.py b/src/squidpy/experimental/methods/__init__.py new file mode 100644 index 000000000..5de9c9f1b --- /dev/null +++ b/src/squidpy/experimental/methods/__init__.py @@ -0,0 +1,33 @@ +"""In-memory model-fitting core for experimental methods. + +The :mod:`.registry` subpackage holds the registry machinery and the family +registries; each family subpackage (e.g. :mod:`.align_samples`, +:mod:`.align_landmarks`) holds the estimator implementations. Importing this +package imports those subpackages so the estimators register themselves into the +(public) family registries. Each subpackage stays cheap to import -- heavy or +optional dependencies (e.g. JAX) are pulled in lazily, only when an estimator +actually runs. +""" + +from __future__ import annotations + +from squidpy.experimental.methods.registry import ( + ALIGN_LANDMARKS, + ALIGN_SAMPLES, + AlignLandmarksFn, + AlignResult, + AlignSamplesFn, + Registry, +) + +# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. +from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 + +__all__ = [ + "Registry", + "AlignResult", + "AlignSamplesFn", + "AlignLandmarksFn", + "ALIGN_SAMPLES", + "ALIGN_LANDMARKS", +] diff --git a/src/squidpy/experimental/method_registry/align_landmarks/__init__.py b/src/squidpy/experimental/methods/align_landmarks/__init__.py similarity index 64% rename from src/squidpy/experimental/method_registry/align_landmarks/__init__.py rename to src/squidpy/experimental/methods/align_landmarks/__init__.py index c049ec09d..6aee5d9a4 100644 --- a/src/squidpy/experimental/method_registry/align_landmarks/__init__.py +++ b/src/squidpy/experimental/methods/align_landmarks/__init__.py @@ -1,14 +1,14 @@ """``align_landmarks`` family: closed-form alignment from paired landmarks. Importing this package registers the family's estimators into -:data:`~squidpy.experimental.method_registry.registry.ALIGN_LANDMARKS`. Only the +:data:`~squidpy.experimental.methods.registry.ALIGN_LANDMARKS`. Only the implementations are re-exported here; the registry itself lives in (and is public -from) :mod:`squidpy.experimental.method_registry`. +from) :mod:`squidpy.experimental.methods`. """ from __future__ import annotations -from squidpy.experimental.method_registry.methods.align_landmarks._landmark import ( +from squidpy.experimental.methods.align_landmarks._landmark import ( AffineFitResult, fit_affine, fit_similarity, diff --git a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py b/src/squidpy/experimental/methods/align_landmarks/_landmark.py similarity index 98% rename from src/squidpy/experimental/method_registry/align_landmarks/_landmark.py rename to src/squidpy/experimental/methods/align_landmarks/_landmark.py index b1fce233d..e53369273 100644 --- a/src/squidpy/experimental/method_registry/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/methods/align_landmarks/_landmark.py @@ -10,7 +10,7 @@ import numpy.typing as npt from squidpy._utils import NDArrayA -from squidpy.experimental.method_registry.registry import ALIGN_LANDMARKS +from squidpy.experimental.methods.registry import ALIGN_LANDMARKS @dataclass diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/__init__.py b/src/squidpy/experimental/methods/align_samples/__init__.py similarity index 61% rename from src/squidpy/experimental/method_registry/methods/align_samples/__init__.py rename to src/squidpy/experimental/methods/align_samples/__init__.py index f0120e440..f446f2d38 100644 --- a/src/squidpy/experimental/method_registry/methods/align_samples/__init__.py +++ b/src/squidpy/experimental/methods/align_samples/__init__.py @@ -1,14 +1,14 @@ """``align_samples`` family: align two samples' point clouds (STalign). Importing this package registers the family's estimators into -:data:`~squidpy.experimental.method_registry.registry.ALIGN_SAMPLES`. It stays +:data:`~squidpy.experimental.methods.registry.ALIGN_SAMPLES`. It stays cheap -- JAX is pulled in lazily, only when an estimator's ``fit`` runs. Only the implementations are re-exported here; the registry itself lives in (and is public -from) :mod:`squidpy.experimental.method_registry`. +from) :mod:`squidpy.experimental.methods`. """ from __future__ import annotations -from squidpy.experimental.method_registry.methods.align_samples._stalign import StalignResult, fit_stalign +from squidpy.experimental.methods.align_samples._stalign import StalignResult, fit_stalign __all__ = ["fit_stalign", "StalignResult"] diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py similarity index 98% rename from src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py rename to src/squidpy/experimental/methods/align_samples/_stalign.py index d3853020f..bb16ffdad 100644 --- a/src/squidpy/experimental/method_registry/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -12,7 +12,7 @@ import numpy.typing as npt -from squidpy.experimental.method_registry.registry import ALIGN_SAMPLES +from squidpy.experimental.methods.registry import ALIGN_SAMPLES if TYPE_CHECKING: import jax diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/__init__.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/__init__.py similarity index 100% rename from src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/__init__.py rename to src/squidpy/experimental/methods/align_samples/_stalign_impl/__init__.py diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py similarity index 100% rename from src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_core.py rename to src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py diff --git a/src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py similarity index 100% rename from src/squidpy/experimental/method_registry/methods/align_samples/_stalign_impl/_helpers.py rename to src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py diff --git a/src/squidpy/experimental/method_registry/registry/__init__.py b/src/squidpy/experimental/methods/registry/__init__.py similarity index 98% rename from src/squidpy/experimental/method_registry/registry/__init__.py rename to src/squidpy/experimental/methods/registry/__init__.py index cb9bc49b6..99a002746 100644 --- a/src/squidpy/experimental/method_registry/registry/__init__.py +++ b/src/squidpy/experimental/methods/registry/__init__.py @@ -24,7 +24,7 @@ from squidpy._utils import NDArrayA if TYPE_CHECKING: - from squidpy.experimental.method_registry.methods.align_landmarks._landmark import AffineFitResult + from squidpy.experimental.methods.align_landmarks._landmark import AffineFitResult __all__ = [ "Registry", diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index abad050c7..968c1b565 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -3,9 +3,9 @@ # `AlignResult` is the only result type on the public surface: it is the estimator # contract (a `transform` mapping points into the reference frame) and the declared # return of `align` / `align_by_landmarks`. The concrete results (`StalignResult`, -# `AffineFitResult`) stay in their home modules under `squidpy.experimental.method_registry` +# `AffineFitResult`) stay in their home modules under `squidpy.experimental.methods` # for callers that need raw fields -- the public API stays method-agnostic. -from squidpy.experimental.method_registry import AlignResult +from squidpy.experimental.methods import AlignResult from ._align import align, align_by_landmarks from ._tiling_qc import TilingQCParams, calculate_tiling_qc diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 537a6eb89..66c454cf8 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -1,4 +1,4 @@ -"""Public alignment functions built on the :mod:`squidpy.experimental.method_registry` core. +"""Public alignment functions built on the :mod:`squidpy.experimental.methods` core. These are thin orchestrators: resolve inputs to in-memory arrays, dispatch to a fit-core estimator, write the result back. All container I/O and write-back live @@ -15,7 +15,7 @@ from spatialdata import SpatialData from squidpy._validators import assert_one_of -from squidpy.experimental.method_registry import ALIGN_LANDMARKS, ALIGN_SAMPLES +from squidpy.experimental.methods import ALIGN_LANDMARKS, ALIGN_SAMPLES from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, @@ -24,7 +24,7 @@ ) if TYPE_CHECKING: - from squidpy.experimental.method_registry import AlignResult, Registry + from squidpy.experimental.methods import AlignResult, Registry OUTPUT_MODES = ("object", "copy", "inplace") ON_VALUES = ("obs", "image") diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index a1c35cb68..24bb55b04 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -2,7 +2,7 @@ This is the *only* layer that knows about the ``AnnData | SpatialData`` argument shapes and the ``output_mode`` write-back strategies. The fit estimators in -:mod:`squidpy.experimental.method_registry` operate on plain arrays and never see a +:mod:`squidpy.experimental.methods` operate on plain arrays and never see a container. """ @@ -17,8 +17,8 @@ from squidpy._validators import assert_key_in_sdata if TYPE_CHECKING: - from squidpy.experimental.method_registry import AlignResult - from squidpy.experimental.method_registry.align_landmarks import AffineFitResult + from squidpy.experimental.methods import AlignResult + from squidpy.experimental.methods.align_landmarks import AffineFitResult # --------------------------------------------------------------------------- From 3f9ab341409cb252d85c2269be6097343cf236f1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 08:57:52 +0000 Subject: [PATCH 109/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/squidpy/experimental/methods/__init__.py | 5 ++--- tests/experimental/method_registry/test_core.py | 1 - tests/experimental/tl/test_align.py | 1 + tests/experimental/tl/test_align_by_landmarks.py | 2 +- tests/experimental/tl/test_align_io.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/squidpy/experimental/methods/__init__.py b/src/squidpy/experimental/methods/__init__.py index 5de9c9f1b..4beec0617 100644 --- a/src/squidpy/experimental/methods/__init__.py +++ b/src/squidpy/experimental/methods/__init__.py @@ -11,6 +11,8 @@ from __future__ import annotations +# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. +from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 from squidpy.experimental.methods.registry import ( ALIGN_LANDMARKS, ALIGN_SAMPLES, @@ -20,9 +22,6 @@ Registry, ) -# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. -from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 - __all__ = [ "Registry", "AlignResult", diff --git a/tests/experimental/method_registry/test_core.py b/tests/experimental/method_registry/test_core.py index 4493028d8..57cacb89c 100644 --- a/tests/experimental/method_registry/test_core.py +++ b/tests/experimental/method_registry/test_core.py @@ -5,7 +5,6 @@ import numpy as np import pytest - from squidpy.experimental.method_registry import Registry diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 3622c88ad..cd48593d2 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -13,6 +13,7 @@ pytest.importorskip("jax") from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult + from squidpy.experimental.tl import align # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index 9a8baed3e..b0667fd72 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -3,8 +3,8 @@ import numpy as np import pytest from anndata import AnnData - from squidpy.experimental.method_registry.align_landmarks import AffineFitResult + from squidpy.experimental.tl import align_by_landmarks # square corners; query = ref shifted by (5, 7) -> a pure translation both models recover diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index 91d1ba668..79a1b92f7 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -12,8 +12,8 @@ import numpy as np import pytest from anndata import AnnData - from squidpy.experimental.method_registry.align_landmarks import AffineFitResult + from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, From 5646282f7465959356d2ebb8e1b280fe2ec778b9 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 10:58:03 +0200 Subject: [PATCH 110/131] fix tests and imports --- tests/experimental/{method_registry => methods}/__init__.py | 0 tests/experimental/{method_registry => methods}/test_core.py | 2 +- .../experimental/{method_registry => methods}/test_stalign.py | 4 ++-- tests/experimental/tl/test_align.py | 2 +- tests/experimental/tl/test_align_by_landmarks.py | 2 +- tests/experimental/tl/test_align_io.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename tests/experimental/{method_registry => methods}/__init__.py (100%) rename tests/experimental/{method_registry => methods}/test_core.py (98%) rename tests/experimental/{method_registry => methods}/test_stalign.py (94%) diff --git a/tests/experimental/method_registry/__init__.py b/tests/experimental/methods/__init__.py similarity index 100% rename from tests/experimental/method_registry/__init__.py rename to tests/experimental/methods/__init__.py diff --git a/tests/experimental/method_registry/test_core.py b/tests/experimental/methods/test_core.py similarity index 98% rename from tests/experimental/method_registry/test_core.py rename to tests/experimental/methods/test_core.py index 4493028d8..5864bf1aa 100644 --- a/tests/experimental/method_registry/test_core.py +++ b/tests/experimental/methods/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental.method_registry import Registry +from squidpy.experimental.methods import Registry @dataclass diff --git a/tests/experimental/method_registry/test_stalign.py b/tests/experimental/methods/test_stalign.py similarity index 94% rename from tests/experimental/method_registry/test_stalign.py rename to tests/experimental/methods/test_stalign.py index 21ae72a44..0261f4a51 100644 --- a/tests/experimental/method_registry/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -11,8 +11,8 @@ pytest.importorskip("jax") -from squidpy.experimental.method_registry.align_samples import ALIGN_SAMPLES, fit_stalign -from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult +from squidpy.experimental.methods import ALIGN_SAMPLES +from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. _TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 3622c88ad..d1a48ed52 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -12,7 +12,7 @@ pytest.importorskip("jax") -from squidpy.experimental.method_registry.align_samples._stalign_impl._tools import StalignResult +from squidpy.experimental.methods.align_samples import StalignResult from squidpy.experimental.tl import align # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index 9a8baed3e..f179bff35 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -4,7 +4,7 @@ import pytest from anndata import AnnData -from squidpy.experimental.method_registry.align_landmarks import AffineFitResult +from squidpy.experimental.methods.align_landmarks import AffineFitResult from squidpy.experimental.tl import align_by_landmarks # square corners; query = ref shifted by (5, 7) -> a pure translation both models recover diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index 91d1ba668..c241d95b7 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -13,7 +13,7 @@ import pytest from anndata import AnnData -from squidpy.experimental.method_registry.align_landmarks import AffineFitResult +from squidpy.experimental.methods.align_landmarks import AffineFitResult from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, From 4a6e6ff85aa6a6f7a74caca757e9bd07e11ef73f Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 10:59:55 +0200 Subject: [PATCH 111/131] fix merge error --- tests/experimental/tl/test_align_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index 79a1b92f7..fc51a2ab9 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -12,7 +12,7 @@ import numpy as np import pytest from anndata import AnnData -from squidpy.experimental.method_registry.align_landmarks import AffineFitResult +from squidpy.experimental.methods.align_landmarks import AffineFitResult from squidpy.experimental.tl._align._io import ( get_coords, From bb20bc057581d70dcf6dde432ad27dc5a474160b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 09:00:18 +0000 Subject: [PATCH 112/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/experimental/tl/test_align_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index fc51a2ab9..c241d95b7 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -12,8 +12,8 @@ import numpy as np import pytest from anndata import AnnData -from squidpy.experimental.methods.align_landmarks import AffineFitResult +from squidpy.experimental.methods.align_landmarks import AffineFitResult from squidpy.experimental.tl._align._io import ( get_coords, resolve_obs_pair, From 44a5a22d8c83668cfd1e8b5b7afcc7e75bde3eeb Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:00:28 +0200 Subject: [PATCH 113/131] fix merge error --- tests/experimental/methods/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/experimental/methods/test_core.py b/tests/experimental/methods/test_core.py index 57cacb89c..238277b2b 100644 --- a/tests/experimental/methods/test_core.py +++ b/tests/experimental/methods/test_core.py @@ -5,7 +5,7 @@ import numpy as np import pytest -from squidpy.experimental.method_registry import Registry +from squidpy.experimental.methods.registry import Registry @dataclass From 6ed6344efb18c00a1b30729b81c50c1954e0d1bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 09:00:45 +0000 Subject: [PATCH 114/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/experimental/methods/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/experimental/methods/test_core.py b/tests/experimental/methods/test_core.py index 238277b2b..901b6cde1 100644 --- a/tests/experimental/methods/test_core.py +++ b/tests/experimental/methods/test_core.py @@ -5,6 +5,7 @@ import numpy as np import pytest + from squidpy.experimental.methods.registry import Registry From 6a0b10605338d9c2c261a892cf645a12fef8c14d Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:10:51 +0200 Subject: [PATCH 115/131] autodoc mock jax --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 0f413dfac..b6ddc5548 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -103,6 +103,7 @@ autodoc_member_order = "groupwise" autodoc_typehints = "signature" autodoc_docstring_signature = True +autodoc_mock_imports = ["jax"] napoleon_google_docstring = False napoleon_numpy_docstring = True napoleon_include_init_with_doc = False From 21cd780591ba36e2cb14ce7feefd0fabd9a5c607 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:14:48 +0200 Subject: [PATCH 116/131] remove casting --- src/squidpy/experimental/tl/_align/_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index 24bb55b04..9b38de411 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -68,7 +68,7 @@ def get_coords(adata: AnnData, spatial_key: str) -> np.ndarray: """Return a validated ``(N, 2)`` ``(x, y)`` coordinate array from ``obsm``.""" if spatial_key not in adata.obsm: raise KeyError(f"`obsm[{spatial_key!r}]` not found; pass `spatial_key=` to select the coordinate key.") - arr = np.asarray(adata.obsm[spatial_key], dtype=float) + arr = np.asarray(adata.obsm[spatial_key]) if arr.ndim != 2 or arr.shape[1] != 2: raise ValueError(f"`obsm[{spatial_key!r}]` must be an (N, 2) array, found shape {arr.shape}.") return arr From 9236ac35e61439e505bf14624dce3f7cbd40f5c4 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:16:26 +0200 Subject: [PATCH 117/131] strict=True --- .../methods/align_samples/_stalign_impl/_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py index 82c89ae58..a02360f8a 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py @@ -90,7 +90,7 @@ def rasterize( radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) denom = 2.0 * (dx * blur_values * 2.0) ** 2 - for x_i, y_i in zip(x, y, strict=False): + for x_i, y_i in zip(x, y, strict=True): col = int(np.rint((x_i - grid_x[0]) / dx)) row = int(np.rint((y_i - grid_y[0]) / dx)) From 5c663a2f3d3be6d947e804c2e428e64a186fd7b1 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:23:13 +0200 Subject: [PATCH 118/131] add jax docs --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index b6ddc5548..7f13ca59e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -148,6 +148,8 @@ ("py:class", "NDArray"), ("py:class", "np.number"), ("py:class", "csr_matrix"), + # optional dep mocked at build time (see autodoc_mock_imports), so no resolvable target + ("py:class", "jax.Array"), # no idea why those aren’t exported ("py:class", "squidpy._constants._constants.SpatialAutocorr"), ("py:class", "squidpy._constants._constants.CoordType"), From c07a707ea042b1132ce8cc19ec23e5856e583235 Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:25:38 +0200 Subject: [PATCH 119/131] speed up rasterize by avoiding loop --- .../align_samples/_stalign_impl/_helpers.py | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py index a02360f8a..3cb17ef95 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py @@ -52,9 +52,14 @@ def rasterize( ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Rasterize a point cloud into a multi-scale Gaussian density image. - Each point splats a normalized Gaussian over a fixed ``(2r + 1)`` patch and - the patches are accumulated onto the grid. + Each point deposits unit mass into its nearest cell of a regular + ``dx``-spaced grid; every ``blur`` scale is then an isotropic Gaussian blur + of that histogram, so each point becomes a unit-integral Gaussian. ``blur`` + is the kernel width in units of ``2 * dx`` (``sigma = 2 * blur`` cells). + Mass within ~``blur`` cells of the border leaks off-grid (``mode="constant"``). """ + from scipy.ndimage import gaussian_filter + x = np.asarray(x, dtype=float).reshape(-1) y = np.asarray(y, dtype=float).reshape(-1) if x.shape != y.shape: @@ -85,28 +90,15 @@ def rasterize( if grid_x.size < 2 or grid_y.size < 2: raise ValueError("Rasterized grid is too small. Increase the point spread or lower `dx`.") - mesh_x, mesh_y = np.meshgrid(grid_x, grid_y) - out = np.zeros((len(blur_values), grid_y.size, grid_x.size), dtype=float) - radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) - denom = 2.0 * (dx * blur_values * 2.0) ** 2 - - for x_i, y_i in zip(x, y, strict=True): - col = int(np.rint((x_i - grid_x[0]) / dx)) - row = int(np.rint((y_i - grid_y[0]) / dx)) - - row0 = max(row - radius, 0) - row1 = min(row + radius, out.shape[1] - 1) - col0 = max(col - radius, 0) - col1 = min(col + radius, out.shape[2] - 1) - - patch_x = mesh_x[row0 : row1 + 1, col0 : col1 + 1] - patch_y = mesh_y[row0 : row1 + 1, col0 : col1 + 1] - - kernels = np.exp(-((patch_x[..., None] - x_i) ** 2 + (patch_y[..., None] - y_i) ** 2) / denom) - kernels_sum = kernels.sum(axis=(0, 1), keepdims=True) - kernels /= np.where(kernels_sum == 0.0, 1.0, kernels_sum) - out[:, row0 : row1 + 1, col0 : col1 + 1] += np.moveaxis(kernels, -1, 0) + # Bin each point onto its nearest grid cell (unit mass each), dropping any + # whose nearest cell falls outside the padded grid. + col = np.rint((x - grid_x[0]) / dx).astype(np.intp) + row = np.rint((y - grid_y[0]) / dx).astype(np.intp) + inside = (col >= 0) & (col < grid_x.size) & (row >= 0) & (row < grid_y.size) + histogram = np.zeros((grid_y.size, grid_x.size), dtype=float) + np.add.at(histogram, (row[inside], col[inside]), 1.0) + out = np.stack([gaussian_filter(histogram, sigma=2.0 * float(b), mode="constant") for b in blur_values]) return grid_x, grid_y, out From f41aeb16abf4bf9bad6c8abe47273451a93ae4ea Mon Sep 17 00:00:00 2001 From: "selman.ozleyen" Date: Mon, 22 Jun 2026 11:47:11 +0200 Subject: [PATCH 120/131] apply suggestion --- src/squidpy/experimental/tl/_align/_io.py | 8 +++-- .../tl/test_align_by_landmarks.py | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index 9b38de411..6026cd151 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -126,7 +126,7 @@ def writeback_affine_sdata( coordinate system inherits the alignment. Nothing is materialised. """ from spatialdata import deepcopy as sd_deepcopy - from spatialdata.transformations import Affine, get_transformation, set_transformation + from spatialdata.transformations import Affine, Sequence, get_transformation, set_transformation if moving_cs is None or target_cs is None: raise ValueError("`cs_name_query` and `cs_name_ref` are required to register a transform on a SpatialData.") @@ -144,7 +144,11 @@ def writeback_affine_sdata( # element we register a transform on so `output_mode="copy"` leaves the input untouched. element = sd_deepcopy(element) getattr(out, etype)[name] = element - set_transformation(element, sd_affine, to_coordinate_system=target_cs) + # The fitted affine maps `moving_cs` coords into `target_cs`, not the element's + # intrinsic frame. Compose it after the element's existing intrinsic -> `moving_cs` + # transform so a non-identity placement into `moving_cs` is preserved. + existing = get_transformation(element, to_coordinate_system=moving_cs) + set_transformation(element, Sequence([existing, sd_affine]), to_coordinate_system=target_cs) touched = True if not touched: raise KeyError(f"No elements in the SpatialData are registered to coordinate system {moving_cs!r}.") diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index f179bff35..c7d7d657d 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -140,3 +140,34 @@ def test_spatialdata_registers_transformation() -> None: ) assert out is None assert "ref_cs" in get_transformation(sdata.points["pts"], get_all=True) + + +def test_spatialdata_composes_with_existing_transform() -> None: + # The fitted affine maps `query_cs` coords -> `ref_cs`. When the element already has a + # non-identity transform into `query_cs`, the registered `ref_cs` transform must compose + # the two so the element's *intrinsic* coords still land on the reference. + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Translation, get_transformation + + offset = np.array([100.0, 200.0]) + # `pts` lives in `query_cs` via a non-identity (translation) transform. + pts = PointsModel.parse(_QUERY, transformations={"query_cs": Translation(offset, axes=("x", "y"))}) + sdata = sd.SpatialData(points={"pts": pts}) + + # Landmarks are expressed in `query_cs`: the intrinsic coords shifted by `offset`. + align_by_landmarks( + _REF, + _QUERY + offset, + method="affine", + data=sdata, + cs_name_query="query_cs", + cs_name_ref="ref_cs", + output_mode="inplace", + ) + + matrix = get_transformation(sdata.points["pts"], to_coordinate_system="ref_cs").to_affine_matrix( + input_axes=("x", "y"), output_axes=("x", "y") + ) + mapped = _QUERY @ matrix[:2, :2].T + matrix[:2, 2] + np.testing.assert_allclose(mapped, _REF, atol=1e-6) From 5388c37f8c304338e5426768db86ce50e77438b6 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 15:37:34 +0200 Subject: [PATCH 121/131] wip stalign reference tests --- .github/workflows/test.yaml | 34 + hatch.toml | 9 +- pyproject.toml | 6 + .../stalign_reference/converged_n500.npz | Bin 0 -> 64095 bytes tests/_data/stalign_reference/energy.npz | Bin 0 -> 32217 bytes tests/_data/stalign_reference/gradients.npz | Bin 0 -> 53573 bytes tests/_data/stalign_reference/primitives.npz | Bin 0 -> 171843 bytes .../_data/stalign_reference/trajectory_n1.npz | Bin 0 -> 11137 bytes .../_data/stalign_reference/trajectory_n5.npz | Bin 0 -> 26751 bytes .../stalign_reference/trajectory_n50.npz | Bin 0 -> 26564 bytes .../methods/STALIGN_DIVERGENCES.md | 146 +++++ .../experimental/methods/_stalign_fixtures.py | 167 +++++ tests/experimental/methods/test_stalign.py | 82 ++- .../methods/test_stalign_reference.py | 601 ++++++++++++++++++ 14 files changed, 1042 insertions(+), 3 deletions(-) create mode 100644 tests/_data/stalign_reference/converged_n500.npz create mode 100644 tests/_data/stalign_reference/energy.npz create mode 100644 tests/_data/stalign_reference/gradients.npz create mode 100644 tests/_data/stalign_reference/primitives.npz create mode 100644 tests/_data/stalign_reference/trajectory_n1.npz create mode 100644 tests/_data/stalign_reference/trajectory_n5.npz create mode 100644 tests/_data/stalign_reference/trajectory_n50.npz create mode 100644 tests/experimental/methods/STALIGN_DIVERGENCES.md create mode 100644 tests/experimental/methods/_stalign_fixtures.py create mode 100644 tests/experimental/methods/test_stalign_reference.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a38c81478..b49fada8b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -155,6 +155,40 @@ jobs: DISPLAY: :42 run: uvx hatch run ${{ matrix.env.name }}:run -v --color=yes -n auto + # Numerical comparison against the vendored STalign reference (scverse/squidpy#1243). + # Scheduled runs only: it needs float64 and is far slower than the wiring tests. + # + # Deliberately confined to one cell. jax is only installed in the -jax env, and the + # tolerances are calibrated on Linux -- macOS Accelerate and Linux OpenBLAS reduce in + # different orders, which shows up over a few hundred gradient steps. + # + # HATCH_TEST_ARGS lands after the `-m "not reference"` in hatch.toml's run script, + # and pytest's -m is last-wins, so this selects exactly the reference tests. + - name: run STalign reference comparison + if: >- + github.event_name == 'schedule' + && matrix.os == 'ubuntu-latest' + && matrix.env.name == 'hatch-test.py3.13-stable-jax' + env: + HATCH_TEST_ARGS: " -m reference" + JAX_ENABLE_X64: "1" + JAX_PLATFORMS: cpu + MKL_NUM_THREADS: "1" + OMP_NUM_THREADS: "1" + run: >- + uvx hatch run ${{ matrix.env.name }}:run -v --color=yes + --junitxml=stalign-reference.xml + + # Every comparison records its measured relative error via record_property, so this + # artifact is a running log of how far the port has drifted -- including on green runs. + - name: Archive STalign reference measurements + if: always() && github.event_name == 'schedule' && matrix.env.name == 'hatch-test.py3.13-stable-jax' + uses: actions/upload-artifact@v4 + with: + name: stalign_reference_${{ runner.os }} + path: ${{ github.workspace }}/stalign-reference.xml + if-no-files-found: ignore + - name: Archive figures generated during testing if: always() uses: actions/upload-artifact@v4 diff --git a/hatch.toml b/hatch.toml index acd8e0a6a..82defb133 100644 --- a/hatch.toml +++ b/hatch.toml @@ -26,8 +26,13 @@ overrides.matrix.extras.features = [ { value = "jax", if = ["jax"] }, ] # default commands (only `cov-report` is overridden) -scripts.run = "pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}" -scripts.run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}" +# +# The `-m "not reference"` is the actual gate on the STalign reference suite: a CLI flag +# works on every pytest, whereas pyproject's `[tool.pytest]` table is only read by +# pytest >= 9. HATCH_TEST_ARGS is interpolated after it and pytest's `-m` is last-wins, +# so the scheduled job re-enables the suite with HATCH_TEST_ARGS=' -m reference'. +scripts.run = 'pytest -m "not reference"{env:HATCH_TEST_ARGS:} -p no:cov {args}' +scripts.run-cov = 'coverage run -m pytest -m "not reference"{env:HATCH_TEST_ARGS:} -p no:cov {args}' scripts.cov-combine = ["coverage combine"] scripts.cov-report = [ "coverage report", diff --git a/pyproject.toml b/pyproject.toml index ea01bb7a5..387bbc31b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -278,9 +278,15 @@ python_files = [ "test_*.py" ] testpaths = [ "tests/" ] addopts = [ "--ignore=docs", + # Local convenience only. The real exclusion is the `-m "not reference"` baked into + # hatch.toml's run/run-cov, because that is a CLI flag and so survives any pytest that + # does not read this table. Keeping both means a bare `pytest` is also quiet. + "-m", + "not reference", ] markers = [ "internet: tests that require internet", + "reference: numerical comparison against the vendored STalign reference (slow, needs JAX_ENABLE_X64=1, scheduled runs only)", # the below markers are just there because we import from anndata.tests.helpers, # which means pytest sees them despite us not using them. "gpu: tests that require GPU", diff --git a/tests/_data/stalign_reference/converged_n500.npz b/tests/_data/stalign_reference/converged_n500.npz new file mode 100644 index 0000000000000000000000000000000000000000..f1983b3566ac18250573fa9e83c49f7cb9e156ea GIT binary patch literal 64095 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 literal 0 HcmV?d00001 diff --git a/tests/_data/stalign_reference/energy.npz b/tests/_data/stalign_reference/energy.npz new file mode 100644 index 0000000000000000000000000000000000000000..53bbbf7a9984fd8d74da0d514a1bf467029b1beb GIT binary patch literal 32217 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 literal 0 HcmV?d00001 diff --git a/tests/_data/stalign_reference/primitives.npz b/tests/_data/stalign_reference/primitives.npz new file mode 100644 index 0000000000000000000000000000000000000000..cd8ef0bcb18185a707e43f171f0c53983902724c GIT binary patch literal 171843 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 literal 0 HcmV?d00001 diff --git a/tests/_data/stalign_reference/trajectory_n1.npz b/tests/_data/stalign_reference/trajectory_n1.npz new file mode 100644 index 0000000000000000000000000000000000000000..23d393cbe3721a22428dd8d7eea640d8de7d1c82 GIT binary patch literal 11137 zcmc(lWl)^Uy0&q5f;$8a?oMz51a}(o5ce?hqu%;0}ZPzzJ)u z>^jLl=f_vOPJK^RSJzC<{l0hK{q+3lOHBzLfei)*2Ic3VfdT1i%s%{6crZ_3000+P zXHQEfGbeLP0D#@e#RnD!2Zs0Y$3II7?(excbPH)ub#4L`iN$HI;_pkXLw)IM|GcSjDA~zj9}KN`rDk zT=?th*0zevc9~C~QRLO`fOLR^GQfk<@5EN5+|Z$ZMmN;?Z2WSjhoVR+7Q_q{PRk=R zw(~7IQNpi%?s=~KvK{H%46>2yZHQk~8Dv+^6W=fIc53Z8G{ig8Y(D}_zrOv15^KDj z*~+~UfWXlsE}f45ETqwOI73wXG9(ktZn1ufsAbb=_L3cxYVWE465QfHQ`KB5k>DN3 zfV^4l$56F#HRqM%VL15F&r3CrCAl1Op4P_lik|IQV`e{v`Shr@57OT3*FvWS@>IRt z5BIk&kw^8of{MBg2ab*pKSyg@fSjaH9Xt&3vw095*{#W+LqZPig7VP(i-)?FMCzeW zub7%}?zng^9+G`Q63K>W@|Qz=HJH`BB@$r^>_Tr(fZJ;i zTMHK-0OzkB6omb|_Xhu-V6yg!cFA!1`_y{WDsz(zvW-4C73Vu8TNP9l<>#gWv@}xQ z6XgL?lF;zHl!lm95Z@A6KPCJCcTFnSBb+IWR*$w0Uxk;mO&79+kKGnMJ6+vWK_wM+ z{@kWk1%+)IsQ|=B(e#B&ek{OIEPSLge5~agGT~FO)^2O6Pshp?gKkEpIx=wwk`k4m z=}PNfwQiGiCwYBq#3vFgq`+kG0SZg#STYOsS+Owkn~mHJuAG-RzLw9z#phrP0G`q| zBY{!5gGv=MZ*$Q|x#0^$6- z!DCXDI5(PWA?PGTneA+q5n~@Wa182?IznU6^}x9*V#{A<6K|)_^5HSQNpQRbnC`ex zTY-tb$9hL00IvX23_@5)ljrtAMT*pC6)aICrKJ$_e#hW3;4qG_BU!lkJZb^>LNMEB zmjc|)4=bgbwf$!hP;~W$UqK-L>V?td{^|t__^Pr=qIkVLf9KKk zg9A?M+XCn!enZ`t=-e+Hidz0W=j$SVf9zAw4NLrgx2N^X-n)NnY5(W;{xbIjhW)c$ zwQkGIOA5uO-^@$CZHj&Q5gF`gXQ6sUHtdB}4X={&8*EfhIl+|3{2ApIB>uS%2JR$^T2!?2ffl z23g6F11h5+PtThR>D!m8*)Po6>~_aE_l^SP#_=x#;b^={vi+?=t|vunGlL#joTi9W z`>?;CQ7@(Rf1S}s)1JRfn*#ohIGF11`C#r+gvewT*~RdMdIJ(mKMINZ1Qb=Bc|z?X zP5rHfj$BybkfK7;=ft6**jsZ5@N&s-)Z6zyd~0UwCVNjkiUZ_%)1?;bAlHJ&7p) zwSYJ?dpFw!$ao*HMYI|?0u6F<-%F;57#HbpIv%s9VQzYYkbEtLR^+NKHqWn=R7hC$ z>@`w#8X-*>$T}weH63}j7?tyR6&$>3yj)ebZ3N_E(Q&{mRQtCjj=zXPm=Qn>^6e?Vxg9;gZZ)O!i z?)1yBH%O65CKndlz*+e~kqyQL!#dU{Pn2ML^3dKOQ$7U4aok`U&db=4ZyD**%E2v` zB5S-APwW^U${`k`!k+vH4kRT2BFtm&5VY`;94WnX_Gt8Dq7)(g+)yuzfEA%}YK^W> zoXb*LoNoj##LI1aK7z>9m!5%h+6X&E!o39$=a2?A*V50|R31Hz0=F?$BbDlGYfpEH ze6+=<%q+k2H)OIWa9+qr9lIgLKM)hGSbez& zUKGz`e~Pi|4N4SPzNSm6W=#~Mj*^HIZ_zCn3L|EVZ1x zG0ot|P<#G{8X(}evJ)vrIgsw(!;yl#_z9j?l}W<1`6Mt(`iacDrBn0Eh)#&$on^mn zMgS+dxF$Sm$ki&JJ=SwdcO zT2bx?(EW&FrE%Wwj_NU;h=Vj3*kBKQ)C44GQXwY4{W_VxD3WMODwtanL>#}f6p(8* z|5mf4^*ufW>$y@9)?8yM(plYhwJGUg$g)1W3VOU<_K)GEd&zo=Gd|fVw5lg`8)?vC zDx{a36(s)LMoid{g+wLxAT4D4yo}v#+Q~15<4s?UL3q1DnBP4Sl3y>xG8|2uaxV{* z-iwlFgeRM|Wa@4VEKlVJfQ|3srrFTbu_ASzdrNhF6{!dsD>hwQ2ml)BEwK>_8|sBV zd|%R4PH;rTHU19v@hU%lZR-T{AVp+Bb^1~7rjf*8(3XLv0D5F0cQ|aFq2th8f+Nw% zi6K!&mDWVD(`i25MkON=A2MK?|FM5ioHr&Tp88C=GtNBEq$v%9nr>cZXQhMW2v5|o z1Yb1Voq_7&HYYbsI^iL5VSu8?1WWGtX2y-P(QcKyML4#^RVD7_gqx{3u^BBBcPtU` z^1S~+0DdcJl)FrvMCP7c4Dbr_)F{wxH*H*nO7U3h$Nd*FcsVDw9<_RQi!R+=2pCTOn1}m^(FYiCsM6* zp5%^%U3`TdVD8}>B3D+&Xl+2@k_|DE=Vo*P>Evq^LhzI)61Ym5RaaUP)UY`wxrbg@ ze5r9$rgI)G0%!UtEqfkMCnD>X97z+zaYwSk7403jL**IAhN4~K3e6}N$O}os3FSHO z(i$8lGxVc@EK(UPT+!j1s3R%8_{&u6pBHn$Vdk2-qFahl1^a_lh4Lho@8<$Gdb;uh zU9JlQg@MhK1cx4^PK(>2bzVgk3ls#zXDGyArut6FEDpeIIJn7qkxKd+2amBCMtki{2Wrjohv=-KzDf^3Rm|3+;5&6f{8W3d8#S;y2L1uK zbJ3!@Vjgq>bXmD4x2UKa3nCPB8MUN;Y#FOyAA0}En|xQW@ZwwPIUQy_$0Gfp{x<#R zQWOW0o~s1K*ZUyCj{X#|jgkx8C?;SiovFrS`2BM#v@|qAhU6sx1hZsFwtXkKBW!+Ce?j;iz1`J(s(;>-~pvN~QgF)+eQ>7rXT z!+wE~C}$)S;hos&u2*E z?{R{1+NHV?*RS~TonKxMQJQS;Yg`2sK_0nddr<3&}` zwrZ!`=-++&VS!BCSEo~Er_0$b;&&D~2blGvWf^s{K$R#T67vaigIAFQ=BAtLbr6w9 z1vEi*^Ot$8FU%5b9k`OyAsEIm#g$_cbEkAR1)W6AX}p+u?61l1zBE$R_urXBQ<5O2 z?ejQi2mCma7**{BpPh%jUKtOACZ5|26?tkLm=MWm@Wtut>YBdpH#45ccRZRl`t|^7 zEhZi%qaz%eM2lMxEh^Em^sd|;hpr|cbdX+U8(rJ;ZMLnxY8_sH=ZF`d4LY^rnE(X1 zc1w7oe7N|crt)ym*?U$=3D{)}4#G~4@2;2hFXnN17w1KZ#Viwjaz`Oe?9j|Qhb5wG9H|>h4@9HZCYC)|La1EWp5{H^N72-85CR|{dmY5RM zx8&;xliwgCi#hh*T&5p&q^!scWWuLY?hn#wEr;$$RrbD^7rUPvFOzc*rkyc+vxOLG zuWdYwUu$w1WY|{+vwACA)S?Vx51a6|U^iAqofse{ zUaLr>8oM7pC)2=~jyPBi1l;1y#OV-cu}XL))*{forM++1YUsd75E4r^AsH|*Hcx!v z6m_4~K0{^yz0mGReB2Km_-@&Ybz9#K{rMIDE)nQR-`3o6GEq?6_0gqf6%k}_m-g?6cgyq)Cy{VYeFbL&y zY`?kk8`hW|;^ORcP?9cACR_wjlE=65!Gq(N;)a6aSB(bdjUn3)K>0|1JkPY+_pXCe zWa3rQCnn_ri?K?5+avY{%XRRGR3}auA1Vq4aty1Bz)UDXcNGcXbZAU>>5^3X>_^n9 z-b@J@#CNzLMM1ra%`uz>4be~Oe*JHpxxK@0yKPZ-+qmGXmPYb%HR%@b_FzHk{xhJVL%xjv%H4 zaZ55THX)2hCUYDj#1%|0%|~G^WJJ0|b&#uXP%hwpF3EUJfsnx@9Mjw;JoUCfpK9F! znJOkGOU&Zh#&t73d|!h??a&^%JsdoX_mZBv4}i$NaGrOCrulmKo=xA{mF>&hN~NH8 z1M-QJAP`j94vBm2h|qb>ZmL;Nk>0C{$cZf8+3G`MzM%wU9`Vxb$aXNF&u=kdJe^si zEqCNO$86b0%-DjjbK$u#Tl6TF#M@K~S<)p@7}*|?WJm4yK8-DF+^(}IrDIF9bjlB9 z<@N519Sg;zhMED2Qk}fW0B`4#WpI;Q+A3=@y*F>kJPLAkWH7A!grA1l#!uD3<3512;kN#dQW8MtCvBI+1HBI>oyHy zwKTgA;Pzx#u4#kUJiLlc&lF6$ZGbSZfM>RKm>T1?8Y7V6=VI&Vhv)ADHR9Pvxo;1K zT9?J^Z@10It@aNCgiekj)Z`R}R{Poqy};UTNxmH0v6=EWYlP&zpWZnM_S7KI8;(pnQ8e zhU4(9jQ}~L?@f|V;oh2XO8CBlQq^bq#v{Lop~9&M3PDm&rIX1^de)Chb#8|yuuFTM zEX}<5eZmgwa|c40Ls9j-Gc7-kt3s22b~+r{R|8JGL|4e=*@rW(ZET3h3382Os~?{e z*zmjFPfA<((Daut$7VhS2=6PZxX&%U6YWrv<%CEG(E;u_K4X6x9(nr?-(8=_ zTug7~2oB_R^L7-8DafCB(cd3yd~YyK#ueCP z9Z}Ez1k(%^s}2OCKd;?xJ|dMRZF#02`MKRLSgrIZOw9hf0>mZYZqEpxvog!BbYN;t zZa*?5Wdsz|(}p~}=a;IN^ueH?L>*~^5k%rMU`dTRnIAE`g}^D;pRz%pi!>6k=;uE5 zV5;-3p-EJiE}Fyki5O3n>`KXnweS-$wX1K1%&4tg?D^qJcOAqSd9=heA7b;DLdN%e zQcjP$%f;$;23S4QtulQUj6#f+9Yg7ol&<969D)=k7+B>N%xpP~y7o)oZ1fBCqe%B8 zBk9x|mgVGdWx6JYy40OG{=8Db5--Ii@`p_c{~&d7r-8~{m^K`LpDv3gb*ynt2sw2; z)A0t+_@-qqUlc@5@Yqu%fV}Q~7d_FZhIkL0tvO5^+NsJJ*}t~j^+xe=L{)>AtZwEO zh_$?q$bDQt!AXmcC?EClg|;``d}-0g^)KaM8njT5JzNA|U7yb9*UL#wR#u^U>>r}e zJ6v?ou=OIQ?x+lDT8$Vc9KecyJf)=K5vi{DBs5&yMzQW(PB>drw>%~S@@m}N3t;a{DpkSZL$$Bnkan1jJ zqKF|X({o)=qtD^Q6{Tj%4bkpfw316iiTrP^u?e zKJZXPKl8h`O&)8&y^}vlCDYBP!tDmrwQgKhfdY>OPn4!%xkh-2t71F?6~|QpDhN>v zV;_oh#HrC|CFj-grBQPbfhTo(s`-WY4yg(|skM-$b7xOgrc+U-Y#R@5Wwo;#Nt52i z;K*gHc?FXB0eYUTps20X$m)13&?PWhpi#C01g_yMx|S?zB?r zu3qYwH%+98t{6qmUI{xnSz*g{nHO(YN!o~97dE<9X`d4J*~kO+p37qK!3P{_qg+mC znr0Yv^05uZ08*ySsM%A6mjTcQb)VhjwIg8*O7@)Pa|5|dl1KwgFrUKEZO)=$V{mn0 z)IIMByY%L+;$p$Z3*|!oTl_^)bdCdYBj`l|vi!}3AWtn;=zajjQvWuftePM%NTBwK zVmZhl8|)O`8abC9o}~2detlfbl^fo)E`Zi|)tE?p2Df>lhzG7pET<;CHtfCgjEz2* z5L~){{*=1Pr%rGwfYj_6o|eR&D7%p844m3vR1H`FIAfUgNTC`c)lAI&k0iltXq z9PHfLG#Mz=*7_;Gn1QviDm^+cJWHDVx-fXbVSd1rcLQMJc_CUoBE#`oUb;;k(fNUA zlzqKSUNS}t_aPPQsD*SdFMlZlxqAyBTZt`Y>xOo@(IWX_ws19}i3hgiwWeFGb?-5I zb4X`Envm&hCo_YC-+m%<_LUCOXuEUvI1T(_c>*a-gC)ELZ z8)psiuhgPZh+mkrp@FbEbqICjB8)EVVWP?f&PchFxhzQwJi*dLo3kncnDCZ+E7?q& zEJL+cNA2traJ4(n0O|WDG@OMRL@n7X4S~FuOkPAs_Dul_e9q7FTxWQMC8b^FX`7=G zF2Jp;ZMpXwP$rkN&5qMhNo6s!(>&leCWNTQ=}b|o&g_U@r|hY&Bln2?m{%h(lu_V> z3fbGQ0F|{B)Er7J!Cg)+pC*bj!o@gfZzkvIi?Fj^IzF`l!Bd~$5)^drT%FvY6gUTv z>ZZM2c;`lxs_RqAr_0yfTBQahuf}QvnDL$TLzbE`>?)FmJA9v&bN&D!Zh2uE+i~n5 zN`b)>hk3xh3kBfzb~nz`y=*S`eBw?C$ZC2l7LL;#GYZEJPWwf%S&6t=e+x#X1tpJqIuHpV+?hctX~JofE%$ z=GOH^=4^VreJnPD745TsZ%c=asJEP<5R$g$+9(4hM}%wx(sx4|zOjsNhbCi*@$Kot zfLZwqm@e@hgc;qJ-WOK*^OR7Uvi>bbzn+O0@lSV$I_z|6UKwEMfDOGo4(JyXuN}{Lq8r|)hQNN&gowL{Lg_O zk>5REfz%0Mr9!1$q8la*tnvxqi8X&1j$DLRY0V{_E20M0YXDQK?jQ>QXRlXBZ;@)t z_*UF3NI0dPB%iSwU^fbuM2rbyznR1LCABy1cu8Sp(KHdvD5upeb%+t%C%q^^)++Kk z@U_=XMuDWaoP;&>Mv7xe*nRFYVTW3Qz@jI|j_(|#4imL7o0e33vrM|3()tJ(Oig@xjdqJBth zePLX1qY)k0&&cbg<&i6zj>!lJEK^t<>DmEae3f**uOwR?79!SoQ>p{uqVkzSfQwa> z$As_~njpqh&zxepaE$p4%FabzBVV-LUl$SyHy%dLmU+BVx5{@+bChU13MjwiZ9$*O z3K)4>i}CzHI}1-eM#Y~5To?GGjH-m83}GtVpjz2`*|!5Rv`#&h zIBba?M-i$F?R#SUtbvtivBdnPfXzE3;0X%5ITXR=iuY#b@C93TO`$JOepUqNy=GTa z=a1*HGcT^Y?f7&Qbww@xf57wkjj{r89pq3CncngZilbc3J`^srVCue_is=Hmz(cmrPF~PNS`HX_K%une2z9Ga9VOMX7BoD8uJAFrSa*+XY+8I@gisu#7dW zE~Qf!Ycw4;^|fjw$8A0VUECO!bcS%BAL;=#v!7LM?!Cd3u{{(9Ri+0mi~4*wSRG`= zXvou`YzH7E*q@+3A zVT|W1`fN|FVl7*p9!h}dcz{Yv;+O{NTY|yyt9_^URC8c&k7CqsI*tVQf!lBbE1Ew2 zD(ADE&FfS_pD0=?KlG0xrWIC|4rb`i#f^P;744Z5Bx!A{Y(!KZp!v}ALiQsUZeVQ# zkXhgXlQB)#fdxD5>pa3m*A#)e0%08uwUR?{)MYv}To?U*Q3SBJB=xL~S*WKc(I~>C z5W;HtVcu*n?%Q}f^xRue)6ciA0h6WJuxO$kTJF{CxRp#{Xu9Flt^>IiUNC)o>dxX2 z>?~_Q5OYtUph54O2q(W}qWR-*hofF-n<&Z-RlgQ=n$?G@m-p9pnUs=%6?1-y9f z!jsKh2+XIi{ZtPd)8@WZc`6~^$aSLe1)~~el8smy-#_f@aK<=DY-k$AzA{IbgcOpR zfeJ2WMtyDdj-;ow*Zm0g6B>Za+PjDwMMYS2fM`>>%@f9ATCzSD&6A-nl;}7!uW>vM zcr)>gSdPK*vR=8N7BfS1{77{%7Hc=P{ghc)0K^wZOkLD1uN22mx}m{VYFAQqpr79| zA4Om|)Y;{9Mp9!Tzi2;Q>W+BD(QVm+UhO>kuJ`uI@lzswtQZg6aCM{zK*Dugcc};& z0*JM#RZ-&IsW410D`PT204;T8OeJR{Kj~Dx8xQ?iQKH$_Vb>xydVWN9QR$5-;XV7l zHT@@@;7Z?+4PiH$s>5+e$yx<+L}<1fn!`rlDh1h9c?;?lq+?R%p7iWKK$h1YNFb;& zcwR2It2S~tjLP#JYb?jW3~i{q7un2g)t5u$YjdIq0YbOywbuz6@`?R&p4*DGDIkt{UrgnbT)C4rj6=s|X7=(lQ6gq=9eZn&tlaD940w6vDoA19cDJ>O{_ z(|w!Bv{unZK$C^YMK_l&1zk|T%KqYvd&|M3W^LhDdPc!MVi!9uFA^NOeCyHM%u9VO z+Q0QeT=m2!X*AhCcm4Ef9^?P#ukIhhMjx<$zqSv4`uycD)&S|}wY~1kU-$M+On=|k z`~O2o9Q1Kvg+6~GJ6`QU0J?pzED%SGCn6FThe(jofrAcj)iX=1^A$<|Z66+Mf532m z>)M8Adm7m(Kb51@ry@$37|+i-bq8zDZmM?8@dKgjRLnzSi%^~+3Abi~El(Y!Yd6DM z2VvTOuhknU-0z;7y3JL7^ZYMB;+N-NRL3vR>NdvTAmR*@FX6kxM^wzs%`6$XyF-IB zxcGat_4-w2DZU^ox&hoy4lljMv)OG=6qgKcz0nQc9%dTF*-S=RyDZjY*8^^r>c>aL zmKM84C2AvZ?HlF9Yh1Hd8XYQdaR052A9DVnI#76v27hxc^=H!mZ>~pXG5?QT!@{w_ z|IR}`QjU+rBi=u~@Hhqk1zi0*9jT`DyZ(`6dnD8zX*HbR^pQWG`X|@+r}`27dPKS& z@vh(1$?(6a{{q4OR6p`kk4RMh&w==J{HkEsYy_CU>8U>@j||fzru2v`{Voyt`SCBJ z=}*ZcYV^2wk4zEz&+)5*VS9f{{u_Aor|=Q<`6c}QU7<)ng^&I5r{oc{c_e8bshZy< z0?06bLpOg)9*_9rA$~j@KOg(A3WhyofcfXZ|EYOAVvo-LT~o;TQ}aJsdu;IkS)=#! a&ANZJyP6W>Ul!1R24(zn`>~iG?fe&NP=jm$ literal 0 HcmV?d00001 diff --git a/tests/_data/stalign_reference/trajectory_n5.npz b/tests/_data/stalign_reference/trajectory_n5.npz new file mode 100644 index 0000000000000000000000000000000000000000..750ed2a325cd25442592e179c69a7e32140fed8a GIT binary patch literal 26751 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%0D#}VdOc(OVu{<4F3u!bwR$=b3v1pwEk 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 None: + self.ref = ref + self.query = query + self.landmarks_ref = landmarks_ref + self.landmarks_query = landmarks_query + + # `torch.tensor` rejects arrays with negative strides, which a bare `[:, ::-1]` + # produces, so every row-col view is materialised contiguous. + + @property + def ref_rc(self) -> np.ndarray: + """Reference cloud in row-col order, which is what upstream works in.""" + return np.ascontiguousarray(self.ref[:, ::-1]) + + @property + def query_rc(self) -> np.ndarray: + """Query cloud in row-col order.""" + return np.ascontiguousarray(self.query[:, ::-1]) + + @property + def landmarks_ref_rc(self) -> np.ndarray: + """Reference landmarks in row-col order.""" + return np.ascontiguousarray(self.landmarks_ref[:, ::-1]) + + @property + def landmarks_query_rc(self) -> np.ndarray: + """Query landmarks in row-col order.""" + return np.ascontiguousarray(self.landmarks_query[:, ::-1]) + + +def rotation(theta: float) -> np.ndarray: + """A 2x2 rotation matrix acting on ``(x, y)`` column vectors.""" + c, s = np.cos(theta), np.sin(theta) + return np.array([[c, -s], [s, c]], dtype=float) + + +def make_clouds() -> Clouds: + """Build the deterministic reference/query pair. + + The reference is three structures -- an anisotropic Gaussian lobe, a smaller offset + lobe, and an annulus -- so the rasterised density has real structure rather than one + featureless blob, and the registration has something to lock onto. The query is that + cloud rotated, shifted, jittered and randomly thinned. + """ + rng = np.random.default_rng(SEED) + + n_lobe, n_small = 400, 200 + n_ring = N_POINTS - n_lobe - n_small + + lobe = rng.normal(size=(n_lobe, 2)) @ np.diag([150.0, 100.0]) @ rotation(0.4).T + small = rng.normal(size=(n_small, 2)) @ np.diag([80.0, 80.0]) + np.array([400.0, 250.0]) + + angle = rng.uniform(0.0, 2.0 * np.pi, size=n_ring) + radius = 300.0 + rng.normal(scale=25.0, size=n_ring) + ring = np.stack([radius * np.cos(angle), radius * np.sin(angle)], axis=1) + np.array([100.0, -200.0]) + + ref = np.concatenate([lobe, small, ring], axis=0) + + rms_radius = float(np.sqrt(np.mean(np.sum((ref - ref.mean(0)) ** 2, axis=1)))) + moved = ref @ rotation(THETA).T + np.asarray(SHIFT) + jittered = moved + rng.normal(scale=JITTER_FRAC * rms_radius, size=moved.shape) + + keep = rng.permutation(N_POINTS)[: int(round(N_POINTS * (1.0 - DROPOUT_FRAC)))] + keep.sort() + query = jittered[keep] + + # Landmarks are spread across the whole cloud rather than clustered, so the affine + # they induce is well conditioned. + lm_idx = keep[np.linspace(0, keep.size - 1, N_LANDMARKS).round().astype(int)] + landmarks_ref = ref[lm_idx] + landmarks_query = jittered[lm_idx] + + return Clouds( + ref=np.ascontiguousarray(ref, dtype=float), + query=np.ascontiguousarray(query, dtype=float), + landmarks_ref=np.ascontiguousarray(landmarks_ref, dtype=float), + landmarks_query=np.ascontiguousarray(landmarks_query, dtype=float), + ) + + +def checksum() -> str: + """SHA-256 of this file, so both copies can prove they are the same file.""" + return hashlib.sha256(Path(__file__).read_bytes()).hexdigest() diff --git a/tests/experimental/methods/test_stalign.py b/tests/experimental/methods/test_stalign.py index 0261f4a51..66d4f6c26 100644 --- a/tests/experimental/methods/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -1,7 +1,12 @@ """Integration tests for the ported STalign estimator. -Tiny synthetic fixtures with ``niter=1`` keep these fast; they verify wiring +Tiny synthetic fixtures with ``niter=1`` keep most of these fast; they verify wiring and shapes (dispatch -> JAX LDDMM -> StalignResult), not solver quality. + +The two tests at the bottom do check solver quality, cheaply. They exist because the +full numerical comparison against the original STalign lives in +``test_stalign_reference.py``, which is deselected by default and only runs on the +scheduled job -- leaving a fortnight in which a real regression could ship green. """ from __future__ import annotations @@ -98,3 +103,78 @@ def test_stalign_rejects_unknown_kwarg() -> None: ref, query = _points_xy(), _points_xy() with pytest.raises(TypeError, match="unexpected keyword argument"): fit_stalign(ref, query, not_a_real_param=1.0, **_TINY) + + +def test_default_dtype_is_unchanged() -> None: + """Guards the reference suite's blast radius. + + ``test_stalign_reference.py`` needs float64, but ``jax.config.update`` is + process-global and every xdist worker imports every module in this directory. If + that suite ever starts enabling x64 itself instead of reading ``JAX_ENABLE_X64`` + from the environment, these tests would silently stop testing float32 -- and this + assertion is what would catch it. + """ + import jax + import jax.numpy as jnp + + from squidpy.experimental.methods.align_samples._stalign_impl._core import jax_dtype + + expected = jnp.float64 if jax.config.jax_enable_x64 else jnp.float32 + assert jax_dtype() == expected + + +# --- solver quality, cheap enough to run on every PR ---------------------------------- + +# A cloud with enough structure to be alignable, and a query that is a known rigid +# transform of it. Small enough that a few hundred iterations run in about a second. +_SOLVE = {"dx": 8.0, "blur": 1.0, "a": 40.0, "nt": 3, "epV": 5e2} + + +def _blobs(seed: int = 0) -> np.ndarray: + rng = np.random.default_rng(seed) + return np.concatenate( + [ + rng.normal(scale=(18.0, 11.0), size=(120, 2)), + rng.normal(scale=8.0, size=(60, 2)) + np.array([55.0, 34.0]), + ] + ) + + +def test_lddmm_energy_decreases() -> None: + """More iterations must buy a lower objective. Catches a broken gradient or step.""" + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + from squidpy.experimental.methods.align_samples._stalign_impl._helpers import rasterize_cloud + + ref = _blobs() + query = ref @ np.array([[np.cos(0.12), -np.sin(0.12)], [np.sin(0.12), np.cos(0.12)]]).T + np.array([6.0, -4.0]) + + source = rasterize_cloud(query[:, ::-1], dx=_SOLVE["dx"], blur=_SOLVE["blur"], expand=1.1) + target = rasterize_cloud(ref[:, ::-1], dx=_SOLVE["dx"], blur=_SOLVE["blur"], expand=1.1) + solve = {k: v for k, v in _SOLVE.items() if k not in {"dx", "blur"}} + + common = {"L": np.eye(2), "T": np.zeros(2), **solve} + first = lddmm(*source, *target, niter=1, **common)["E"] + later = lddmm(*source, *target, niter=60, **common)["E"] + + assert float(later) < float(first), f"energy did not decrease: {float(first)} -> {float(later)}" + + +def test_lddmm_recovers_a_known_rigid_transform() -> None: + """The query is a known rotation+translation of the reference; alignment must undo it. + + Asserts on the median rather than the mean: rasterisation at ``dx=8`` quantises the + clouds, so a handful of points on the sparse outskirts stay poorly matched however + good the fit is. + """ + ref = _blobs() + angle = 0.12 + rotation = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) + query = ref @ rotation.T + np.array([6.0, -4.0]) + + result = fit_stalign(ref, query, niter=250, **_SOLVE) + residual = np.linalg.norm(np.asarray(result.aligned_points) - ref, axis=1) + + before = np.median(np.linalg.norm(query - ref, axis=1)) + after = float(np.median(residual)) + assert after < before / 2.0, f"alignment barely improved: median {before:.2f} -> {after:.2f}" + assert after < _SOLVE["dx"], f"median residual {after:.2f} exceeds one grid cell ({_SOLVE['dx']})" diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py new file mode 100644 index 000000000..45a45ba6f --- /dev/null +++ b/tests/experimental/methods/test_stalign_reference.py @@ -0,0 +1,601 @@ +"""Numerical comparison of the STalign port against the original implementation. + +Implements scverse/squidpy#1243. The reference values in ``tests/_data/stalign_reference`` +are produced out of band by https://github.com/scverse/squidpy-ports, which vendors the +PyTorch STalign at a pinned commit -- so torch never becomes a squidpy dependency. + +These are **excluded from normal runs**. See ``STALIGN_DIVERGENCES.md`` next to this file +for what each comparison found and why the known-divergent ones are pinned rather than +fixed here. + +Run them with:: + + JAX_ENABLE_X64=1 pytest tests/experimental/methods/test_stalign_reference.py -m reference +""" + +from __future__ import annotations + +import json +import re +from pathlib import Path + +import numpy as np +import pytest + +jax = pytest.importorskip("jax") + +import jax.numpy as jnp # noqa: E402 + +from squidpy.experimental.methods.align_samples._stalign_impl import _core, _helpers # noqa: E402 + +from . import _stalign_fixtures as F # noqa: E402 + +pytestmark = pytest.mark.reference + +DATA = Path(__file__).parent.parent.parent / "_data" / "stalign_reference" +LEDGER = Path(__file__).parent / "STALIGN_DIVERGENCES.md" + +# Tolerances are calibrated from the measured gaps recorded in STALIGN_DIVERGENCES.md, +# not guessed. Everything the port reproduces faithfully lands at 1e-15 or better, so a +# 1e-12 budget leaves three orders of headroom while still being three orders tighter +# than any divergence we are pinning. +EXACT = 1e-12 + + +def _skip_without_x64() -> None: + """float64 is a precondition, and it cannot be turned on from inside a test. + + ``jax.config.update`` is process-global; under ``-n auto`` every worker imports every + test module, so setting it here would silently flip the float32 tests in + ``test_stalign.py`` to float64 in the same worker. It has to come from the + environment. + """ + if not jax.config.jax_enable_x64: + pytest.skip( + "STalign reference comparison needs float64 (upstream is double throughout). " + "Set JAX_ENABLE_X64=1 in the environment -- not via jax.config.update, which " + "would corrupt the float32 tests in this directory." + ) + + +def _load(name: str) -> np.lib.npyio.NpzFile: + path = DATA / f"{name}.npz" + if not path.is_file(): + pytest.skip(f"reference bundle missing at {path}; regenerate it with scverse/squidpy-ports") + return np.load(path) + + +def rel(actual, expected) -> float: + """Relative L2 error, the single measure used throughout.""" + actual, expected = np.asarray(actual, float), np.asarray(expected, float) + denominator = np.linalg.norm(expected) + return float(np.linalg.norm(actual - expected) / max(denominator, np.finfo(float).tiny)) + + +@pytest.fixture(scope="module", autouse=True) +def _x64(): + _skip_without_x64() + + +@pytest.fixture(scope="module") +def primitives(): + return _load("primitives") + + +@pytest.fixture(scope="module") +def clouds(): + return F.make_clouds() + + +@pytest.fixture(scope="module") +def source_grid(primitives): + """Upstream's rasterised reference image and its axes, as ``((y, x), image)``.""" + return (jnp.asarray(primitives["raster_ref_y"]), jnp.asarray(primitives["raster_ref_x"])), jnp.asarray( + primitives["raster_ref"] + ) + + +@pytest.fixture(scope="module") +def target_grid(primitives): + """Upstream's rasterised query image and its axes.""" + return (jnp.asarray(primitives["raster_query_y"]), jnp.asarray(primitives["raster_query_x"])), jnp.asarray( + primitives["raster_query"] + ) + + +@pytest.fixture(scope="module") +def velocity_grid(primitives): + """Upstream's own velocity grid, which ``_lddmm_loss`` accepts directly.""" + return jnp.asarray(primitives["xv_upstream_0"]), jnp.asarray(primitives["xv_upstream_1"]) + + +# -------------------------------------------------------------------------------------- +# Provenance -- the fixtures have to stay falsifiable +# -------------------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + "name", + ["primitives", "energy", "gradients", "trajectory_n1", "trajectory_n5", "trajectory_n50", "converged_n500"], +) +def test_every_fixture_carries_provenance(name): + """Without this the .npz files are unfalsifiable magic numbers within a year.""" + payload = json.loads(str(_load(name)["__provenance__"])) + assert payload["upstream_sha"] == "b2068edc98974efa54537eca194736e177bbe11d" + for key in ("ports_commit", "torch", "numpy", "python", "platform", "generated_utc"): + assert payload[key], f"{name}: empty provenance field {key!r}" + + +def test_fixture_definitions_have_not_drifted(): + """The generator and these tests must build inputs from the same file, byte for byte. + + ``_stalign_fixtures.py`` is vendored from squidpy-ports. If someone edits one copy + without regenerating, every comparison below silently compares different inputs. + """ + recorded = json.loads(str(_load("primitives")["__provenance__"]))["fixtures_checksum"] + assert F.checksum() == recorded, ( + "_stalign_fixtures.py differs from the copy the reference bundle was generated " + "with. Re-sync it from squidpy-ports and regenerate the bundle." + ) + + +# -------------------------------------------------------------------------------------- +# Fixture validation -- runs before anything that depends on it +# -------------------------------------------------------------------------------------- + + +def test_fixture_samples_are_off_grid(primitives): + """No interpolation sample may sit on a grid line. See ledger row D10. + + Upstream and squidpy compute the fractional index by formulas that agree to ~1 ulp; + exactly on a grid line they can floor() to different neighbours, giving an O(1) + difference that says nothing about the port. + """ + coords = np.asarray(primitives["interp_coords"]) + axes = (np.asarray(primitives["raster_ref_y"]), np.asarray(primitives["raster_ref_x"])) + for axis, values in zip(axes, coords, strict=True): + fractional = np.abs(np.modf((values - axis[0]) / (axis[1] - axis[0]))[0]) + assert np.minimum(fractional, 1.0 - fractional).min() > 1e-6 + + +def test_fixture_stays_inside_velocity_grid(primitives, velocity_grid): + """Keeps the padding divergence (D5) out of every test that is not about it.""" + points = np.asarray(primitives["points"]) + for axis, column in zip(velocity_grid, points.T, strict=True): + assert column.min() > float(axis[0]) and column.max() < float(axis[-1]) + + +# -------------------------------------------------------------------------------------- +# Primitives that the port reproduces faithfully +# -------------------------------------------------------------------------------------- + + +def test_interp_matches_upstream(primitives, source_grid, record_property): + """``_interp`` vs ``STalign.interp(padding_mode='border')``.""" + axes, image = source_grid + got = _core._interp(axes, image, jnp.asarray(primitives["interp_coords"])) + error = rel(got, primitives["interp_border"]) + record_property("rel_error", error) + assert error < EXACT + + +def test_regularizer_matches_upstream(primitives, velocity_grid, record_property): + """``LL``/``K``/``DV`` vs STalign.py:1078-1090. + + This is a precondition, not a nicety: the regulariser sets the scale of the whole + velocity term, so if it disagreed every later comparison would be meaningless. + """ + kernel, ll, dv_prod = _core._build_regularizer(velocity_grid, a=F.LDDMM_PARAMS["a"], p=F.LDDMM_PARAMS["p"]) + for name, got, expected in ( + ("LL", ll, primitives["regularizer_LL"]), + ("K", kernel, primitives["regularizer_K"]), + ("DV", dv_prod, primitives["regularizer_DV"]), + ): + error = rel(got, expected) + record_property(f"rel_error_{name}", error) + assert error < EXACT, name + + +def test_transform_grid_backward_matches_upstream(primitives, target_grid, velocity_grid, record_property): + """``_transform_grid_backward`` vs ``STalign.build_transform(direction='b')``. + + This is the inner loop of the objective: invert the affine, then integrate ``-v`` + backwards in time. Upstream returns ``(H, W, 2)``; squidpy returns ``(2, H, W)``. + """ + axes, _ = target_grid + got = _core._transform_grid_backward( + axes, velocity_grid, jnp.asarray(primitives["velocity"]), jnp.asarray(primitives["to_A"]) + ) + error = rel(np.moveaxis(np.asarray(got), 0, -1), primitives["grid_backward"]) + record_property("rel_error", error) + assert error < EXACT + + +def test_transform_points_forward_matches_upstream(primitives, velocity_grid, record_property): + """``direction='forward'`` vs ``STalign.transform_points_source_to_target``.""" + got = _core.transform_points_row_col( + velocity_grid, + jnp.asarray(primitives["velocity"]), + jnp.asarray(primitives["to_A"]), + jnp.asarray(primitives["points"]), + direction="forward", + ) + error = rel(got, primitives["points_forward"]) + record_property("rel_error", error) + assert error < EXACT + + +def test_to_affine_matches_upstream(primitives): + """``_to_affine`` vs ``STalign.to_A``.""" + got = _core._to_affine(jnp.asarray(primitives["to_A_linear"]), jnp.asarray(primitives["to_A_translation"])) + np.testing.assert_allclose(np.asarray(got), primitives["to_A"], rtol=0, atol=0) + + +# -------------------------------------------------------------------------------------- +# The objective itself +# -------------------------------------------------------------------------------------- + + +def _loss_arguments(primitives, source_grid, target_grid, velocity_grid, *, nt, with_points): + source_axes, source_image = source_grid + target_axes, target_image = target_grid + kernel, ll, dv_prod = _core._build_regularizer(velocity_grid, a=F.LDDMM_PARAMS["a"], p=F.LDDMM_PARAMS["p"]) + empty = jnp.zeros((0, 2)) + landmarks_source = jnp.asarray(primitives["landmarks_query"])[:, ::-1] + landmarks_target = jnp.asarray(primitives["landmarks_ref"])[:, ::-1] + return ( + kernel, + { + "x_source": source_axes, + "source_image": source_image, + "x_target": target_axes, + "target_image": target_image, + "xv": velocity_grid, + # LDDMM's initial state: uniform 0.5 matching weights (STalign.py:1102). + "match_weights": jnp.full(target_image.shape[1:], 0.5), + "ll": ll, + "dv_prod": dv_prod, + "points_source": landmarks_source if with_points else empty, + "points_target": landmarks_target if with_points else empty, + "sigmaM": F.LDDMM_PARAMS["sigmaM"], + "sigmaR": F.LDDMM_PARAMS["sigmaR"], + "sigmaP": F.LDDMM_PARAMS["sigmaP"], + }, + jnp.zeros((nt, velocity_grid[0].shape[0], velocity_grid[1].shape[0], 2)), + ) + + +@pytest.mark.parametrize("nt", [1, 3]) +@pytest.mark.parametrize("with_points", [False, True]) +@pytest.mark.parametrize("warm", [False, True], ids=["v_zero", "v_nonzero"]) +def test_energy_matches_upstream( + primitives, source_grid, target_grid, velocity_grid, nt, with_points, warm, record_property +): + """``_lddmm_loss`` vs upstream's ``E`` at iteration 0. + + The strongest single result in this suite: it says the two implementations optimise + the *same function*. Divergence D3 is inert here because no gradient is taken. + + The ``v_nonzero`` half is not redundant. LDDMM starts at ``v = 0``, where the + regularisation term ``ER`` contributes exactly nothing -- so evaluating only there + would compare equal even if ``ER`` were negated. Verified: with ``ER``'s sign flipped + in ``_lddmm_loss``, only these cases fail. + """ + energy = _load("energy") + _, kwargs, velocity = _loss_arguments( + primitives, source_grid, target_grid, velocity_grid, nt=nt, with_points=with_points + ) + key = f"E_nt{nt}_{'points' if with_points else 'nopoints'}" + if warm: + velocity = jnp.asarray(energy[f"warm_velocity_nt{nt}"]) + key += "_warmv" + + total, _ = _core._lddmm_loss(jnp.asarray(energy["L"]), jnp.asarray(energy["T"]), velocity, **kwargs) + error = rel(total, energy[key]) + record_property("rel_error", error) + assert error < EXACT, f"squidpy {float(total)!r} vs upstream {float(energy[key])!r}" + + +def test_energy_budget_is_not_vacuous(primitives, source_grid, target_grid, velocity_grid): + """A tolerance that a perturbed run also passes is decoration, not a test. + + Nudge one physically meaningful knob and the same assertion must fail. + """ + energy = _load("energy") + _, kwargs, velocity = _loss_arguments(primitives, source_grid, target_grid, velocity_grid, nt=3, with_points=True) + kwargs["sigmaM"] *= 1.0001 + total, _ = _core._lddmm_loss(jnp.asarray(energy["L"]), jnp.asarray(energy["T"]), velocity, **kwargs) + assert rel(total, energy["E_nt3_points"]) > EXACT + + +# -------------------------------------------------------------------------------------- +# Gradients -- ledger row D3 +# -------------------------------------------------------------------------------------- + + +@pytest.fixture(scope="module", params=[False, True], ids=["v_zero", "v_nonzero"]) +def measured_gradients(request, primitives, source_grid, target_grid, velocity_grid): + """Gradients of ``_lddmm_loss``, at ``v = 0`` and at a non-zero velocity. + + Both are needed for the same reason as the energy test: ``dER/dv`` vanishes at the + origin, so gradients taken only there cannot see the regularisation term. + """ + gradients = _load("gradients") + kernel, kwargs, velocity = _loss_arguments( + primitives, source_grid, target_grid, velocity_grid, nt=F.LDDMM_PARAMS["nt"], with_points=True + ) + suffix = "" + if request.param: + velocity = jnp.asarray(gradients["warm_velocity"]) + suffix = "_warmv" + + (_, _), (grad_l, grad_t, grad_v) = jax.value_and_grad(_core._lddmm_loss, argnums=(0, 1, 2), has_aux=True)( + jnp.asarray(gradients["L"]), jnp.asarray(gradients["T"]), velocity, **kwargs + ) + # Upstream stores the Sobolev-smoothed velocity gradient, since that is what actually + # drives the step (STalign.py:1215). + smoothed = jnp.fft.ifftn(jnp.fft.fftn(grad_v, axes=(1, 2)) * kernel[None, ..., None], axes=(1, 2)).real + return gradients, {"L": grad_l, "T": grad_t, "v": smoothed}, suffix + + +@pytest.mark.parametrize(("component", "key"), [("L", "grad_L"), ("T", "grad_T"), ("v", "grad_v_smoothed")]) +@pytest.mark.xfail( + strict=True, + reason=( + "ledger row D3: _contrast_transform differentiates through jnp.linalg.solve, " + "while upstream solves the ridge coefficients under torch.no_grad " + "(STalign.py:1184-1188). The ridge fit is an EM M-step; differentiating through " + "it turns alternating minimisation into joint optimisation. Fix is " + "jax.lax.stop_gradient around the solve -- when it lands, delete this xfail, " + "update STALIGN_DIVERGENCES.md row D3, and add a changelog entry." + ), +) +def test_gradients_match_upstream(measured_gradients, component, key, record_property): + gradients, computed, suffix = measured_gradients + error = rel(computed[component], gradients[key + suffix]) + record_property("rel_error", error) + assert error < EXACT + + +def test_gradient_gap_is_the_contrast_transform(measured_gradients, record_property): + """The positive form of D3: the gap is real, bounded, and where we say it is. + + Everything else in the objective agrees to ~1e-15, so a gradient discrepancy three + orders of magnitude larger cannot be floating-point noise. + """ + gradients, computed, suffix = measured_gradients + for component, key in (("L", "grad_L"), ("T", "grad_T"), ("v", "grad_v_smoothed")): + error = rel(computed[component], gradients[key + suffix]) + record_property(f"rel_error_{component}", error) + assert 1e-5 < error < 1e-2, f"{component}: {error:.3e} is outside the recorded D3 band" + + +def test_contrast_transform_leaks_gradient_through_the_solve(record_property): + """Isolates D3 without reference to upstream at all. + + Upstream's ``no_grad`` makes the ridge coefficients a constant of the optimisation. + Here, freezing them changes the gradient -- which is exactly the bug, stated as a + property of squidpy's own code rather than as a disagreement with someone else's. + """ + rng = np.random.default_rng(F.SEED) + warped = jnp.asarray(rng.normal(size=(3, 12, 15)) ** 2) + target = jnp.asarray(rng.normal(size=(3, 12, 15)) ** 2) + weights = jnp.asarray(rng.uniform(0.2, 0.8, size=(12, 15))) + + def live(x): + return jnp.sum(_core._contrast_transform(x, target, weights) ** 2) + + def frozen(x): + # _contrast_transform verbatim, with the one change upstream's no_grad makes. + flat_source = x.reshape(x.shape[0], -1) + flat_target = target.reshape(target.shape[0], -1) + design = jnp.concatenate((jnp.ones((1, flat_source.shape[1]), dtype=x.dtype), flat_source), axis=0) + weighted = design * weights.reshape(-1)[None, :] + coefficients = jax.lax.stop_gradient( + jnp.linalg.solve( + weighted @ design.T + 0.1 * jnp.eye(design.shape[0], dtype=x.dtype), + weighted @ flat_target.T, + ) + ) + return jnp.sum((coefficients.T @ design).reshape(target.shape) ** 2) + + # Same value, different gradient: the leak is entirely in the backward pass. + np.testing.assert_allclose(float(live(warped)), float(frozen(warped)), rtol=1e-12) + error = rel(jax.grad(live)(warped), jax.grad(frozen)(warped)) + record_property("rel_error", error) + assert error > 1e-6 + + +# -------------------------------------------------------------------------------------- +# Pinned divergences +# -------------------------------------------------------------------------------------- + + +@pytest.mark.xfail( + strict=True, + reason=( + "ledger row D2: squidpy uses np.arange(lo, hi + dx, dx) where upstream uses " + "np.arange(lo, hi, dx), so every axis is one point longer -- and because `hi` is " + "a float sum, the surplus is one or two points depending on rounding. Fix is " + "lo + dx * np.arange(int(np.ceil((hi - lo) / dx))). Needs its own PR: it changes " + "user-visible output shapes." + ), +) +def test_rasterize_grid_length_matches_upstream(primitives, clouds): + grid_x, grid_y, _ = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) + assert grid_x.size == primitives["raster_ref_x"].size + assert grid_y.size == primitives["raster_ref_y"].size + + +def test_rasterize_grid_agrees_where_it_overlaps(primitives, clouds): + """The positive half of D2: the surplus is purely trailing, the spacing is right.""" + grid_x, grid_y, _ = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) + upstream_x, upstream_y = primitives["raster_ref_x"], primitives["raster_ref_y"] + np.testing.assert_allclose(grid_x[: upstream_x.size], upstream_x, rtol=0, atol=1e-9) + np.testing.assert_allclose(grid_y[: upstream_y.size], upstream_y, rtol=0, atol=1e-9) + assert grid_x.size > upstream_x.size + + +@pytest.mark.xfail( + strict=True, + reason=( + "ledger row D2: _build_velocity_grid has the same np.arange(lo, hi + step, step) " + "off-by-one as rasterize. Fixed by the same change." + ), +) +def test_velocity_grid_length_matches_upstream(primitives, source_grid): + axes, _ = source_grid + built = _core._build_velocity_grid(axes, a=F.LDDMM_PARAMS["a"], expand=F.LDDMM_PARAMS["expand"]) + assert built[0].shape[0] == primitives["xv_upstream_0"].size + assert built[1].shape[0] == primitives["xv_upstream_1"].size + + +@pytest.mark.xfail( + strict=True, + reason=( + "ledger row D6: squidpy integrates the backward flow in reversed time order, " + "upstream does not (STalign.py:1828-1843). squidpy is correct -- see " + "test_backward_transform_inverts_better, which is the assertion that matters. " + "This xfail exists to pin the literal difference, and should be deleted only if " + "squidpy ever deliberately adopts upstream's ordering." + ), +) +def test_transform_points_backward_matches_upstream(primitives, velocity_grid): + got = _core.transform_points_row_col( + velocity_grid, + jnp.asarray(primitives["velocity"]), + jnp.asarray(primitives["to_A"]), + jnp.asarray(primitives["points"]), + direction="backward", + ) + assert rel(got, primitives["points_backward"]) < EXACT + + +def test_backward_transform_inverts_better(primitives, velocity_grid, record_property): + """D6, stated usefully: squidpy's backward map is the better inverse of the forward one.""" + points = jnp.asarray(primitives["points"]) + roundtrip = _core.transform_points_row_col( + velocity_grid, + jnp.asarray(primitives["velocity"]), + jnp.asarray(primitives["to_A"]), + jnp.asarray(primitives["points_forward"]), + direction="backward", + ) + ours = rel(roundtrip, points) + theirs = rel(primitives["points_roundtrip"], points) + record_property("roundtrip_squidpy", ours) + record_property("roundtrip_upstream", theirs) + assert ours <= theirs + + +@pytest.mark.xfail( + strict=True, + reason=( + "ledger row D5: upstream samples the velocity field with grid_sample's default " + "padding_mode='zeros' (STalign.py:1163, :1167), squidpy uses " + "map_coordinates(mode='nearest'). squidpy is correct -- zeros make a point that " + "drifts off the velocity grid snap to no displacement at all." + ), +) +def test_interp_outside_domain_matches_upstream(primitives, source_grid): + axes, image = source_grid + got = _core._interp(axes, image, jnp.asarray(primitives["interp_coords_outside"])) + assert rel(got, primitives["interp_zeros_outside"]) < EXACT + + +def test_interp_outside_domain_is_border_padding(primitives, source_grid, record_property): + """The positive half of D5: squidpy's behaviour is exactly upstream's 'border' mode.""" + axes, image = source_grid + got = _core._interp(axes, image, jnp.asarray(primitives["interp_coords_outside"])) + error = rel(got, primitives["interp_border_outside"]) + record_property("rel_error", error) + assert error < EXACT + assert rel(got, primitives["interp_zeros_outside"]) > 0.1 + + +# -------------------------------------------------------------------------------------- +# Budgeted divergences +# -------------------------------------------------------------------------------------- + +#: Per-blur relative-L2 budget for the rasteriser (ledger row D1). Measured 6.20 % / +#: 1.98 % / 5.96 % at blur 2.0 / 1.0 / 0.5; 9 % leaves headroom without going vacuous. +RASTER_BUDGET = 0.09 +#: Upstream conserves each point's mass exactly; squidpy's mode="constant" leaks at the +#: border. Measured worst case 777.3 of 800 = 2.8 % at blur=2.0. +RASTER_MASS_BUDGET = 0.95 + + +def test_rasterize_stays_within_budget(primitives, clouds, record_property): + """D1 is a deliberate speedup, so it gets a measured budget rather than equality.""" + _, _, got = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) + expected = primitives["raster_ref"] + cropped = np.asarray(got)[:, : expected.shape[1], : expected.shape[2]] + + for index, blur in enumerate(F.RASTER_PARAMS["blur"]): + error = rel(cropped[index], expected[index]) + mass = float(np.asarray(got)[index].sum()) / float(expected[index].sum()) + correlation = float(np.corrcoef(cropped[index].ravel(), expected[index].ravel())[0, 1]) + record_property(f"rel_error_blur{blur}", error) + record_property(f"mass_ratio_blur{blur}", mass) + assert error < RASTER_BUDGET, f"blur={blur}: relL2 {error:.4%} exceeds {RASTER_BUDGET:.0%}" + assert correlation > 0.99, f"blur={blur}: correlation {correlation:.5f}" + assert RASTER_MASS_BUDGET < mass <= 1.0, f"blur={blur}: mass ratio {mass:.4f}" + + +def _residual(linear, translation, source, target) -> float: + source, target = np.asarray(source), np.asarray(target) + return float(np.linalg.norm(source @ np.asarray(linear).T + np.asarray(translation) - target)) + + +def test_affine_from_points_is_equivalent_when_well_conditioned(primitives, record_property): + """D7: the two are different estimators, not the same one implemented twice. + + Upstream solves the normal equations for the plain least-squares fit; skimage solves + a Hartley-normalised homogeneous system by SVD, minimising algebraic rather than + geometric error. So their coefficients differ by ~1e-3 even on clean landmarks, and + asserting agreement would be wrong. What must hold is that neither is meaningfully + worse at the job. + """ + source = np.asarray(primitives["landmarks_query"])[:, ::-1] + target = np.asarray(primitives["landmarks_ref"])[:, ::-1] + linear, translation = _helpers.affine_from_points(jnp.asarray(source), jnp.asarray(target)) + + ours = _residual(linear, translation, source, target) + theirs = _residual(primitives["lt_well_L"], primitives["lt_well_T"], source, target) + record_property("residual_squidpy", ours) + record_property("residual_upstream", theirs) + record_property("rel_error_L", rel(linear, primitives["lt_well_L"])) + assert abs(ours - theirs) / theirs < 1e-2, f"squidpy {ours:.6f} vs upstream {theirs:.6f}" + + +def test_affine_from_points_survives_ill_conditioning(primitives, record_property): + """D7, the half that matters: upstream's ``inv(XᵀX)`` collapses, skimage does not.""" + source, target = primitives["ill_src"], primitives["ill_dst"] + linear, translation = _helpers.affine_from_points(jnp.asarray(source), jnp.asarray(target)) + + ours = _residual(linear, translation, source, target) + theirs = _residual(primitives["lt_ill_L"], primitives["lt_ill_T"], source, target) + record_property("residual_squidpy", ours) + record_property("residual_upstream", theirs) + assert ours < theirs * 1e-6, ( + f"expected upstream to lose badly on near-collinear landmarks, got squidpy {ours:.3e} vs upstream {theirs:.3e}" + ) + + +# -------------------------------------------------------------------------------------- +# The ledger has to stay in sync +# -------------------------------------------------------------------------------------- + + +def test_divergences_doc_covers_all_xfails(): + """Every strict xfail cites a ledger row, and every cited row exists.""" + ledger = LEDGER.read_text() + documented = set(re.findall(r"\*\*(D\d+)\*\*", ledger)) + assert documented, "no ledger rows found; STALIGN_DIVERGENCES.md changed shape" + + source = Path(__file__).read_text() + cited = set(re.findall(r"ledger row (D\d+)", source)) + assert cited, "no test cites a ledger row" + + missing = cited - documented + assert not missing, f"tests cite ledger rows that do not exist: {sorted(missing)}" From 9006b1b8e06e6cb343b391e12765c22bbc389b9c Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 16:17:51 +0200 Subject: [PATCH 122/131] fix stalign port divergences --- docs/release/notes-dev.md | 21 ++ .../methods/align_samples/_stalign.py | 11 ++ .../align_samples/_stalign_impl/_core.py | 34 +++- .../align_samples/_stalign_impl/_helpers.py | 83 ++++++-- .../methods/STALIGN_DIVERGENCES.md | 182 ++++++++---------- tests/experimental/methods/test_stalign.py | 16 ++ .../methods/test_stalign_reference.py | 154 +++++++-------- 7 files changed, 292 insertions(+), 209 deletions(-) diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 221023e5e..1ec9a8736 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -3,6 +3,27 @@ ## Features - {func}`squidpy.experimental.im.calculate_image_features` now featurizes tiles on a shared dask engine: `n_jobs > 1` runs worker processes via a `dask.distributed.LocalCluster` (or an active `Client`), and per-tile BLAS/OpenMP threads are pinned to avoid oversubscription. This also speeds up the serial path. {func}`squidpy.experimental.tl.calculate_tiling_qc` shares the same engine. Adds `distributed` and `threadpoolctl` as dependencies. +- Add a numerical reference suite for the experimental STalign port, comparing it against the + original PyTorch implementation. Deselected by default; runs on the scheduled job. Reference + values are generated out of band by + [scverse/squidpy-ports](https://github.com/scverse/squidpy-ports), so `torch` is not a squidpy + dependency. + [#1243](https://github.com/scverse/squidpy/issues/1243) + +## Bugfixes + +- The experimental STalign estimator no longer differentiates through the contrast-transform ridge + solve. That solve is an expectation-maximisation M step and must be held constant; treating it as + part of the objective changed the search direction. Gradients now agree with the original + implementation to ~1e-15, previously ~1e-3. +- **Breaking (experimental):** the rasterisation and velocity grids in the experimental STalign + estimator were one sample longer per axis than intended, and their length varied with + floating-point rounding. Output shapes change accordingly. +- The experimental STalign rasteriser now deposits each point bilinearly rather than snapping it to + the nearest cell, and conserves mass at the image border. Relative error against the original + implementation drops from 6.2 %/2.0 %/6.0 % to 4.1 %/0.8 %/2.9 % across the default blur scales. +- `lddmm(niter=0)` no longer raises `UnboundLocalError`. + - Fix {func}`squidpy.tl.var_by_distance` behaviour when providing {mod}`numpy` arrays of coordinates as anchor point. - Update :attr:`squidpy.pl.var_by_distance` to show multiple variables on same plot. [@LLehner](https://github.com/LLehner) diff --git a/src/squidpy/experimental/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py index bb16ffdad..cca5b4db7 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -118,6 +118,17 @@ def fit_stalign( A :class:`StalignResult` whose :meth:`~StalignResult.transform` maps ``(x, y)`` points into the reference frame; ``aligned_points`` is the fitted ``query`` already mapped. + + Notes + ----- + Runs in JAX's active float precision, which is **single** unless x64 is enabled. + The original STalign is double throughout, so results differ correspondingly. For + ``niter`` in the thousands, or a large ``sigmaR``, enable double precision before + importing JAX:: + + import jax + + jax.config.update("jax_enable_x64", True) """ # Import the JAX-backed solver only after the registry's requirements check # passes, so callers without JAX get a clean ImportError rather than a diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py index 0eb3ef6b9..96194ef84 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py @@ -113,6 +113,13 @@ def _transform_grid_backward( def _contrast_transform(source_image: jax.Array, target_image: jax.Array, weights: jax.Array) -> jax.Array: + """Weighted ridge fit mapping source intensities onto target intensities. + + The coefficients are held constant with respect to the optimisation. This is an + expectation-maximisation M step, solved exactly at the current estimate, not a + quantity to descend on -- differentiating through the solve would silently turn the + alternating minimisation into a joint one and change the search direction. + """ flat_source = source_image.reshape(source_image.shape[0], -1) flat_target = target_image.reshape(target_image.shape[0], -1) flat_weights = weights.reshape(-1) @@ -122,21 +129,33 @@ def _contrast_transform(source_image: jax.Array, target_image: jax.Array, weight design_cov = weighted_design @ design.T target_cov = weighted_design @ flat_target.T regularized = design_cov + 0.1 * jnp.eye(design_cov.shape[0], dtype=design_cov.dtype) - coefficients = jnp.linalg.solve(regularized, target_cov) + coefficients = jax.lax.stop_gradient(jnp.linalg.solve(regularized, target_cov)) return (coefficients.T @ design).reshape(target_image.shape) +def _axis(start: float, stop: float, step: float) -> jax.Array: + """``step``-spaced samples covering ``[start, stop)``, with a stable length. + + ``jnp.arange(start, stop, step)`` on floats derives its length from the arguments by + floating-point division, so a ``stop`` that is itself a sum of floats can yield one + more or one fewer sample than intended. Taking the count first makes the length a + function of the interval alone. + """ + count = max(int(np.ceil((stop - start) / step)), 1) + return start + step * jnp.arange(count, dtype=jax_dtype()) + + def _build_velocity_grid( x_source: tuple[jax.Array, jax.Array], *, a: float, expand: float ) -> tuple[jax.Array, jax.Array]: - minimum = jnp.array([x_source[0][0], x_source[1][0]]) - maximum = jnp.array([x_source[0][-1], x_source[1][-1]]) + minimum = np.array([x_source[0][0], x_source[1][0]], dtype=float) + maximum = np.array([x_source[0][-1], x_source[1][-1]], dtype=float) center = (minimum + maximum) / 2.0 half_width = (maximum - minimum) * expand / 2.0 step = a * 0.5 return ( - jnp.arange(center[0] - half_width[0], center[0] + half_width[0] + step, step), - jnp.arange(center[1] - half_width[1], center[1] + half_width[1] + step, step), + _axis(center[0] - half_width[0], center[0] + half_width[0], step), + _axis(center[1] - half_width[1], center[1] + half_width[1], step), ) @@ -309,6 +328,11 @@ def lddmm( loss_and_grad = jax.jit(jax.value_and_grad(_lddmm_loss, argnums=(0, 1, 2), has_aux=True)) + # Bound up front so `niter=0` -- evaluate the initial state and stop -- returns a + # well-formed result instead of reading unbound locals off the end of the loop. + energy = jnp.asarray(jnp.nan, dtype=jax_dtype()) + transformed_points = source_landmarks + for iteration in range(niter): (energy, aux), (grad_linear, grad_translation, grad_velocity) = loss_and_grad( linear, diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py index 3cb17ef95..b26fab863 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py @@ -42,6 +42,18 @@ def validate_points(points: Any, *, name: str) -> JaxArray: return arr +def _axis(start: float, stop: float, step: float) -> np.ndarray: + """``step``-spaced samples covering ``[start, stop)``, with a stable length. + + ``np.arange`` on floats derives its length from the arguments by floating-point + division, so a ``stop`` that is itself a sum of floats can yield one more or one + fewer sample than intended. Taking the count first makes the length a function of + the interval alone. + """ + count = max(int(np.ceil((stop - start) / step)), 1) + return start + step * np.arange(count, dtype=float) + + def rasterize( x: np.ndarray, y: np.ndarray, @@ -52,14 +64,12 @@ def rasterize( ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Rasterize a point cloud into a multi-scale Gaussian density image. - Each point deposits unit mass into its nearest cell of a regular - ``dx``-spaced grid; every ``blur`` scale is then an isotropic Gaussian blur - of that histogram, so each point becomes a unit-integral Gaussian. ``blur`` - is the kernel width in units of ``2 * dx`` (``sigma = 2 * blur`` cells). - Mass within ~``blur`` cells of the border leaks off-grid (``mode="constant"``). + Each point deposits unit mass bilinearly across its four neighbouring cells of a + regular ``dx``-spaced grid; every ``blur`` scale is then an isotropic Gaussian blur + of that histogram, so each point becomes a unit-integral Gaussian. ``blur`` sets the + kernel width: ``sigma = 2 * blur`` pixels, i.e. ``2 * blur * dx`` in physical units. + Total mass is preserved exactly, including for points near the border. """ - from scipy.ndimage import gaussian_filter - x = np.asarray(x, dtype=float).reshape(-1) y = np.asarray(y, dtype=float).reshape(-1) if x.shape != y.shape: @@ -85,23 +95,60 @@ def rasterize( half_x = (max_x - min_x) * expand / 2.0 half_y = (max_y - min_y) * expand / 2.0 - grid_x = np.arange(center_x - half_x, center_x + half_x + dx, dx, dtype=float) - grid_y = np.arange(center_y - half_y, center_y + half_y + dx, dx, dtype=float) + grid_x = _axis(center_x - half_x, center_x + half_x, dx) + grid_y = _axis(center_y - half_y, center_y + half_y, dx) if grid_x.size < 2 or grid_y.size < 2: raise ValueError("Rasterized grid is too small. Increase the point spread or lower `dx`.") - # Bin each point onto its nearest grid cell (unit mass each), dropping any - # whose nearest cell falls outside the padded grid. - col = np.rint((x - grid_x[0]) / dx).astype(np.intp) - row = np.rint((y - grid_y[0]) / dx).astype(np.intp) - inside = (col >= 0) & (col < grid_x.size) & (row >= 0) & (row < grid_y.size) - histogram = np.zeros((grid_y.size, grid_x.size), dtype=float) - np.add.at(histogram, (row[inside], col[inside]), 1.0) - - out = np.stack([gaussian_filter(histogram, sigma=2.0 * float(b), mode="constant") for b in blur_values]) + histogram = _deposit(x, y, grid_x, grid_y, dx) + out = np.stack([_blur_conserving(histogram, sigma=2.0 * float(b)) for b in blur_values]) return grid_x, grid_y, out +def _deposit(x: np.ndarray, y: np.ndarray, grid_x: np.ndarray, grid_y: np.ndarray, dx: float) -> np.ndarray: + """Spread each point's unit mass bilinearly over its four neighbouring cells. + + Snapping to the nearest cell instead would quantise every position by up to half a + cell before any blurring happens, which at a typical ``dx`` is comparable to the + features being registered. ``np.bincount`` keeps this a handful of vectorised passes + rather than a Python loop over points. + """ + n_rows, n_cols = grid_y.size, grid_x.size + col_f = (x - grid_x[0]) / dx + row_f = (y - grid_y[0]) / dx + col_0 = np.floor(col_f).astype(np.intp) + row_0 = np.floor(row_f).astype(np.intp) + col_w = col_f - col_0 + row_w = row_f - row_0 + + flat = np.zeros(n_rows * n_cols, dtype=float) + for row_offset, row_weight in ((0, 1.0 - row_w), (1, row_w)): + for col_offset, col_weight in ((0, 1.0 - col_w), (1, col_w)): + rows, cols = row_0 + row_offset, col_0 + col_offset + inside = (rows >= 0) & (rows < n_rows) & (cols >= 0) & (cols < n_cols) + flat += np.bincount( + rows[inside] * n_cols + cols[inside], + weights=(row_weight * col_weight)[inside], + minlength=flat.size, + ) + return flat.reshape(n_rows, n_cols) + + +def _blur_conserving(histogram: np.ndarray, *, sigma: float) -> np.ndarray: + """Gaussian blur that keeps every point's mass on the grid. + + ``mode="constant"`` lets a kernel centred near the border spill off the edge, so + points there contribute less than one unit and the density is biased low around the + rim. The mass a point at cell ``c`` retains is ``sum_p K(p - c)``, which by symmetry + of ``K`` equals ``gaussian_filter(ones)[c]`` -- dividing by that before blurring + makes the total exactly the number of points, wherever they lie. + """ + from scipy.ndimage import gaussian_filter + + retained = gaussian_filter(np.ones_like(histogram), sigma=sigma, mode="constant") + return gaussian_filter(histogram / retained, sigma=sigma, mode="constant") + + def affine_from_points( points_source: JaxArray, points_target: JaxArray, diff --git a/tests/experimental/methods/STALIGN_DIVERGENCES.md b/tests/experimental/methods/STALIGN_DIVERGENCES.md index f0387cd1e..6f56e64dc 100644 --- a/tests/experimental/methods/STALIGN_DIVERGENCES.md +++ b/tests/experimental/methods/STALIGN_DIVERGENCES.md @@ -2,7 +2,7 @@ Where squidpy's JAX port (`src/squidpy/experimental/methods/align_samples/_stalign_impl/`) differs from the -PyTorch original it was ported from, why, and what to do about it. +PyTorch original it was ported from, why, and what was done about it. Every number below is **measured**, not estimated, by `tests/experimental/methods/test_stalign_reference.py` against the reference bundle in @@ -15,132 +15,112 @@ Each row has an id. Every `xfail(strict=True)` in the test module cites its row `test_divergences_doc_covers_all_xfails` asserts the citation resolves — so this file cannot silently rot. -## Headline +## Where it stands -The **forward objective is identical**: `_lddmm_loss` reproduces upstream's `E` to a -relative error of **0.0** (bit-for-bit, `131.057468`). So do `_interp` (4e-16), -`_transform_grid_backward` (5e-18), the forward point transform (5e-17) and the -regulariser `LL`/`K`/`DV` (≤1e-15). The port is faithful where it counts most. +The port now reproduces the original **to machine precision** on everything that defines +the optimisation: -The differences are concentrated in three places: the **gradient** of the objective -(D3), the **rasteriser** (D1), and **grid construction** (D2). +| | relative error | +| --- | --- | +| objective `E` | **0.0** (bit-for-bit, `131.057468`) | +| `dE/dL`, `dE/dT`, `dE/dv` | **≤ 6e-15** | +| `_interp` | 4e-16 | +| `_transform_grid_backward` | 5e-18 | +| forward point transform | 5e-17 | +| regulariser `LL` / `K` / `DV` | ≤ 1e-15 | +| raster and velocity grids | identical | + +Three findings were real bugs and are **fixed** (D2, D3, R1). One is a deliberate +approximation, now measured and budgeted (D1). The rest are places where squidpy is +deliberately *not* bug-compatible with upstream (D4, D5, D6, D7, D9) — those stay, pinned. ## Ledger -| id | What | Upstream | squidpy | Measured | Verdict | +| id | What | Upstream | squidpy | Measured | Status | | --- | --- | --- | --- | --- | --- | -| **D1** | Rasterisation algorithm | exact sub-pixel Gaussian splat per point, truncated to ±`ceil(4·max(blur))` px and renormalised over that window — `:178-201` | `np.rint` to nearest cell, then `scipy.ndimage.gaussian_filter(sigma=2·blur, mode="constant")` — [`_helpers.py:95-101`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py#L95) | relL2 **6.20 % / 1.98 % / 5.96 %** at blur 2.0/1.0/0.5; mass **777.3 / 797.2 / 799.9** of 800 | Accept, budget it. Two fixable sub-issues — see below | -| **D2** | Grid construction off-by-one | `np.arange(lo, hi, dx)` — `:137-138`, `:1069` | `np.arange(lo, hi + dx, dx)` — [`_helpers.py:88`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py#L88), [`_core.py:138`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L138) | +1 point per axis (raster 37×38 vs 36×37; velocity 23×22 vs 22×21). **Length is float-rounding dependent**: in a 9-case sweep it emitted `n+1` seven times and `n+2` twice | **Bug.** Pin now, fix in its own PR | -| **D3** | Gradient of the contrast transform | ridge coefficients solved under `torch.no_grad()` — `:1184-1188` | differentiates through `jnp.linalg.solve` — [`_core.py:125`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L125) | `dE/dL` **1.25e-3**, `dE/dT` **9.98e-4**, `dE/dv` **1.22e-3** relative — against ~1e-16 everywhere else | **Bug.** One-line fix, see below | -| **D4** | Returned affine lags a step | `A = to_A(L,T)` built at the *top* of the loop and returned — `:1155`, `:1308` | built after the loop — [`_core.py:361`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L361) | `LDDMM(n)["A"]` reflects `n-1` updates; asserted in squidpy-ports' `test_returned_affine_lags_one_step` | **squidpy is right** — upstream discards its last step. Comparisons use `squidpy(n).A ↔ upstream(n+1).A` | -| **D5** | Padding when sampling outside the domain | `grid_sample` default `padding_mode='zeros'` for velocity *and* point warps (`:1163`, `:1167`); only the image warp uses `'border'` (`:1171`) | `map_coordinates(mode="nearest")` (≈`border`) everywhere — [`_core.py:59`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L59) | outside the domain: **25 %** from upstream-`zeros`, **2.8e-16** from upstream-`border` | **squidpy is right.** Upstream's zeros make a point that drifts off the velocity grid snap to *no* displacement — a discontinuity. Latent at `expand=2.0` | -| **D6** | Backward point transform, time order | integrates `-v[t]` for `t` in `range(nt)` — forward order — `:1828-1843` | `reversed(range(nt))` — [`_core.py:81`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L81) | outputs differ by **1.03e-6**; forward∘backward round-trip error **5.79e-7 (squidpy)** vs **9.01e-7 (upstream)** | **squidpy is right** — it is the correct explicit-Euler inverse, and upstream contradicts its own image warp (`:1163`, which *does* reverse) | -| **D7** | Landmark affine solve | normal equations for the plain least-squares fit, with explicit `np.linalg.inv` on the Gram matrix — `:897-910` | `skimage.transform.estimate_transform("affine")`, a Hartley-normalised homogeneous solve by SVD — [`_helpers.py:125`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py#L125) | `L` differs **6.4e-4**, `T` **7.2e-3**. Fit residual on clean landmarks: **21.7026 (squidpy)** vs **21.6984 (upstream)** — a 1.9e-4 relative difference. On near-collinear landmarks: **7.4e-13 (squidpy)** vs **5.6e+2 (upstream)** | **Different estimators, not the same one twice.** skimage minimises algebraic error, upstream geometric, so they will never agree exactly; upstream is a hair better on clean input and collapses entirely when ill-conditioned. Keep squidpy's — but see R7 | -| **D8** | `lddmm()` cannot take a precomputed `xv`/`v` | `LDDMM` accepts both — `:1060-1064` | no such parameters | — | **No change needed.** The reference generator forces *upstream* onto squidpy's grid instead, which isolates D2 without touching squidpy's source | -| **D9** | Division guards | none | `jnp.maximum(…, 1e-12)` in `_update_mixture_weights` — [`_core.py:179`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L179) | inert for short runs | **squidpy is right.** Keep | -| **D10** | On-grid interpolation kink | normalises `(c-x0)/(x[-1]-x0)`, then `grid_sample(align_corners=True)` scales by `(n-1)` | `(c-x0)/(x[1]-x0)` — [`_core.py:52`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L52) | equal to ~1 ulp, but a sample landing exactly on a grid line can `floor()` to different neighbours | Not a defect in either. Fixtures are built off-grid on purpose and assert it | +| **D1** | Rasterisation algorithm | exact sub-pixel Gaussian splat per point, truncated to ±`ceil(4·max(blur))` px and renormalised over that window — `:178-201` | bilinear deposit onto the grid, then one `scipy.ndimage.gaussian_filter` per scale, mass-corrected — [`_helpers.py`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_helpers.py) | relL2 **4.08 % / 0.81 % / 2.87 %** at blur 2.0/1.0/0.5; mass exact | **Accepted, budgeted.** Improved from 6.20/1.98/5.96 % — see R2 | +| **D2** | Grid construction off-by-one | `np.arange(lo, hi, dx)` — `:137-138`, `:1069` | was `np.arange(lo, hi + dx, dx)` | was +1 sample per axis, and *length varied with rounding* — `n+1` in 7 of 9 sweep cases, `n+2` in 2 | **Fixed.** Count derived from the interval first; grids now identical to upstream | +| **D3** | Gradient of the contrast transform | ridge coefficients solved under `torch.no_grad()` — `:1184-1188` | was differentiating through `jnp.linalg.solve` | was `dE/dL` 1.25e-3, `dE/dT` 9.98e-4, `dE/dv` 1.22e-3; now **≤ 6e-15** | **Fixed** with `jax.lax.stop_gradient`. The ridge fit is an EM **M step**; differentiating through it turned alternating minimisation into joint optimisation | +| **D4** | Returned affine lags a step | `A = to_A(L,T)` built at the *top* of the loop and returned — `:1155`, `:1308` | built after the loop | `LDDMM(n)["A"]` reflects `n-1` updates | **squidpy is right** — upstream discards its last step. Comparisons use `squidpy(n).A ↔ upstream(n+1).A` | +| **D5** | Padding when sampling outside the domain | `grid_sample` default `padding_mode='zeros'` for velocity *and* point warps (`:1163`, `:1167`); only the image warp uses `'border'` (`:1171`) | `map_coordinates(mode="nearest")` (≈`border`) everywhere | outside the domain: **25 %** from upstream-`zeros`, **2.8e-16** from upstream-`border` | **squidpy is right.** Upstream's zeros make a point that drifts off the velocity grid snap to *no* displacement — a discontinuity. Pinned `xfail` | +| **D6** | Backward point transform, time order | integrates `-v[t]` for `t` in `range(nt)` — forward order — `:1828-1843` | `reversed(range(nt))` | outputs differ by **1.03e-6**; forward∘backward round-trip error **5.79e-7 (squidpy)** vs **9.01e-7 (upstream)** | **squidpy is right** — it is the correct explicit-Euler inverse, and upstream contradicts its own image warp (`:1163`, which *does* reverse). Pinned `xfail` | +| **D7** | Landmark affine solve | normal equations for the plain least-squares fit, with explicit `np.linalg.inv` on the Gram matrix — `:897-910` | `skimage.transform.estimate_transform("affine")`, a Hartley-normalised homogeneous solve by SVD | `L` differs **6.4e-4**, `T` **7.2e-3**. Fit residual on clean landmarks: **21.7026** vs **21.6984** (1.9e-4 apart). On near-collinear landmarks: **7.4e-13** vs **5.6e+2** | **Different estimators, not the same one twice.** skimage minimises algebraic error, upstream geometric; upstream is a hair better on clean input and collapses when ill-conditioned. Keep squidpy's — see R7 | +| **D8** | `lddmm()` cannot take a precomputed `xv`/`v` | `LDDMM` accepts both — `:1060-1064` | no such parameters | — | **No change needed.** The generator forces *upstream* onto squidpy's grid instead | +| **D9** | Division guards | none | `jnp.maximum(…, 1e-12)` in `_update_mixture_weights` | inert for short runs | **squidpy is right.** Keep | +| **D10** | On-grid interpolation kink | normalises `(c-x0)/(x[-1]-x0)`, then `grid_sample(align_corners=True)` scales by `(n-1)` | `(c-x0)/(x[1]-x0)` | equal to ~1 ulp, but a sample landing exactly on a grid line can `floor()` to different neighbours | Not a defect in either. Fixtures are built off-grid on purpose and assert it | ## Review — beyond the divergences -These are port-quality findings, not upstream comparisons. None are blocking; all are -cheap. - -### R1. `lddmm(niter=0)` raises `UnboundLocalError` - -`energy` and `transformed_points` are only bound inside the loop -([`_core.py:313`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L313)), -so the `return` at the end reads unbound locals: +Port-quality findings, not upstream comparisons. -``` -UnboundLocalError: cannot access local variable 'energy' where it is not associated with a value -``` +### R1. `lddmm(niter=0)` raised `UnboundLocalError` — fixed -`niter=0` is a reasonable thing to ask for (evaluate the initial affine and nothing -else). Either initialise both before the loop or reject `niter < 1` explicitly. +`energy` and `transformed_points` were bound only inside the loop, so the `return` +read unbound locals. `niter=0` is a reasonable request (evaluate the initial affine and +stop); both are now initialised before the loop, and +`test_lddmm_accepts_zero_iterations` covers it. -### R2. Two independent fixes for D1, each worth about half the gap +### R2. Rasteriser accuracy — fixed, ~2× better -The 1–6 % rasteriser gap is *two* problems, and they dominate at opposite ends of the -blur range: +The gap was two separable problems, dominating at opposite ends of the blur range. +Measured, all four combinations: -1. **Sub-pixel quantisation** (`np.rint` snaps every point to a cell centre) dominates at - small blur. Depositing each point bilinearly across its four neighbouring cells — still - fully vectorised, four `np.add.at` calls, no Python loop over points — measurably - halves the error where the kernel is narrow: +| variant | blur 2.0 | blur 1.0 | blur 0.5 | mass (of 800) | +| --- | --- | --- | --- | --- | +| nearest-cell + leaky border (was) | 6.20 % | 1.98 % | 5.96 % | 770.3 / 795.1 / 799.5 | +| bilinear deposit only | 6.34 % | 1.25 % | 2.88 % | leaks | +| mass conservation only | 3.92 % | 1.72 % | 5.97 % | exact | +| **both (now)** | **4.08 %** | **0.81 %** | **2.87 %** | **exact** | - | blur | current | bilinear deposit | - | --- | --- | --- | - | 2.0 | 6.20 % | 6.34 % | - | 1.0 | 1.98 % | **1.25 %** | - | 0.5 | 5.96 % | **2.88 %** | +1. **Sub-pixel quantisation.** `np.rint` snapped every point to a cell centre before any + blurring — up to half a cell of positional error, comparable to the features being + registered. Now deposited bilinearly across the four neighbouring cells, still fully + vectorised (`np.bincount`, no Python loop over points). +2. **Border mass loss.** `mode="constant"` let kernels near the edge spill off-grid, so + the density was biased low around the whole rim — 3 % of total mass at `blur=2.0`. The + mass a point at cell `c` retains is `sum_p K(p-c)`, which by symmetry of `K` equals + `gaussian_filter(ones)[c]`; dividing by that before blurring conserves mass exactly, + for one extra filter pass. -2. **Border mass loss** dominates at large blur: `mode="constant"` lets **2.8 %** of the - total mass leak off-grid at `blur=2.0` (777.3 of 800), where upstream renormalises - each point's kernel over its window and so conserves mass exactly. Renormalising by - `gaussian_filter(np.ones_like(histogram))` restores it for one extra filter pass. +Note the two are not additive: bilinear deposit alone made `blur=2.0` slightly *worse*, +because the error there was dominated by mass loss. -Note that (1) does not help at `blur=2.0` precisely because (2) is what is wrong there — -which is why they are worth doing as separate changes. +### R3. `blur` docstring — fixed -### R3. `blur` semantics are right; the docstring is not +It said blur was "the kernel width in units of `2 * dx`", which reads as if `blur` were +scaled by `2·dx`. The code was always correct and matches upstream exactly; the docstring +now says σ = `2·blur` pixels = `2·blur·dx` physical. -`_helpers.rasterize`'s docstring says blur is "the kernel width in units of `2 * dx`". -The code is correct and matches upstream exactly (σ = `2·blur` pixels = `2·blur·dx` -physical), but that phrasing reads as if `blur` were scaled by `2·dx`. Say "σ = `2·blur` -pixels" and stop. +### R4. The default dtype is float32; upstream is float64 throughout — documented -### R4. The default dtype is float32; upstream is float64 throughout - -`jax_dtype()` -([`_core.py:15`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L15)) -returns `float32` unless `jax_enable_x64` is set, and nothing in squidpy ever sets it — -so the shipped default runs a 5000-iteration gradient descent with `sigmaR=5e5` in single -precision, while every published STalign result is double. That is worth a line in -`fit_stalign`'s docstring at minimum. This reference suite therefore requires +`jax_dtype()` returns `float32` unless `jax_enable_x64` is set, and nothing in squidpy +sets it — so the shipped default runs a 5000-iteration gradient descent with +`sigmaR=5e5` in single precision, while every published STalign result is double. +`fit_stalign` now says so and shows how to enable x64. This suite requires `JAX_ENABLE_X64=1` and skips without it. -### R7. `affine_from_points` silently changes the landmark estimator +### R7. `affine_from_points` silently changes the landmark estimator — open Following on from D7: `estimate_transform("affine")` is not a drop-in for upstream's -normal-equations fit. It minimises **algebraic** error on Hartley-normalised coordinates, +normal-equations fit. It minimises **algebraic** error on Hartley-normalised coordinates; upstream minimises **geometric** residual. Every landmark-initialised alignment therefore -starts from a measurably different affine (`L` by 6.4e-4, `T` by 7.2e-3 here), which for -a 5000-iteration gradient descent is a different starting point, not a rounding detail. +starts from a measurably different affine (`L` by 6.4e-4, `T` by 7.2e-3), which for a +5000-iteration descent is a different starting point, not a rounding detail. -skimage's choice is the safer one — upstream's `inv(XᵀX)` produces a residual of 5.6e+2 -where skimage gets 7.4e-13 on near-collinear landmarks — so this is not a request to -revert. It is a request to *say so*: `affine_from_points`' docstring claims only to -"compute an affine initialization from corresponding landmarks", which reads as if the -choice of estimator were immaterial. If exact least-squares is wanted instead, -`np.linalg.lstsq` on the padded design matrix gives upstream's answer without upstream's -conditioning problem. +skimage's choice is the safer one, so this is not a request to revert — it is a request to +*say so* in the docstring. If exact least-squares is wanted, `np.linalg.lstsq` on the +padded design matrix gives upstream's answer without upstream's conditioning problem. -### R5. `niter=5000` runs blind +### R5. `niter=5000` runs blind — open -There is no energy trace in the return value and no convergence criterion; `lddmm` -returns only the final scalar `E`. Users cannot tell a converged run from a diverged one +No energy trace in the return value and no convergence criterion; `lddmm` returns only +the final scalar `E`. Users cannot distinguish a converged run from a diverged one without re-running. Returning the per-iteration energies (upstream accumulates them in a -local `Esave` and also throws them away) would cost nothing and make `niter` tunable. - -### R6. The Python loop forgoes most of JAX's advantage - -`lddmm` calls a jitted `value_and_grad` from a Python `for` loop -([`_core.py:312`](../../../src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py#L312)), -paying dispatch per iteration and blocking XLA from fusing across steps. The mixture-weight -update every 5th iteration is the only real obstacle, and it is expressible with -`lax.cond`. Worth measuring before committing to — but at 5000 iterations the dispatch -overhead is not negligible. - -## Suggested PR order - -1. **This PR** — tests only. Nothing under `src/` changes; every divergence above is - pinned with its measured number. -2. **D3** — `jax.lax.stop_gradient` around the ridge solve in `_contrast_transform`. One - line, no API or shape change. Upstream's `no_grad` is not incidental: the ridge fit is - an EM **M-step**, and differentiating through it turns alternating minimisation into - joint optimisation. Deletes three `xfail`s. -3. **R1**, **R3**, **R4** — trivial, batchable. -4. **D2** — `lo + dx * np.arange(int(np.ceil((hi - lo) / dx)))` in both places. Changes - user-visible output shapes, so it needs its own PR and a changelog entry. Deletes two - `xfail`s. -5. **R2** — rasteriser accuracy, with the numbers above as the acceptance criterion. +local `Esave` and throws them away too) would cost nothing and make `niter` tunable. + +### R6. The Python loop forgoes most of JAX's advantage — open + +`lddmm` calls a jitted `value_and_grad` from a Python `for` loop, paying dispatch per +iteration and blocking XLA from fusing across steps. The mixture-weight update every 5th +iteration is the only real obstacle and is expressible with `lax.cond`. Worth measuring +before committing to, but at 5000 iterations the dispatch overhead is not negligible. diff --git a/tests/experimental/methods/test_stalign.py b/tests/experimental/methods/test_stalign.py index 66d4f6c26..2aca5fb9b 100644 --- a/tests/experimental/methods/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -105,6 +105,22 @@ def test_stalign_rejects_unknown_kwarg() -> None: fit_stalign(ref, query, not_a_real_param=1.0, **_TINY) +def test_lddmm_accepts_zero_iterations() -> None: + """``niter=0`` means "evaluate the initial state and stop", not a crash. + + ``energy`` and the transformed landmarks used to be bound only inside the loop, so + the return statement read unbound locals. + """ + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + from squidpy.experimental.methods.align_samples._stalign_impl._helpers import rasterize_cloud + + grid = rasterize_cloud(_points_xy()[:, ::-1], dx=0.5, blur=1.0, expand=1.1) + result = lddmm(*grid, *grid, L=np.eye(2), T=np.zeros(2), niter=0, a=1.0, nt=1) + + assert result["v"].shape[0] == 1 + assert result["A"].shape == (3, 3) + + def test_default_dtype_is_unchanged() -> None: """Guards the reference suite's blast radius. diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py index 45a45ba6f..4dfc5cc47 100644 --- a/tests/experimental/methods/test_stalign_reference.py +++ b/tests/experimental/methods/test_stalign_reference.py @@ -339,43 +339,27 @@ def measured_gradients(request, primitives, source_grid, target_grid, velocity_g @pytest.mark.parametrize(("component", "key"), [("L", "grad_L"), ("T", "grad_T"), ("v", "grad_v_smoothed")]) -@pytest.mark.xfail( - strict=True, - reason=( - "ledger row D3: _contrast_transform differentiates through jnp.linalg.solve, " - "while upstream solves the ridge coefficients under torch.no_grad " - "(STalign.py:1184-1188). The ridge fit is an EM M-step; differentiating through " - "it turns alternating minimisation into joint optimisation. Fix is " - "jax.lax.stop_gradient around the solve -- when it lands, delete this xfail, " - "update STALIGN_DIVERGENCES.md row D3, and add a changelog entry." - ), -) def test_gradients_match_upstream(measured_gradients, component, key, record_property): + """``dE/dL``, ``dE/dT`` and the smoothed ``dE/dv`` vs upstream's, at iteration 0. + + Together with the energy test this pins both halves of the optimisation: the same + objective *and* the same search direction. See ledger row D3 -- these disagreed by + ~1.2e-3 until ``_contrast_transform`` stopped differentiating through the ridge solve. + """ gradients, computed, suffix = measured_gradients error = rel(computed[component], gradients[key + suffix]) record_property("rel_error", error) assert error < EXACT -def test_gradient_gap_is_the_contrast_transform(measured_gradients, record_property): - """The positive form of D3: the gap is real, bounded, and where we say it is. +def test_contrast_transform_freezes_the_ridge_coefficients(record_property): + """Regression guard for ledger row D3, stated without reference to upstream. - Everything else in the objective agrees to ~1e-15, so a gradient discrepancy three - orders of magnitude larger cannot be floating-point noise. - """ - gradients, computed, suffix = measured_gradients - for component, key in (("L", "grad_L"), ("T", "grad_T"), ("v", "grad_v_smoothed")): - error = rel(computed[component], gradients[key + suffix]) - record_property(f"rel_error_{component}", error) - assert 1e-5 < error < 1e-2, f"{component}: {error:.3e} is outside the recorded D3 band" - - -def test_contrast_transform_leaks_gradient_through_the_solve(record_property): - """Isolates D3 without reference to upstream at all. - - Upstream's ``no_grad`` makes the ridge coefficients a constant of the optimisation. - Here, freezing them changes the gradient -- which is exactly the bug, stated as a - property of squidpy's own code rather than as a disagreement with someone else's. + The ridge fit is an EM M step solved exactly at the current estimate, so its + coefficients must be constant with respect to the optimisation. Dropping the + ``stop_gradient`` would leave the *value* untouched and change only the gradient -- + invisible to any test that checks outputs rather than derivatives, which is how this + survived unnoticed in the first place. """ rng = np.random.default_rng(F.SEED) warped = jnp.asarray(rng.normal(size=(3, 12, 15)) ** 2) @@ -385,25 +369,23 @@ def test_contrast_transform_leaks_gradient_through_the_solve(record_property): def live(x): return jnp.sum(_core._contrast_transform(x, target, weights) ** 2) - def frozen(x): - # _contrast_transform verbatim, with the one change upstream's no_grad makes. + def leaky(x): + # _contrast_transform verbatim, minus the stop_gradient: the bug as it was. flat_source = x.reshape(x.shape[0], -1) flat_target = target.reshape(target.shape[0], -1) design = jnp.concatenate((jnp.ones((1, flat_source.shape[1]), dtype=x.dtype), flat_source), axis=0) weighted = design * weights.reshape(-1)[None, :] - coefficients = jax.lax.stop_gradient( - jnp.linalg.solve( - weighted @ design.T + 0.1 * jnp.eye(design.shape[0], dtype=x.dtype), - weighted @ flat_target.T, - ) + coefficients = jnp.linalg.solve( + weighted @ design.T + 0.1 * jnp.eye(design.shape[0], dtype=x.dtype), + weighted @ flat_target.T, ) return jnp.sum((coefficients.T @ design).reshape(target.shape) ** 2) - # Same value, different gradient: the leak is entirely in the backward pass. - np.testing.assert_allclose(float(live(warped)), float(frozen(warped)), rtol=1e-12) - error = rel(jax.grad(live)(warped), jax.grad(frozen)(warped)) - record_property("rel_error", error) - assert error > 1e-6 + # Identical value, different gradient -- which is exactly why this needs its own test. + np.testing.assert_allclose(float(live(warped)), float(leaky(warped)), rtol=1e-12) + error = rel(jax.grad(live)(warped), jax.grad(leaky)(warped)) + record_property("rel_error_vs_leaky", error) + assert error > 1e-6, "the ridge coefficients are being differentiated through again" # -------------------------------------------------------------------------------------- @@ -411,43 +393,31 @@ def frozen(x): # -------------------------------------------------------------------------------------- -@pytest.mark.xfail( - strict=True, - reason=( - "ledger row D2: squidpy uses np.arange(lo, hi + dx, dx) where upstream uses " - "np.arange(lo, hi, dx), so every axis is one point longer -- and because `hi` is " - "a float sum, the surplus is one or two points depending on rounding. Fix is " - "lo + dx * np.arange(int(np.ceil((hi - lo) / dx))). Needs its own PR: it changes " - "user-visible output shapes." - ), -) -def test_rasterize_grid_length_matches_upstream(primitives, clouds): +def test_rasterize_grid_matches_upstream(primitives, clouds): + """Ledger row D2, fixed: the raster axes are identical to upstream's.""" grid_x, grid_y, _ = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) - assert grid_x.size == primitives["raster_ref_x"].size - assert grid_y.size == primitives["raster_ref_y"].size + np.testing.assert_allclose(grid_x, primitives["raster_ref_x"], rtol=0, atol=1e-9) + np.testing.assert_allclose(grid_y, primitives["raster_ref_y"], rtol=0, atol=1e-9) -def test_rasterize_grid_agrees_where_it_overlaps(primitives, clouds): - """The positive half of D2: the surplus is purely trailing, the spacing is right.""" - grid_x, grid_y, _ = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) - upstream_x, upstream_y = primitives["raster_ref_x"], primitives["raster_ref_y"] - np.testing.assert_allclose(grid_x[: upstream_x.size], upstream_x, rtol=0, atol=1e-9) - np.testing.assert_allclose(grid_y[: upstream_y.size], upstream_y, rtol=0, atol=1e-9) - assert grid_x.size > upstream_x.size - - -@pytest.mark.xfail( - strict=True, - reason=( - "ledger row D2: _build_velocity_grid has the same np.arange(lo, hi + step, step) " - "off-by-one as rasterize. Fixed by the same change." - ), -) -def test_velocity_grid_length_matches_upstream(primitives, source_grid): +def test_velocity_grid_matches_upstream(primitives, source_grid): + """Ledger row D2, fixed: same off-by-one, same fix, in ``_build_velocity_grid``.""" axes, _ = source_grid built = _core._build_velocity_grid(axes, a=F.LDDMM_PARAMS["a"], expand=F.LDDMM_PARAMS["expand"]) - assert built[0].shape[0] == primitives["xv_upstream_0"].size - assert built[1].shape[0] == primitives["xv_upstream_1"].size + np.testing.assert_allclose(np.asarray(built[0]), primitives["xv_upstream_0"], rtol=0, atol=1e-9) + np.testing.assert_allclose(np.asarray(built[1]), primitives["xv_upstream_1"], rtol=0, atol=1e-9) + + +@pytest.mark.parametrize("step", [30.0, 7.3, 0.017]) +def test_grid_length_is_stable_against_float_rounding(step): + """The other half of D2: ``np.arange(lo, hi + step, step)`` had an unstable length. + + Deriving the count from the interval first makes it exact for every case; the old + form emitted one extra point sometimes and two others, depending on how ``hi`` + happened to round. + """ + for start, n in ((-400.4123, 33), (0.1, 41), (-1234.567, 77)): + assert _helpers._axis(start, start + n * step, step).size == n @pytest.mark.xfail( @@ -517,29 +487,43 @@ def test_interp_outside_domain_is_border_padding(primitives, source_grid, record # Budgeted divergences # -------------------------------------------------------------------------------------- -#: Per-blur relative-L2 budget for the rasteriser (ledger row D1). Measured 6.20 % / -#: 1.98 % / 5.96 % at blur 2.0 / 1.0 / 0.5; 9 % leaves headroom without going vacuous. -RASTER_BUDGET = 0.09 -#: Upstream conserves each point's mass exactly; squidpy's mode="constant" leaks at the -#: border. Measured worst case 777.3 of 800 = 2.8 % at blur=2.0. -RASTER_MASS_BUDGET = 0.95 +#: Per-blur relative-L2 budget for the rasteriser (ledger row D1). squidpy bins onto a +#: grid and convolves once; upstream splats an exact sub-pixel Gaussian per point and +#: renormalises it over a truncated window. Measured 4.08 % / 0.81 % / 2.87 % at blur +#: 2.0 / 1.0 / 0.5 -- 6 % leaves headroom without going vacuous. +RASTER_BUDGET = 0.06 def test_rasterize_stays_within_budget(primitives, clouds, record_property): """D1 is a deliberate speedup, so it gets a measured budget rather than equality.""" _, _, got = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) expected = primitives["raster_ref"] - cropped = np.asarray(got)[:, : expected.shape[1], : expected.shape[2]] + got = np.asarray(got) + assert got.shape == expected.shape for index, blur in enumerate(F.RASTER_PARAMS["blur"]): - error = rel(cropped[index], expected[index]) - mass = float(np.asarray(got)[index].sum()) / float(expected[index].sum()) - correlation = float(np.corrcoef(cropped[index].ravel(), expected[index].ravel())[0, 1]) + error = rel(got[index], expected[index]) + correlation = float(np.corrcoef(got[index].ravel(), expected[index].ravel())[0, 1]) record_property(f"rel_error_blur{blur}", error) - record_property(f"mass_ratio_blur{blur}", mass) assert error < RASTER_BUDGET, f"blur={blur}: relL2 {error:.4%} exceeds {RASTER_BUDGET:.0%}" assert correlation > 0.99, f"blur={blur}: correlation {correlation:.5f}" - assert RASTER_MASS_BUDGET < mass <= 1.0, f"blur={blur}: mass ratio {mass:.4f}" + + +def test_rasterize_conserves_mass(clouds, record_property): + """Every point contributes exactly one unit, wherever it sits. + + Upstream renormalises each point's kernel over its (possibly clipped) window, so a + point near the border still carries unit mass. A plain ``mode="constant"`` blur does + not, and used to lose 3 % of the total at the coarsest scale -- a density biased low + around the whole rim. + """ + n_points = clouds.ref.shape[0] + _, _, got = _helpers.rasterize(clouds.ref[:, 0], clouds.ref[:, 1], **F.RASTER_PARAMS) + + for index, blur in enumerate(F.RASTER_PARAMS["blur"]): + mass = float(np.asarray(got)[index].sum()) + record_property(f"mass_blur{blur}", mass) + assert mass == pytest.approx(n_points, rel=1e-9), f"blur={blur}: {mass:.2f} of {n_points}" def _residual(linear, translation, source, target) -> float: From 8cf57289d6901403b44aff35f0a37ec00550a77b Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 18:03:09 +0200 Subject: [PATCH 123/131] redesign align io with paths --- docs/api.md | 1 + docs/release/notes-dev.md | 15 ++ src/squidpy/experimental/methods/__init__.py | 6 +- .../methods/align_samples/__init__.py | 4 +- .../methods/align_samples/_stalign.py | 154 ++++++++++- .../experimental/methods/registry/__init__.py | 22 +- src/squidpy/experimental/tl/_align/_api.py | 247 +++++++++++------- src/squidpy/experimental/tl/_align/_io.py | 125 +-------- src/squidpy/experimental/tl/_align/_paths.py | 166 ++++++++++++ .../stalign_reference/converged_n500.npz | Bin 64095 -> 62604 bytes tests/_data/stalign_reference/energy.npz | Bin 32217 -> 32094 bytes tests/_data/stalign_reference/gradients.npz | Bin 53573 -> 53062 bytes tests/_data/stalign_reference/primitives.npz | Bin 171843 -> 172493 bytes .../_data/stalign_reference/trajectory_n1.npz | Bin 11137 -> 10243 bytes .../_data/stalign_reference/trajectory_n5.npz | Bin 26751 -> 24184 bytes .../stalign_reference/trajectory_n50.npz | Bin 26564 -> 24519 bytes .../methods/STALIGN_DIVERGENCES.md | 5 + .../methods/test_stalign_reference.py | 129 ++++++++- tests/experimental/tl/test_align.py | 235 +++++++++++++---- .../tl/test_align_by_landmarks.py | 56 ++-- tests/experimental/tl/test_align_io.py | 212 +-------------- tests/experimental/tl/test_align_paths.py | 166 ++++++++++++ 22 files changed, 1041 insertions(+), 502 deletions(-) create mode 100644 src/squidpy/experimental/tl/_align/_paths.py create mode 100644 tests/experimental/tl/test_align_paths.py diff --git a/docs/api.md b/docs/api.md index 3db1a95db..93b5e1ee5 100644 --- a/docs/api.md +++ b/docs/api.md @@ -152,6 +152,7 @@ result types listed alongside. :toctree: api experimental.methods.align_samples._stalign.fit_stalign + experimental.methods.align_samples._stalign.fit_stalign_image experimental.methods.align_landmarks._landmark.fit_similarity experimental.methods.align_landmarks._landmark.fit_affine experimental.methods.align_samples._stalign.StalignResult diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 1ec9a8736..4273e8685 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -9,6 +9,21 @@ [scverse/squidpy-ports](https://github.com/scverse/squidpy-ports), so `torch` is not a squidpy dependency. [#1243](https://github.com/scverse/squidpy/issues/1243) +- **Breaking (experimental):** {func}`squidpy.experimental.tl.align` now locates data with `in_` + and `out` paths instead of a stack of key arguments. `ref_key`, `query_key`, `spatial_key`, + `key_added`, `output_mode` and `on` are replaced by `in_` (e.g. `"obsm/spatial"`, + `"tables/slice1/obsm/spatial"`, `"images/he"`), `out`, and `copy`. `on` is gone because the path + already says which modality is meant. `out=None` (the default) returns the fitted alignment and + writes nothing. This follows the shape proposed for scanpy in + [scanpy#4007](https://github.com/scverse/scanpy/issues/4007). +- {func}`squidpy.experimental.tl.align` can now align on images. The fitted diffeomorphism cannot + be expressed as a SpatialData transformation, so writing to an `images/...` path materialises the + warped image rather than registering it lazily. Adds an `align_images` method family and + `squidpy.experimental.methods.align_samples.fit_stalign_image`. +- **Breaking (experimental):** {func}`squidpy.experimental.tl.align_by_landmarks` takes `in_` / + `out` / `copy` in place of `spatial_key` / `key_added` / `output_mode`, and its coordinate-system + arguments are renamed `cs_ref` / `cs_query`. Because `out` is always named explicitly, the guard + that refused to overwrite an auto-derived key is gone. ## Bugfixes diff --git a/src/squidpy/experimental/methods/__init__.py b/src/squidpy/experimental/methods/__init__.py index 4beec0617..2da430e5e 100644 --- a/src/squidpy/experimental/methods/__init__.py +++ b/src/squidpy/experimental/methods/__init__.py @@ -11,11 +11,13 @@ from __future__ import annotations -# Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. +# Import for side effects: populates ALIGN_SAMPLES / ALIGN_IMAGES / ALIGN_LANDMARKS. from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 from squidpy.experimental.methods.registry import ( + ALIGN_IMAGES, ALIGN_LANDMARKS, ALIGN_SAMPLES, + AlignImagesFn, AlignLandmarksFn, AlignResult, AlignSamplesFn, @@ -26,7 +28,9 @@ "Registry", "AlignResult", "AlignSamplesFn", + "AlignImagesFn", "AlignLandmarksFn", "ALIGN_SAMPLES", + "ALIGN_IMAGES", "ALIGN_LANDMARKS", ] diff --git a/src/squidpy/experimental/methods/align_samples/__init__.py b/src/squidpy/experimental/methods/align_samples/__init__.py index f446f2d38..d53ff9fdf 100644 --- a/src/squidpy/experimental/methods/align_samples/__init__.py +++ b/src/squidpy/experimental/methods/align_samples/__init__.py @@ -9,6 +9,6 @@ from __future__ import annotations -from squidpy.experimental.methods.align_samples._stalign import StalignResult, fit_stalign +from squidpy.experimental.methods.align_samples._stalign import StalignResult, fit_stalign, fit_stalign_image -__all__ = ["fit_stalign", "StalignResult"] +__all__ = ["fit_stalign", "fit_stalign_image", "StalignResult"] diff --git a/src/squidpy/experimental/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py index cca5b4db7..d517f630a 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -12,7 +12,7 @@ import numpy.typing as npt -from squidpy.experimental.methods.registry import ALIGN_SAMPLES +from squidpy.experimental.methods.registry import ALIGN_IMAGES, ALIGN_SAMPLES if TYPE_CHECKING: import jax @@ -34,6 +34,32 @@ class StalignResult: velocity: JaxArray velocity_grid: tuple[JaxArray, JaxArray] aligned_points: JaxArray + #: Row-col axes of the query and reference rasters the fit ran on, when it ran on + #: images. ``None`` for point-cloud fits, where no raster survives the call. + query_axes: tuple[JaxArray, JaxArray] | None = None + ref_axes: tuple[JaxArray, JaxArray] | None = None + + def warp_image(self, image: JaxArray) -> JaxArray: + """Resample a query-frame ``(c, y, x)`` image onto the reference grid. + + A diffeomorphism cannot be expressed as a SpatialData transformation -- the + available types are affine at most -- so an aligned image has to be materialised + rather than registered. + """ + import jax.numpy as jnp + + from ._stalign_impl._core import _interp, _transform_grid_backward, jax_dtype + + if self.query_axes is None or self.ref_axes is None: + raise ValueError( + "This result was fitted on point clouds, so it carries no raster axes to " + "warp an image with. Fit with `align(in_='images/', ...)` instead." + ) + arr = jnp.asarray(image, dtype=jax_dtype()) + if arr.ndim == 2: + arr = arr[None] + grid = _transform_grid_backward(self.ref_axes, self.velocity_grid, self.velocity, self.affine) + return _interp(self.query_axes, arr, grid) def transform( self, @@ -187,4 +213,130 @@ def fit_stalign( velocity=result["v"], velocity_grid=result["xv"], aligned_points=aligned_rc[:, ::-1], + # No raster axes: the grids here are the internal density rasters at `dx` + # resolution, not a frame any real image lives on. Offering `warp_image` off them + # would quietly resample the caller's image onto a coarse, unrelated grid. + ) + + +@ALIGN_IMAGES.register("stalign", requires=("jax",)) +def fit_stalign_image( + ref: npt.ArrayLike, + query: npt.ArrayLike, + *, + ref_scale: tuple[float, float] = (1.0, 1.0), + query_scale: tuple[float, float] = (1.0, 1.0), + # LDDMM registration + a: float = 20.0, + p: float = 2.0, + expand: float = 2.0, + nt: int = 3, + niter: int = 200, + diffeo_start: int = 100, + epL: float = 2e-8, + epT: float = 2e-1, + epV: float = 1.0, + sigmaM: float = 1.0, + sigmaB: float = 2.0, + sigmaA: float = 5.0, + sigmaR: float = 5e5, + sigmaP: float = 2e1, +) -> StalignResult: + """Fit a deformation mapping the ``query`` image onto the ``ref`` image. + + Parameters + ---------- + ref, query + Channels-first ``(c, y, x)`` rasters (a bare ``(y, x)`` array is promoted). The + query is aligned onto the reference; they need not share a shape. + ref_scale, query_scale + Physical size of one pixel as ``(y, x)``. Defaults to pixel units. Pass the + element's scale when the two images have different resolutions, otherwise the + fit is done in mismatched coordinates. + a, p, expand, nt, niter, diffeo_start + LDDMM controls, as in :func:`fit_stalign`. Note ``a`` is a length in the *same* + units as ``ref_scale`` -- the default of 20 suits pixel units, where + :func:`fit_stalign`'s 500 would exceed most images. ``diffeo_start`` defaults to + half of ``niter`` so the affine settles before the deformable part switches on; + starting both at once lets the velocity field absorb what is really a + translation, and it fits it worse than the affine would have. + epL, epT, epV + Gradient-descent step sizes for the linear part, translation, and velocity field. + These are **scale dependent**: they are tuned here for images in pixel units, so + a non-unit ``ref_scale`` will need them rescaled to match. ``epV`` is the one to + reach for first -- too large and the deformation overwhelms the affine. + sigmaM, sigmaB, sigmaA, sigmaR, sigmaP + Noise scales for the matching, background, artifact, regularisation, and + landmark-point terms of the objective. + + Returns + ------- + A :class:`StalignResult`. Its :meth:`~StalignResult.transform` maps ``(x, y)`` points + in query pixel coordinates into the reference frame, and + :meth:`~StalignResult.warp_image` resamples a query image onto the reference grid. + """ + import jax.numpy as jnp + + from ._stalign_impl._core import jax_dtype, lddmm + + dtype = jax_dtype() + + def as_chw(image: npt.ArrayLike, name: str) -> JaxArray: + # Not `jnp.atleast_3d`: it appends the new axis, turning a (y, x) image into + # (y, x, 1) -- y channels of x by 1 -- instead of a single (1, y, x) channel. + arr = jnp.asarray(image, dtype=dtype) + if arr.ndim == 2: + return arr[None] + if arr.ndim != 3: + raise ValueError(f"Expected `{name}` to be a `(y, x)` or `(c, y, x)` image, found shape {arr.shape}.") + return arr + + source_image = as_chw(query, "query") + target_image = as_chw(ref, "ref") + if source_image.shape[0] != target_image.shape[0]: + raise ValueError( + f"Expected `ref` and `query` to have the same number of channels, found " + f"{target_image.shape[0]} and {source_image.shape[0]}." + ) + + def axes(image: JaxArray, scale: tuple[float, float]) -> tuple[JaxArray, JaxArray]: + # Row-col physical coordinates, centred so the affine initialises near identity. + rows, cols = image.shape[1], image.shape[2] + return ( + (jnp.arange(rows, dtype=dtype) - (rows - 1) / 2.0) * scale[0], + (jnp.arange(cols, dtype=dtype) - (cols - 1) / 2.0) * scale[1], + ) + + source_grid = axes(source_image, query_scale) + target_grid = axes(target_image, ref_scale) + + result = lddmm( + source_grid, + source_image, + target_grid, + target_image, + L=jnp.eye(2, dtype=dtype), + T=jnp.zeros(2, dtype=dtype), + a=a, + p=p, + expand=expand, + nt=nt, + niter=niter, + diffeo_start=diffeo_start, + epL=epL, + epT=epT, + epV=epV, + sigmaM=sigmaM, + sigmaB=sigmaB, + sigmaA=sigmaA, + sigmaR=sigmaR, + sigmaP=sigmaP, + ) + return StalignResult( + affine=result["A"], + velocity=result["v"], + velocity_grid=result["xv"], + aligned_points=jnp.zeros((0, 2), dtype=dtype), + query_axes=source_grid, + ref_axes=target_grid, ) diff --git a/src/squidpy/experimental/methods/registry/__init__.py b/src/squidpy/experimental/methods/registry/__init__.py index 99a002746..12192bd79 100644 --- a/src/squidpy/experimental/methods/registry/__init__.py +++ b/src/squidpy/experimental/methods/registry/__init__.py @@ -8,7 +8,8 @@ concrete estimator result (e.g. ``StalignResult``). A new estimator only has to satisfy :class:`AlignResult` -- a ``transform`` that maps points into the reference frame -- to plug into :func:`squidpy.experimental.tl.align`. -* The family registry instances (:data:`ALIGN_SAMPLES`, :data:`ALIGN_LANDMARKS`) +* The family registry instances (:data:`ALIGN_SAMPLES`, :data:`ALIGN_IMAGES`, + :data:`ALIGN_LANDMARKS`) the estimator implementations register into. """ @@ -30,8 +31,10 @@ "Registry", "AlignResult", "AlignSamplesFn", + "AlignImagesFn", "AlignLandmarksFn", "ALIGN_SAMPLES", + "ALIGN_IMAGES", "ALIGN_LANDMARKS", ] @@ -126,6 +129,17 @@ class AlignSamplesFn(Protocol): def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... +class AlignImagesFn(Protocol): + """Calling convention for ``align_images`` estimators. + + Two channels-first ``(c, y, x)`` images in, one :class:`AlignResult` out. Separate + from :class:`AlignSamplesFn` because the inputs are rasters, not point clouds -- a + single family would have to guess which an ``(N, 2)`` array was meant to be. + """ + + def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... + + class AlignLandmarksFn(Protocol): """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" @@ -140,9 +154,13 @@ def __call__( #: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. -#: Consumed by ``squidpy.experimental.tl.align``. +#: Consumed by ``squidpy.experimental.tl.align`` for ``obsm`` paths. ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") +#: Image alignment estimators -- ref/query rasters in, transform out. +#: Consumed by ``squidpy.experimental.tl.align`` for ``images`` paths. +ALIGN_IMAGES: Registry[AlignImagesFn] = Registry("align_images") + #: Closed-form landmark alignment estimators -- paired landmarks in, affine out. #: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. ALIGN_LANDMARKS: Registry[AlignLandmarksFn] = Registry("align_landmarks") diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 66c454cf8..b28e12777 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -14,26 +14,41 @@ from anndata import AnnData from spatialdata import SpatialData -from squidpy._validators import assert_one_of -from squidpy.experimental.methods import ALIGN_LANDMARKS, ALIGN_SAMPLES -from squidpy.experimental.tl._align._io import ( - get_coords, - resolve_obs_pair, - writeback_affine_sdata, - writeback_obs, -) +from squidpy.experimental.methods import ALIGN_IMAGES, ALIGN_LANDMARKS, ALIGN_SAMPLES +from squidpy.experimental.tl._align._io import shallow_copy_sdata, writeback_affine_sdata +from squidpy.experimental.tl._align._paths import DataPath, parse_path, read_path, write_path if TYPE_CHECKING: from squidpy.experimental.methods import AlignResult, Registry -OUTPUT_MODES = ("object", "copy", "inplace") -ON_VALUES = ("obs", "image") - __all__ = ["align", "align_by_landmarks"] F = TypeVar("F", bound="Callable[..., Any]") +def _resolve_in(in_: str | tuple[str, str]) -> tuple[DataPath, DataPath]: + """Normalise ``in_`` to a ``(ref_path, query_path)`` pair.""" + if isinstance(in_, str): + path = parse_path(in_, name="in_") + return path, path + if not (isinstance(in_, tuple | list) and len(in_) == 2): + raise ValueError(f"`in_` must be a path or a (ref_path, query_path) pair, got {in_!r}.") + return parse_path(in_[0], name="in_[0]"), parse_path(in_[1], name="in_[1]") + + +def _copy_for_write(container: AnnData | SpatialData, path: DataPath) -> AnnData | SpatialData: + """Duplicate just enough of ``container`` that writing at ``path`` leaves it untouched.""" + if isinstance(container, AnnData): + return container.copy() + + out = shallow_copy_sdata(container) + # `shallow_copy_sdata` shares element objects with the original, so the one element + # we are about to write through still has to be duplicated. + if path.modality == "points" and path.element in out.tables: + out.tables[path.element] = out.tables[path.element].copy() + return out + + def _methods_rst(registry: Registry, indent: str = " " * 8) -> str: """Render a registry's methods as a reST list linking to each implementation.""" items = [f"- ``{key}`` -- :func:`~{(fn := registry.get(key)).__module__}.{fn.__name__}`" for key in registry.keys()] @@ -58,18 +73,15 @@ def decorator(fn: F) -> F: return decorator -@_document_methods(align_samples_methods=ALIGN_SAMPLES) +@_document_methods(align_samples_methods=ALIGN_SAMPLES, align_images_methods=ALIGN_IMAGES) def align( data_ref: AnnData | SpatialData, data_query: AnnData | SpatialData | None = None, *, + in_: str | tuple[str, str], + out: str | None = None, method: str = "stalign", - on: Literal["obs", "image"] = "obs", - ref_key: str | None = None, - query_key: str | None = None, - spatial_key: str = "spatial", - output_mode: Literal["object", "copy", "inplace"] = "object", - key_added: str | None = None, + copy: bool = False, **method_kwargs: Any, ) -> AlignResult | AnnData | SpatialData | None: """Align a query sample onto a reference sample. @@ -77,56 +89,81 @@ def align( Parameters ---------- data_ref, data_query - Both :class:`~anndata.AnnData`, or both :class:`~spatialdata.SpatialData`, - or ``data_ref`` a SpatialData with ``data_query=None`` to align two of its - own tables (selected by ``ref_key`` / ``query_key``). + Both :class:`~anndata.AnnData`, or both :class:`~spatialdata.SpatialData`, or + ``data_ref`` a SpatialData with ``data_query=None`` to align two of its own + elements (distinguished by passing a pair to ``in_``). + in_ + Where to read from. One path applied to both containers, or a + ``(ref_path, query_path)`` pair. Accepted forms: + + - ``"obsm/spatial"`` -- an AnnData ``obsm`` key + - ``"tables/slice1/obsm/spatial"`` -- an ``obsm`` key of a SpatialData table + - ``"images/he"`` -- a SpatialData image + + The path also selects what is aligned: an ``obsm`` path fits on point clouds, + an ``images`` path fits on image intensities. + out + Where to write the aligned query, as a path into ``data_query``. ``None`` + (default) writes nothing and returns the fitted alignment instead -- fitting is + expensive and usually worth inspecting before it overwrites anything. + + For an ``obsm`` path the transformed coordinates are written. For an ``images`` + path the warped image is materialised: a diffeomorphism cannot be expressed as a + SpatialData transformation, so it cannot be registered lazily. method - Fitting method in the ``align_samples`` family. See each implementation - for its method-specific arguments: + Fitting method. For ``obsm`` paths, one of the ``align_samples`` family: {align_samples_methods} - on - ``"obs"`` aligns the ``obsm`` point clouds. ``"image"`` is reserved and - currently raises :class:`NotImplementedError`. - ref_key, query_key - Table keys, required (and only valid) for SpatialData inputs. - spatial_key - ``obsm`` key holding the ``(x, y)`` coordinates. Defaults to ``"spatial"``. - output_mode - - ``"object"`` (default) -- return the fitted :class:`~squidpy.experimental.tl.AlignResult`; nothing is written. - - ``"inplace"`` -- write the aligned coordinates into the query and return ``None``. - - ``"copy"`` -- write into a copy of the query and return the copy. - key_added - Destination ``obsm`` key for the aligned coordinates. If ``None`` it - defaults to ``f"aligned_{spatial_key}"``; if that key already exists and - ``key_added`` was not given explicitly, a :class:`ValueError` is raised - (pass ``key_added`` to overwrite intentionally). + + For ``images`` paths, one of the ``align_images`` family: + + {align_images_methods} + copy + Write into a copy of the query container and return it, instead of mutating in + place. Ignored when ``out`` is ``None``. method_kwargs - Method-specific solver arguments, forwarded flat to the chosen - ``method``'s implementation: + Solver arguments, forwarded flat to the chosen ``method``. - {align_samples_methods} + Returns + ------- + The fitted :class:`~squidpy.experimental.tl.AlignResult` when ``out`` is ``None``; + the modified copy when ``copy=True``; otherwise ``None``. """ - assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") - assert_one_of(on, ON_VALUES, name="on") - if on == "image": - raise NotImplementedError("`align(on='image')` is not implemented yet; use `on='obs'`.") + ref_path, query_path = _resolve_in(in_) + if ref_path.modality != query_path.modality: + raise ValueError( + f"`in_` mixes modalities: {ref_path.raw!r} is {ref_path.modality}, " + f"{query_path.raw!r} is {query_path.modality}. Both must address the same kind of data." + ) - ref_adata, query_adata, container, element_key = resolve_obs_pair(data_ref, data_query, ref_key, query_key) - ref_xy = get_coords(ref_adata, spatial_key) - query_xy = get_coords(query_adata, spatial_key) + ref_container = data_ref + query_container = data_ref if data_query is None else data_query + if data_query is None and not isinstance(data_ref, SpatialData): + raise ValueError("`data_query` is required unless `data_ref` is a SpatialData holding both elements.") - result = ALIGN_SAMPLES.get(method)(ref=ref_xy, query=query_xy, **method_kwargs) + ref_array = read_path(ref_container, ref_path, name="in_") + query_array = read_path(query_container, query_path, name="in_") - return writeback_obs( - result, - output_mode=output_mode, - query_adata=query_adata, - container=container, - element_key=element_key, - spatial_key=spatial_key, - key_added=key_added, - ) + registry = ALIGN_IMAGES if ref_path.modality == "image" else ALIGN_SAMPLES + result = registry.get(method)(ref=ref_array, query=query_array, **method_kwargs) + + if out is None: + return result + + out_path = parse_path(out, name="out") + if out_path.modality != query_path.modality: + raise ValueError( + f"`out={out!r}` is {out_path.modality} but `in_` is {query_path.modality}; " + f"alignment does not convert between the two." + ) + + target = _copy_for_write(query_container, out_path) if copy else query_container + if out_path.modality == "points": + value = np.asarray(result.transform(query_array)) + else: + value = np.asarray(result.warp_image(query_array)) + write_path(target, out_path, value) + return target if copy else None @_document_methods(align_landmarks_methods=ALIGN_LANDMARKS) @@ -136,14 +173,19 @@ def align_by_landmarks( *, method: Literal["similarity", "affine"] = "similarity", data: AnnData | SpatialData | None = None, - cs_name_ref: str | None = None, - cs_name_query: str | None = None, - spatial_key: str = "spatial", - output_mode: Literal["object", "copy", "inplace"] = "object", - key_added: str | None = None, + in_: str | None = None, + out: str | None = None, + copy: bool = False, + cs_ref: str | None = None, + cs_query: str | None = None, ) -> AlignResult | AnnData | SpatialData | None: """Align by a closed-form fit on pre-paired landmarks. + Kept separate from :func:`align` because the two write fundamentally different + things: a similarity or affine fit *is* representable as a SpatialData + transformation, so it can be registered lazily on a whole coordinate system, whereas + :func:`align`'s diffeomorphism has to be materialised. + Parameters ---------- ref, query @@ -155,46 +197,55 @@ def align_by_landmarks( {align_landmarks_methods} data - Target to write the alignment into. Required for ``output_mode`` other - than ``"object"``. - cs_name_ref, cs_name_query + Container to write the alignment into. Required unless ``out`` is ``None``. + in_ + For an AnnData ``data``, the path of the coordinates to transform, e.g. + ``"obsm/spatial"``. Unused when registering a transformation on a SpatialData. + out + Where to write. ``None`` (default) writes nothing and returns the fitted affine. + For an AnnData ``data``, a path such as ``"obsm/aligned"``. For a SpatialData, + the affine is registered rather than materialised, so ``out`` is ignored and + ``cs_ref`` / ``cs_query`` select the coordinate systems instead. + copy + Write into a copy of ``data`` and return it, instead of mutating in place. + cs_ref, cs_query Coordinate-system names. For a SpatialData ``data`` the fitted affine is - registered on every element in ``cs_name_query``, mapping into - ``cs_name_ref``. - spatial_key - ``obsm`` key when ``data`` is an :class:`~anndata.AnnData`. - output_mode - See :func:`align`. ``"object"`` (default) returns the fitted - :class:`~squidpy.experimental.tl.AlignResult`. - key_added - Destination ``obsm`` key when ``data`` is an AnnData (see :func:`align`). - """ - assert_one_of(output_mode, OUTPUT_MODES, name="output_mode") + registered on every element living in ``cs_query``, mapping it into ``cs_ref``. - result = ALIGN_LANDMARKS.get(method)( - ref=ref, - query=query, - source_cs=cs_name_query, - target_cs=cs_name_ref, - ) + Returns + ------- + The fitted affine when ``out`` is ``None`` and ``data`` is ``None``; the modified + copy when ``copy=True``; otherwise ``None``. + """ + result = ALIGN_LANDMARKS.get(method)(ref=ref, query=query, source_cs=cs_query, target_cs=cs_ref) - if output_mode == "object": - return result if data is None: - raise ValueError("`data` is required when `output_mode` is 'copy' or 'inplace'.") + if out is not None: + raise ValueError("`data` is required when `out` is given.") + return result if isinstance(data, SpatialData): + # A similarity/affine transform is representable, so register it across the + # coordinate system rather than resampling anything. return writeback_affine_sdata( - result, data, output_mode=output_mode, moving_cs=cs_name_query, target_cs=cs_name_ref - ) - if isinstance(data, AnnData): - return writeback_obs( result, - output_mode=output_mode, - query_adata=data, - container=None, - element_key=None, - spatial_key=spatial_key, - key_added=key_added, + data, + output_mode="copy" if copy else "inplace", + moving_cs=cs_query, + target_cs=cs_ref, ) - raise TypeError(f"`data` must be AnnData or SpatialData, got {type(data).__name__}.") + + if not isinstance(data, AnnData): + raise TypeError(f"`data` must be AnnData or SpatialData, got {type(data).__name__}.") + + if out is None: + return result + in_path = parse_path(in_ or "obsm/spatial", name="in_") + out_path = parse_path(out, name="out") + if in_path.modality != "points" or out_path.modality != "points": + raise ValueError("`align_by_landmarks` reads and writes point coordinates; use an `obsm/...` path.") + + target = data.copy() if copy else data + coords = read_path(target, in_path, name="in_") + write_path(target, out_path, np.asarray(result.transform(coords))) + return target if copy else None diff --git a/src/squidpy/experimental/tl/_align/_io.py b/src/squidpy/experimental/tl/_align/_io.py index 6026cd151..8a7c95e3a 100644 --- a/src/squidpy/experimental/tl/_align/_io.py +++ b/src/squidpy/experimental/tl/_align/_io.py @@ -1,9 +1,11 @@ -"""Input resolvers and output write-back for the public align functions. +"""SpatialData write-back for the public align functions. -This is the *only* layer that knows about the ``AnnData | SpatialData`` argument -shapes and the ``output_mode`` write-back strategies. The fit estimators in -:mod:`squidpy.experimental.methods` operate on plain arrays and never see a -container. +Reading and writing arrays at a path lives in :mod:`._paths`. What is left here is the +one write that is *not* an array write: registering a fitted affine as a SpatialData +transformation, which changes how elements are placed rather than materialising anything. + +The fit estimators in :mod:`squidpy.experimental.methods` operate on plain arrays and +never see a container. """ from __future__ import annotations @@ -14,102 +16,10 @@ from anndata import AnnData from spatialdata import SpatialData -from squidpy._validators import assert_key_in_sdata - if TYPE_CHECKING: - from squidpy.experimental.methods import AlignResult from squidpy.experimental.methods.align_landmarks import AffineFitResult - -# --------------------------------------------------------------------------- -# Read side -# --------------------------------------------------------------------------- - - -def resolve_obs_pair( - data_ref: AnnData | SpatialData, - data_query: AnnData | SpatialData | None, - ref_key: str | None, - query_key: str | None, -) -> tuple[AnnData, AnnData, SpatialData | None, str | None]: - """Normalise ``align(on="obs")`` inputs. - - Returns ``(ref_adata, query_adata, query_container, query_key)`` where - ``query_container`` is the SpatialData to write back into (``None`` for plain - AnnData inputs). - """ - if isinstance(data_ref, AnnData): - if data_query is None: - raise ValueError("`data_query` is required when `data_ref` is an AnnData.") - if not isinstance(data_query, AnnData): - raise TypeError( - f"Mixed AnnData/SpatialData inputs are not supported; `data_query` is {type(data_query).__name__}." - ) - if ref_key is not None or query_key is not None: - raise ValueError("`ref_key`/`query_key` are only valid for SpatialData inputs.") - return data_ref, data_query, None, None - - if not isinstance(data_ref, SpatialData): - raise TypeError(f"`data_ref` must be AnnData or SpatialData, got {type(data_ref).__name__}.") - - sdata_query = data_ref if data_query is None else data_query - if not isinstance(sdata_query, SpatialData): - raise TypeError( - f"Mixed AnnData/SpatialData inputs are not supported; `data_query` is {type(data_query).__name__}." - ) - if ref_key is None or query_key is None: - raise ValueError("`ref_key` and `query_key` are required for SpatialData inputs.") - assert_key_in_sdata(data_ref, ref_key, attr="tables") - assert_key_in_sdata(sdata_query, query_key, attr="tables") - return data_ref.tables[ref_key], sdata_query.tables[query_key], sdata_query, query_key - - -def get_coords(adata: AnnData, spatial_key: str) -> np.ndarray: - """Return a validated ``(N, 2)`` ``(x, y)`` coordinate array from ``obsm``.""" - if spatial_key not in adata.obsm: - raise KeyError(f"`obsm[{spatial_key!r}]` not found; pass `spatial_key=` to select the coordinate key.") - arr = np.asarray(adata.obsm[spatial_key]) - if arr.ndim != 2 or arr.shape[1] != 2: - raise ValueError(f"`obsm[{spatial_key!r}]` must be an (N, 2) array, found shape {arr.shape}.") - return arr - - -# --------------------------------------------------------------------------- -# Write side -# --------------------------------------------------------------------------- - - -def writeback_obs( - result: AlignResult, - *, - output_mode: str, - query_adata: AnnData, - container: SpatialData | None, - element_key: str | None, - spatial_key: str, - key_added: str | None, -) -> AlignResult | AnnData | SpatialData | None: - """Bake ``result.transform(coords)`` into the query ``obsm`` per ``output_mode``.""" - if output_mode == "object": - return result - - dest = _resolve_dest(query_adata, spatial_key=spatial_key, key_added=key_added) - new_coords = np.asarray(result.transform(get_coords(query_adata, spatial_key))) - - if container is None: - target = query_adata if output_mode == "inplace" else query_adata.copy() - target.obsm[dest] = new_coords - return None if output_mode == "inplace" else target - - sdata = container if output_mode == "inplace" else shallow_copy_sdata(container) - target_table = sdata.tables[element_key] - if output_mode == "copy": - # `shallow_copy_sdata` shares table objects with the original; copy the single - # table we mutate so `output_mode="copy"` truly leaves the input untouched. - target_table = target_table.copy() - sdata.tables[element_key] = target_table - target_table.obsm[dest] = new_coords - return None if output_mode == "inplace" else sdata +__all__ = ["shallow_copy_sdata", "writeback_affine_sdata"] def writeback_affine_sdata( @@ -129,7 +39,7 @@ def writeback_affine_sdata( from spatialdata.transformations import Affine, Sequence, get_transformation, set_transformation if moving_cs is None or target_cs is None: - raise ValueError("`cs_name_query` and `cs_name_ref` are required to register a transform on a SpatialData.") + raise ValueError("`cs_query` and `cs_ref` are required to register a transform on a SpatialData.") out = sdata if output_mode == "inplace" else shallow_copy_sdata(sdata) sd_affine = Affine(np.asarray(result.matrix), input_axes=("x", "y"), output_axes=("x", "y")) @@ -141,7 +51,7 @@ def writeback_affine_sdata( continue if output_mode == "copy": # `shallow_copy_sdata` shares element objects with the original; deep-copy each - # element we register a transform on so `output_mode="copy"` leaves the input untouched. + # element we register a transform on so `copy=True` leaves the input untouched. element = sd_deepcopy(element) getattr(out, etype)[name] = element # The fitted affine maps `moving_cs` coords into `target_cs`, not the element's @@ -155,20 +65,7 @@ def writeback_affine_sdata( return None if output_mode == "inplace" else out -def _resolve_dest(adata: AnnData, *, spatial_key: str, key_added: str | None) -> str: - """Resolve the destination obsm key, guarding against silent overwrite.""" - if key_added is not None: - return key_added - dest = f"aligned_{spatial_key}" - if dest in adata.obsm: - raise ValueError( - f"`obsm[{dest!r}]` already exists. Pass `key_added` explicitly to choose the destination " - f"(or to overwrite it intentionally)." - ) - return dest - - def shallow_copy_sdata(sdata: SpatialData) -> SpatialData: - """Shallow copy of a SpatialData for ``output_mode='copy'`` (via ``subset``).""" + """Shallow copy of a SpatialData for ``copy=True`` (via ``subset``).""" names = [name for _, name, _ in sdata.gen_elements()] return sdata.subset(names, filter_tables=False, include_orphan_tables=True) diff --git a/src/squidpy/experimental/tl/_align/_paths.py b/src/squidpy/experimental/tl/_align/_paths.py new file mode 100644 index 000000000..c911c95ea --- /dev/null +++ b/src/squidpy/experimental/tl/_align/_paths.py @@ -0,0 +1,166 @@ +"""Location paths for the ``in_`` / ``out`` arguments of the align functions. + +A path names one array inside an :class:`~anndata.AnnData` or +:class:`~spatialdata.SpatialData`: + +- ``obsm/spatial`` -- an AnnData ``obsm`` key +- ``tables/slice1/obsm/spatial`` -- an ``obsm`` key of a SpatialData table +- ``images/he`` -- a SpatialData image + +Naming a location this way rather than through a stack of ``*_key`` arguments means one +argument covers *which element* and *which array inside it*, and the modality follows +from the path instead of a separate ``on=`` switch. This is the shape proposed for +scanpy in https://github.com/scverse/scanpy/issues/4007. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + +import numpy as np +from anndata import AnnData +from spatialdata import SpatialData + +__all__ = ["DataPath", "parse_path", "read_path", "write_path"] + +Modality = Literal["points", "image"] + +#: Element collections a path may address on a SpatialData. +_SDATA_ATTRS = ("tables", "images", "labels", "points", "shapes") +_EXAMPLES = '"obsm/spatial", "tables//obsm/spatial", or "images/"' + + +@dataclass(frozen=True, slots=True) +class DataPath: + """A parsed location inside a container.""" + + modality: Modality + #: SpatialData element name, or ``None`` for a bare AnnData path. + element: str | None + #: ``obsm`` key for point paths; ``None`` for images. + key: str | None + #: The original string, for error messages. + raw: str + + def __str__(self) -> str: + return self.raw + + +def parse_path(path: str, *, name: str) -> DataPath: + """Parse a location string, or raise :class:`ValueError` explaining the grammar.""" + if not isinstance(path, str): + raise TypeError(f"`{name}` must be a string path, got {type(path).__name__}. Expected one of {_EXAMPLES}.") + + parts = tuple(part for part in path.strip("/").split("/") if part) + if not parts: + raise ValueError(f"`{name}` is empty. Expected one of {_EXAMPLES}.") + + head = parts[0] + if head == "obsm": + if len(parts) != 2: + raise ValueError(f"`{name}={path!r}` is not a valid obsm path. Expected `obsm/`.") + return DataPath(modality="points", element=None, key=parts[1], raw=path) + + if head == "tables": + if len(parts) != 4 or parts[2] != "obsm": + raise ValueError(f"`{name}={path!r}` is not a valid table path. Expected `tables/
/obsm/`.") + return DataPath(modality="points", element=parts[1], key=parts[3], raw=path) + + if head == "images": + if len(parts) != 2: + raise ValueError(f"`{name}={path!r}` is not a valid image path. Expected `images/`.") + return DataPath(modality="image", element=parts[1], key=None, raw=path) + + if head in _SDATA_ATTRS: + raise ValueError( + f"`{name}={path!r}` addresses {head!r}, which alignment does not read or write yet. " + f"Expected one of {_EXAMPLES}." + ) + raise ValueError(f"`{name}={path!r}` does not start with a known collection. Expected one of {_EXAMPLES}.") + + +def _table(container: AnnData | SpatialData, path: DataPath, *, name: str) -> AnnData: + """Resolve the AnnData a point path refers to.""" + if isinstance(container, AnnData): + if path.element is not None: + raise ValueError( + f"`{name}={path.raw!r}` names a SpatialData table, but the container is an AnnData. " + f"Use `obsm/{path.key}`." + ) + return container + + if not isinstance(container, SpatialData): + raise TypeError(f"Expected an AnnData or SpatialData, got {type(container).__name__}.") + if path.element is None: + raise ValueError( + f"`{name}={path.raw!r}` is ambiguous for a SpatialData, which may hold several tables. " + f"Use `tables/
/obsm/{path.key}`." + ) + if path.element not in container.tables: + raise KeyError(f"`{name}={path.raw!r}`: no table {path.element!r}. Available: {sorted(container.tables)}.") + return container.tables[path.element] + + +def read_path(container: AnnData | SpatialData, path: DataPath, *, name: str) -> np.ndarray: + """Read the array a path points at. + + Returns an ``(N, 2)`` ``(x, y)`` array for point paths, or a channels-first + ``(C, H, W)`` array for image paths. + """ + if path.modality == "points": + adata = _table(container, path, name=name) + if path.key not in adata.obsm: + raise KeyError(f"`{name}={path.raw!r}`: no `obsm[{path.key!r}]`. Available: {sorted(adata.obsm)}.") + coords = np.asarray(adata.obsm[path.key]) + if coords.ndim != 2 or coords.shape[1] != 2: + raise ValueError(f"`{name}={path.raw!r}` must be an (N, 2) array, found shape {coords.shape}.") + return coords + + return _read_image(container, path, name=name) + + +def _read_image(container: AnnData | SpatialData, path: DataPath, *, name: str) -> np.ndarray: + if not isinstance(container, SpatialData): + raise TypeError(f"`{name}={path.raw!r}` names an image, which only a SpatialData holds.") + if path.element not in container.images: + raise KeyError(f"`{name}={path.raw!r}`: no image {path.element!r}. Available: {sorted(container.images)}.") + + element = container.images[path.element] + # Multiscale images are a DataTree; the full-resolution level is the first scale. + if not hasattr(element, "dims"): + element = next(iter(element.values())) + element = element[next(iter(element.data_vars))] + + array = np.asarray(element.data) + if array.ndim == 2: + array = array[None] + if array.ndim != 3: + raise ValueError(f"`{name}={path.raw!r}` must be a 2D or (c, y, x) image, found shape {array.shape}.") + return array + + +def write_path( + container: AnnData | SpatialData, + path: DataPath, + value: np.ndarray, + *, + name: str = "out", +) -> None: + """Write ``value`` at ``path``, mutating ``container`` in place. + + Copy semantics belong to the caller: it decides what to duplicate before calling. + """ + if path.modality == "points": + _table(container, path, name=name).obsm[path.key] = np.asarray(value) + return + + if not isinstance(container, SpatialData): + raise TypeError(f"`{name}={path.raw!r}` names an image, which only a SpatialData holds.") + + from spatialdata.models import Image2DModel + + array = np.asarray(value) + if array.ndim == 2: + array = array[None] + container.images[path.element] = Image2DModel.parse(array, dims=("c", "y", "x")) diff --git a/tests/_data/stalign_reference/converged_n500.npz b/tests/_data/stalign_reference/converged_n500.npz index f1983b3566ac18250573fa9e83c49f7cb9e156ea..a3a73c21c9c6a9b7e5656c48cada3b60a3f34776 100644 GIT binary patch literal 62604 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 literal 64095 zcmcG!Q*dwH)~_2|Gq!Epp0Se|+qP}nwr$(C|FMl3C+FRJt#xYeRdueuI=yOCyBQZx zd-`wmi$0oyG$KwGG)7@fE0?j#VY6QTeGZ}+DcYrv_J*!KUec&TSox0X zV_;f*JH9lI5GDTZ5_{^)lT^mQk*3$z=h(M1#K>?_jZF_RgvhbaiysRyv{GwXY23A~ z>QRs#WvqiCTsL)88-L4TClNF@0P9PEINO0T0Mw0M4b~Re7btGeP!1N7k4_7oksn<8 zqIaj_8k7^#kO5U1*r6Xf3$vNdGL-v35)TXVYDpr>FD7uJ9YI|*{C%OlQX)mp|l182y4mNa}&;ydyDOW zT;9)Q5%d%0hc(YU#OgFM_L)omnY}|l*-gtjakg@-KroDpj^Xzakar98k-sq`|NBUC zUmnv^QTNwGLR*KWOGwSim+V!X&nKq_0Np*juAEUHnG#qJnhIM#4A1>U41T<7%~b#{%BvvT3LNZ_hXH{3GASQe2G}|EJI4Z$=vz zUXzD>k0nY|-CO9Zr8gPgx6;~8I+f3T*Tn50R@tl~l;3gg?M}G6k#bcq?`LnUPS-80 zJCkHU*~iXzV)vKdRwv0Va?j|x;{jI!-05m@gJ(!W_G?g59 zB!(F&dNIfww)iM_CO1qA%n@iUa?Tk^z%B}6cli2hLwhxWaM-h;d6OIz-6}*u9~)kK zJ(I+|j}2e&Y)$Zd=LjS^DM00-E5)OK|CL-lqec-DvmZj5fKz|J>#{+;U1Siqr7NTo z3jxA632+TX9XOvvP54y84HT%hMFVm|8|mOCX~y%r(k3emxKR-f`g;YI`)WcBpV?Fmf*7%S24VPHM4dcx#f z2oOupR$N6=giqDfVOV8lw?-2-{_{OENS-!0!Tej$1%SVt*&Y{ofMIrEVfn1H{}%BD ztGN9i5#ax%Lgqi+=l{EF^Z#-8+%V$*I}663wdG3~u^M^ac5`Xh*NkQ#1z$TTtYZz{cv7SzMx`rf zxc{vRFI3U(KP<5Ss1nk1ayGCw`47?fze#-mr$nRNFr)t#Mt_@C82e5E*s}7v2*KTP zHUH^3&Qa%NNayC=WL!ImxP$( zyDaqoCv@#CNGz!24YL45xO&6=35@qa?Q{s&rL?_Y|GwfAUV8zr0~vLdw$)&Z?8Rd$(twh z`#Vv)Q9GX|XLQh%l@U_pa*=`rb`bFHe>-yOTN+Clk$|m!Sn5!xh|QNBzZY!_<8u~S zPVpa)1$ql#F|3-e55(F22*x;4EcnEe(AI=O*D~$AzQ|-lgr@FZAsd<#=>lB$Mc6T- z`LH@2vjl7E%HHw5!W`GvyNa<(!?GxW3 z5lBw>@_!!1GjG@2Bp!K@gUIhr^(FmL0J(mtI$AhTs`osarKlnV$CR5qIU`7hJglQr zMX@6_=satA9l|x?R1{D;N1lc(J~rHud#hx0KR~+|pCc@gVj@kd(SWB{_Eq;upN?v5 z+Ic(>va~Bd8VZ6y$8?_iPI9H%E4{i zJHu(5Ez1vO8`UME*c|R(@FQ-w)rLMF{%u2<0`Biw^V%PS@LQ$OV^KqB78qg?HjEA7 zL4P?Wfx)-tsh>fC<4s9FK$2){rT?@SQTW3x88N#6^v0gVwK7*H5NpIFig)I#e)jOC z<$9Dofc;vaZJA#2Op6ISr&AonWK1n{E;~L@W$ML=sqT0FM^GqAPcgR!4}w6J`!LY+ zHm!A53^vkm?U3s1GK`^K9Ej5g46!OQ!om5&e%CB^ zQZyH9i?H#b>*?RKiRzm1(nal~gC!u4J(7r492G-Urx*;6zn2T*=)xkKgrVP+$K;x1 zEc*jC4ZIpy3KdivZ%B+Qpxe|{Ai-b0NF>2qWZ@VphpZCBIsE8siv>Pe@9z0k^=W=@ zE`QQS=&)G69<}k>i&+!!H`9d0dY0~esKcF9wqe|0Oza_`DXlv`_`8^fm_Bzivhyh| zQgKOvNAA>GJ3^~)e#DIJx9pMbTNms-Usg9WrV0x5vk6G?4kmRnNXug0WPkO0EZOI# zS29T6Kc=&0%jzk1BlF8v-{YZ|wWtRB$XoTUzxR?m!te}t`6)r1=Q6&W%Qt~_()2NS z%cz3)SpO+C+(iq_o|;ReNPG4_*xQ$9FtON2HF^%5fdxgn=%I<@%AQY>9Na#K+{^Jk zQp9g!bQg&EVf7qDQu1M4@s*VXv12c~{4GR6Isds=i(CSQGk_hyk}baX9&B9f$AAC_ zzOnlQYv0GkSqi}>sMk!@VlR_iRih z*ALx+y!-|9i6tHqe)VWkt59wL@knp#6rnU@K<$x13-P(iq1bx?}}GFi<=kPXEaOI3?|szH5(PT1zAwt~aiI6|*;l_Q#H#lGWdV89wrA%3HGPUDp1`K%`&p7xh+}wno$Q z+gM=gFH(VR-3(F_u^^Y*+>gKFdpAGJ$MF&D8mi1Vi!@G97H@Fn#Y09`dz4{1w6m|0Tmx+Tx09m(NcMV~*Ic?!P zBU7ku1>vvH4z})ZAuT_l1jIoWf%E93je!0VI(!6fruen)Z}I(9zV13jp8`sHSh9&c zxfS1LNU?v;_aGs%c68i9aoN?aXSy-xwb&D^?0$zuxdOeX|6jdqjC3d|o*I z*G*qjPB}avU96BMdp*8c=48OgL1eG@%D%`_5lGK6+nBXWALe-U20p=haGWXE+xf&F ze7R&(#S$NFI2YxD2Y-wyWR0B0E1^AB#bN$TDERKneNfJ>qC z{i=Mf0nMJ#-|1x+Ju=5&9N%oEwvdWTw>|tryMycY!fjj&$b21mk`Fb2)a!@P^~klH z1gGu7gD;(TntkGqPMFXnmOJ=rGsAh4AY7MiltT?mHKqcf3wI8(N~FkoXnS^kh1I;` zHwg$uj;GgTGwuMOCYaw(2eo&$5en;JWAIT|Y+#Y{SN@N^@V3ze?x1N7Gh*&(Nb6_f zWpvx&hcQtKWL5S z28f4fTnH&57J>y#%Mx-8(dEI?9+JAL$~&j|;$8#rS>qhAIG2oSkr$c8W-I zAd1BKBXnxaW`JVqAh);>%<`zZTCe>GXpLVTMNs(H7?;JGD?Vp{KW$sZMLzW?LtbWO znM;UBaQ`F#E3^G`i&aC#467hd?)Qw;49Pj7Qo*9|zzZa?JDFC`5Hu8lVm0Cc0<{C@w3_(!oJa26OA zsGbk$IrqoGiA`wah`5pBR5(U4!%dpPMeuCo5dN_k?1J;z%=I9?9;u@X^`2;!5sd1} z5syrKqyg>Iy+78W6q4sQm#TdJrMzZ003qdRm)#OvI#!#>7V%797egNT&;0+$EDm=l zs=rQ4Fd^%efZHwQI7&if4{)4PihPA)%TSc-$#z~qfxB^PP;DgV$9ELwW-s6?oj3MX z!i5juqz%ml2HWUQSqRB%#DP->1-Y>5{Sj)r3Cr7dQwvwOYY%t5ZSog&eV1?@IB@42 zu(%ElM5B!Bbb^GaC|Hw@QYM9N-5cLNn8P>m@5FYhPRv2Dw~*@TTAu{-NxaxBU(4>V zTa-QDECqL5>e!XDbOgSK)J`39w2nXTo-1qBR7b);Kp6IGUH>Z>II+8c#&0S5IiA}l zzAEfFx6?FBxQ;QNo?#eSwo6Z*`*{N7{A26bR%4lex-`_NdEHt+BQ(4DZWKA*SHy1k zzKo7w(+5R2uNd?O3G)QXiZCxWnlLAA1-8K@;L}Nrx8L?U!+S5vl8z{KDh^ALZmFKS zlu++)WKfsnIFmz|Qx|elS;vK0`_PK%$qho&qNr({27RUL`x0?b!51E;%k!)KG|G9A zfMD%))Blmx)1=j0WJkXsL^PMId%uM{bF&OAM^;|9vzr6*#S4G#;0Vm%w8H*JNY%+* zkw_Bc>?nli-y1hH%V0DB3jDEgava8A!7<*RrF$UnlVWvHU9Z_{JM56 z)ZL@m=5SgMhx6h4NE|QIC}E`$NN53=vhNRnS$*@!t(%86o;;VZ-)W8Yr#+^V2It4=8hMXI!i021rQI+!hlXtoJFNTd@5OE;)NB z&p|4igO;!OCfzV5We3F0jgL_%3!>vqA{zF$*Xybg#zfL-Qg86jMlLq+za9!MxRiSm_K(J`YVG>qj4^5^megSHRi7yrMH|BDyr+ z`m4GiavOc}+f@MJ%LvZ)c;;nYWM;p*@Jg=|f<*rl>nZyxnJ5|$w;m^tDfm@S`@Ts8 z+ulRjtMVv<=##U7MSG6~xazusT6R?Y4q!EtK2z5RCBnTsShjm1vR+UD=qXA}cdKgc z<)1{tp9YX4#90-jojd0?wn2~c+TIYAu#XlemH4I6&IsKeAeYF7lHG_>wS3pK1a}{) zQ9n?6$+-C3^W|-0S8k$PHu>@#ymYfutU9SN7UiC0x&xr)z4^2F>)reaqW=siZvN`) zDZ_ntT!ES%hP=6=*Dy3l3>XoCwTTySxzFl}WlyAA_*D0m_!Z3mEvJ)u5cY+*OaJnn z0#u~8jn4vx;_hC(Z!=SjDY`K=2N%of-#Yar4URb6pImW}A=(8S|GlT<5{n8tb(Pl0 zT&KN%KXjDiBzAzrKjcEcFaRD4Y2JoAo)oxPX11Ff%Muf@KLkA(WT7#J*_rb5*o z>o6&$PnoR@e&77{3%ryk)tyd_e)q8L*JtPL)1hM^AGMz#-Aj7A8<9T=;GZgD@){K2wuo>x8O3 zLHD^??73^#_Ir-Uqs)EA$f=K+HUnkOVbYYGc4 zAp+XCyuZd|;r@1?GXSd>w0wr@DE?$I{39uthnlDf!jbP+el90yB(ABpOG?66iOe+K zzE^7CjcpEO2zl8jqu0Cn$0lrz3xV^N0w&?}3@0t3BhC5@xAPlVEZA;q1uUZ5B6z(# zy^5;wmXYKnEL2rw&ASr189>J<@SJJ`3?*76NWh6mRAmN~SRK^aF<6~V?K z3Nt5G)w1B8hCVCzvSI8XUreW2j0d(@fBI;OqK?12aVQ)zl}ihQ!Gw_N5O`YSFYr~c zj!MFZofdV7MWu{oZ#nWodYJM6*s?5#7093YM%FEVw1WZPK2kFTF>~uC!kt|GFsxq( ziNEN;+YRH5Bj8z*;qh5}&!_@tuUrj!qXzA*q)r@0DA;%}sS_QSHL` zmB5cDMUXl~jc+`Zv$5LhI+qy`}sGU{)Xi~chVuK&hk7HG09(uBBX&dFRCsJhO^a#4P*tf@pg|Q_PK=HH596&AmWJjKy zu)nHL@R(TcV%eHYNt!`v6=}uJ*T#?Gn^i@(c%^CW&8%grh~UU6m@e5@D9oX1WlLC< zGh8{Wbr1|SdWWtXRq3=Z+kyj>di}ok%3^2$y7WJ*>;Z_kGwenD|9l&r&?aw+I_+O% zjSMN2(K=VQcE6R5_H7EJ|D-P=+!LEjONg;ZNtWMac6IzPD0=3G(fLFXTuu^$a<^LS zZ+>;5)gdP@+ztQ!-m2wQXeA{Pq_2qTfQwH0F$CX3Gtdpoo`xEcdbpuJei+c~3D|p4 zQsxzypX#h10TO_}O^AT`05wr5gF7KtqfQH7=2zNM&1I5#uu zc59$&E~}HTOjsxJct z>$*)ejA0fl^UI!d@{bZjBVP1!aWcM6eonAskH4q8diugN>ZZ%z?aDAl&iZ8OSfL|XR(8v`Pzl4atc7Jf~hdHI=LTOBW!Wkunu(!E7Snccf5nOhV4V&V&Z z?x+Mb^o~9?0mL^Qy~iBu)gvsa*s$i8e0gf$}H=7b`KsRZ zecgyQ)MC>DEAmNXSZ*uJdBQ02OxfCvvZ*Agz3zjSYsep){M#qLp{hgHnbY`5xQx@*O!{86)coLfL}a64O!W@8%r@I=8A8E zN5(jVx;R%~nO!Hw@|+=U`Mht!=rD=uFA=X(9g~+w{ZOzWamN(mOthaXBdb)wwmIp5 z7Pv-9Y7BdoXm#+nqKkJg%Mg2`gT0eIemrY?<&&r8Gjj6!gylaLScQg?5|_7XiiEGK z(ifHK2RmCoIy~$(5G7TY>}%e%@$6rkD1Je}(+QA~8Jb%AhfYk-w-C{L2hQhx2OnzX z&tVe}=Hbvs9~I4ga`nTXQlXP&)a3T^PZG#Qm0YfKwSF?awqT2y_{xNafsms3OK~uu zDh_jiZe8f~gyH8her?EFHY)0FftsLSkRzOrh*bfiq#`_P`P8$Ia>EE|Q$n=f<|R7` zBSeOQE#di$#^run9>m0ffUx(OENb(9y&Q+!lmKA2t&s~b(ys9Lx{FPcK(&ajg0iZj z*R#9D(d9*2GmZrA#3!3kH2Tlnd2HD_PAOV$mG(XcZ6Kzp_l|aMDr$%hg7#dJ;9vJ- zXu{UG8e~7oG^+|gcJSHAOm-+@HS)rL0wS)~yMJZgKlTvw19=QCnXwF?1{a^!d9pGJ zJ`yak{YG#nkwCeovh*iTQy~bM_#Je$nb4F*9K%@VqGthChJAIxM24HlIKns+j6;jX zeRmr!0!T_TrPLsKaxh#5(lp)}T3MU17YQ>xssZm08QaXF@{>{g!Z8}fAO_|wq;-*x z#w>~!#Tgm!d7pprRIE_nMxtY&i({}i7vUXSSHx50`_!%($4V)J2w^Rn5;$jLl%5qm zQcfeb*6pOYw5|KCjPSj|u7r7bCayxRNYOHmNnZR+{F%g&^8x=Cm;%dg@eo1HmEGOTX0OP!PL9sc$w z#>s&v*hz*iZt=5Z{ysoaeG(R7{K56yI6`sY(t9%~j39C_vrayRsE3`~?Dh%3LyXv2 z#jyjhN3qX|7d5-7h8fdy(CuY0374U$O{-3_65g{maOuuv;;$-RX^3A5WY`QIkxd^3cbY}NB3#E2*h_M%G;XjgKujf1+F}PKL;GV1_{l!xgzUKG|1^A={8(;)rXmTaCZ)sa$xocx)0&FSE z@lPbOqjZRDXyKf~q*+ zDf{@=iI=-6a{+dmenMVn}*6Gl|Dwa8XMEeeYX>E}FWJ6E8DJtg9HRn-{wj`o?M zmy36yAojQ1p1+GqHQnqU$+c=#c(+tDYe0`WzRpGPr?QT|l+WoHLsKbmTv^=nZ<({= z{@QfWp_fx_Oy&WkFSCs|yQ@e|yEF*rZEoV?dwwV{aar4JCe)DAi-2MDU?irB#wsJn zD7?=G#f=sLhRDnr34@mEpS`6xI)$I1Sa6|CHl;z8er8W*BWAbQbp1efCEzYF%2fIK z#WH_hxt}Odjx{Lsa~*0J>TE#-lM)$L*x+s7hpFXG$9Pt^jjU%PDn^}&^KK8dUz!;> zpypCoM6Ps)yT{;Uuj{V7k2{PQ*`9ix&fHp9)fsC6?IO;&q`2P)an3 zgLhJQeMU-wL%)8L9xrdd=0OCgDJg^YYMe3i<0gq8^E`8unJxPxarhREOM~AgPwjeM z)q<=tdyJa7EH@O@_pzTg@n%cw@)tMPgyIJF{>5HJ&d0?0AhnUhLP>Q5fa9Y0kG>1! z*rgRW_%aF%{0OL-C&)j%Q%uJ(5!0Qb-X;#EdDY|@@7ny*ic6`mCjBOpv5~WerceM5 zSJ(ud!_>zT~oQBhncX!!AtoOcUcY=6H%fussIzx}iy zzu*}darg$j{&JyQqR7Z{7X+DZUC#c##74^5>qc+O15Yo5rASU{&gY*0(nq@XMS2vl zoqW>8`5rl4@ts5kt%~noC(^;oUubVJ;$VQ~u9Ef;@p^ptomro1MUXPkOT$C=Q~`m? zii(YzSstY5*WZ!MFsY!cO^vr%1C1XwaP(q2yq`Zkk#V4>$+)m&J;y8!&1x8jjX-5S z@Ugh{HG;9xpt}z=)q(87Slo`?^YjNK6ck$ z$_RlqT{OxZn1SYp?QI>gVseC+pt|TFQ~~H%Abd7fLMwGUVTx;_po*1F-%1xEy)ms5 zC7vGn>SYLzaxO;T=^>YZ9cdyl*P(TFXew*Gf}R(FS1z1YT}R$qWyriUT*()N>Q5Ow{}0`wCnuUK-;ZoVdWHR6SlwQhkFP4MmRyLG@1 z+}^{q%-C2^^n9U{f(8YCe%@228sy!;pA51}5p_5NI>y=`?KDhnZBk^l>==EWfgV|B zErirH`f*=GCXlkx_yrU@6+%nHQYE5oBx=1c&Fz^bAf1-`2NxgEyFC8Qw;3TE80m}6 zF>yA+&n$+a_E3c3Wb_;;ei~Ry;>SFCc*}uK=YuLeBij6qMVj{Ru|B7kq(}7qWcO;N zo{Q^ zLo!%j%_rT=1#=*%fTL}M8B)Z7@RJD1OH9F(?_=&Aozi;m`8sc>El4uKt5}?QgUDr7 zJ_|dSso>SS%MDBsIV?4oxczs4nED_j-Mq5K-zbVD@Wim(-K56M zcrMS27wcYRT5=7m>+teGEj!PJ#YUuUuah1wJO)rK#My_0KMg}7!hFXbj+0;5b4ui9>l$DdGx9-d6X;+yt4=e3Unz24JSFm*B?WxaEJC zN*>+{esOwMb4BSI4{OjT;MT&CAT7Bmnzk7A3pkcpUf|5^rPej=@WNxMO_R2lp=Axp z9h@K3s2_^jiS65BSV@obd-qg~`^4O}n2Hrs?O<2;K>$3GM!MC)x&dQrW)No08ugcx!X;)3u`-%19!= zVH#2Ee>|DL?mDPsL_YbM)&ijR*027K_)&uB!J>&fQ3lJO`1Ry8nG&QWu}hGNwjA7Y z(Y+Rc5p3{rw4lw-!*<5tkeH%C7Nlykkt8qEoS#BhYCN9_taF$dQc(vaXqT$n+CbU~oLsPrMu) zb50w?oiELP%kqgWE}1|Bo(nFg6|o;N^VyDCMyrKzcWWs79H!^=__j$4k9 zuB8kSRV1|9N7;<(QTBkfPXWDn492d@BDd%(r?97;;@k@(5oe+&AZK?69c+w^KT-fK z@K9=OnSeGc2s`EVUR7Q948gfzg_$FOdhpy~l#9Cj+L2P*#~dyKWNeaWxHWu8Z{^JH zG(+fZC|&%Q9R74&`LdsiCA=OWuR4tI7q5gzXLi&Our5<7U|$wxXexUOLl*^C=!opv zDYvfh4E@(AW$iH(uncQh%ng*0=B&bp3nC<^+Jx((s5;O&8_7(`^Oe7MY3tht13oL! zk?Yr|5j+4aS@e%M*ZtRZV-nGT8qzEY@!dHq0}QmqWgP5!{}uQ*>;tK=IfwWMNBk1{ zpuN*)&55qBdXbgh@cNLfbWqY_0rGgy!dWWbDU|b#DlP57V){pEmL1;!)}q=DPbOfJ zFwgcd>?)|Xo(fmJPKhk2m@3a>({M+hs+2+ftOnKBvkKrkzXmVEd-LM?{cGs@vU)I* zjvh%<{XLmS1qpIj6!kHRYrybmjQ?1Qy?Ny6&;jsEqQ7H1?!Ra2mQdP*Pg%%G3vCpLri_ zxC#C~NMhsuV5ol^0$D=RBQ&o=d!6ZQyif0V##Lck0N_OEkFplBMvqt10Q^m1)ST?} z`D#_dPeCyT!AVA&+=WG2zZS!P-*?(-A4@n*HD2y7aE8wp9`6Q353}p$rmdow>iyVs zFr$@QblA;{sk78>pAHvi9$ zV`3h;{x@FrO@^OsgQ)G4n$A(6FVt(;HRYXrmP7~XH3>+D5&TQJU3&Z@Yv0aZrOOMD z(@5U6Qg3x=-+<jbBfZI!kD%5zOK&6J;`idY z(RE&$AS?U>3S%A6NxzTm58|6cd=Fm^I0Rtxw}TF#Jv^++$9Gbin^MGjY4@qOP;n%G z<42D#D~V8iyzP6{{9BXmZwq`m&SBAe&)>TA3X&o-i)MLU|L@@8Jese7ks3#3b8(U+dj z72Itw9xbGks+HwYw`2SQZk!B1`Kfhy>Gz-L9&FzOlS0s7#bGGR z8*kE8f}FQ)fqkeqP-bV2G10vaq@K{6Mp#Np+8-s!q#*L+xFFd~+q=W)pxdmfNWwh* z&Z3bk1Z`OX);Vo;|G!K7@Tg-u`ZwARTKaKt3e+Ed+r76S9ZBCA+dPH}U%r-@^4tM_ zN_(rtv;^spNhwkr-2P5+eRAWdTxaYY9PRB|Vp-f#~AC$Df(0)}V0lJ{kQ6;R;hl=^bl* z2(RstjQ3eYJKsirDdpMutlY{K)-y2ANq$!Aye<$UPc$vJKbr#=&qgOX3`OgJWq@-vr98B>gV@;R^IrUFY86=gTkg z)WC8Z2d-51T@<&j0Bzf1tuwBm`{ZHQ_F7j2rtfXuxP6|5=|wW%r1}64=;P+3?^p&= zO49M|rp6*F=Oid!@w7yE;Ftz&O!iQS$-%UAUcube-F!Jy%tUTQhL&^dlKYZmQI<-TXF#c2SOO0wDQ?rGmV3^R&P z+ar0}Hqu;V%{FpQu8={Yo9Q%%q~14`BhN+-yOMpxmo1c|#X*|QJCxD7QBq^7F}S?qmHC;81k=P@tu4)O4~Ec3an=~* z&%D(#sTDM;Sty&Vx2)hwtA#r0IO@UB3=}EOX#D%3_|XKmKMJ4Kd#~eYy$6f(`4+Tn z{0olMHIFTr8NRqKA2&H@4S^ik3JZ?VJ;mG-VmyAV+w{W*9!ns54{e)>?-eFZ@k-8% z4x)Bb?)_y7e``#Wo~iAH!2r>VD2tv^eO!}rPKQAtev}H;OjoJCtJTC6 zSPPqYc3RF+N+S^Lahx_p8eaX+=&*;7j`VsvM5TZ)Pl2iV;NPo~p3f&&UBvm1~)fhda38W=EQN6xTF{ntF zAM08124dNGPvQ?=EZ%7A%^QWa5cCAK2+&LFy){Dw2Pgw`=iqMx|ysqHcZ z`#6@h^ORSFfs4ht-c)@F_aDH~O1cg@p(qTzian2iu#Ds&9xze)$!dEUr5QW}%Ua>j zR1%o=nMntY0SrbeePwB`ntz@;RbPetg-EATvspkD4C+ru!rWRJ98YPMke1LzzIw6! zf`0_$_bwY*qO*n2WGdj&L4e$Qt^Uc7Ng8||QptyV3yn!bqB8C&g&da)OYuit1W}st zDAOPnKiJ&l3n7MiptiHP5Ox-5f&1lkQ=p#i zIeYC+8jsKvcK7)%nQfTI*<0m;nE9UpGVn`_CYOVrgoSbmHSAaMIQ30QnEsW6^W^dU&ds*QIdlz2nK)~niTvNUu({qCPMid@HGf+|QCv*egQ{*pByXcu zUpae4=Y8(g{cppuNy+KHakKWr=+v=**<*WrM|iTAyd@$2Qf;h@fd_mf${Jg4af5e@kZR+dbtNoX4} zR+`MKbH%oAbvij-H07Eu6K*k&ryMp~v+H)08aqBamPa^T1gEFtgymhH6R*y1dd5@5 zxZRLJhw-}aZ#~Pk`heza{@jvAdY6bsZS&-KG)8miB-^Wn9O7w?)5_GlCnp%%D4sMF zl+1`Bg%=SfbMMiqB+=bQG@i;?Q!=S(xKlQxjQV0d&}+c^=v;+H|G|;>i_9sIL$4O& z0l9j<{7mg{6C4GlS|jW@SyR7*8B%wPo10;Db zp41h2(49CTR_$O^ybKf8AF%j9B_q_s3vi38a_E>Hvb!JvZ^OR4KY1oBt%-7^+EkdMGS{_J~PiIi(zz7 zoX^Et_C}$N*2%f+Fjvljl}6joY%H;V#&A|#s}(bx)yeMsky}5{Z|{pZM)CxHp7~-} z`zM(n!{V$|5>)u9Oy^hNXvlYmx9?Fv^g8yoc6Qm#lh0Odwrru4dx9n-s1YzvY6dBI zt>8(1*pemUxUUAJ2bM@Ea0!#fk_`qnfGvJ=OHNke?tQEeWd~Yl6<4||?>%kV*69Ig zITvjsAkA%yEy(@^Tp5q;WaY^sk!Sf_iOCk6SS>A$WrNYy*5uKA82~|_f5j|(EZ>Kw zJ}KCCQbW~rRs>&59W?3hQowu;&R4k_NJoZ(fABV@@Tb-O@@sq4`s5Y~$*c~mUCIhC za&D2G*yyJl^iv)9TCGz__!+=LxYn|^XF0SN1Ay{PKSEdMk{<^!bXdCOj^Yr(i|nnG z{6XNvA*5JrHH!3wPhj{gloTo*sJ%~W2G>oi`4;?$g0>;;je08A*emPP8>zcm(KBqx zk}Qg#Vn!zBp|`c#uK{aH5|V#f2%68s1s|o9qtI@Orwu*t^WHqIsIgzwisx?-!Ctme zuB-KJl<3Q^vKS^5N#|Sn)1h)3E`hjv{b#uuY-@IKEud7+UH4S+xJeEtK0nn6bJBS@ zpd!DuZHax4##rDpW)oQ5un#+X<55+QDyo1(pqk0pjFa;^42!lY^xo}saFKV=G5lt^W~l=t&t3H|gSdUTBp}~#ajd_!)NrXZvlf$hD z0G1a{e)Bgx!7x8t{o}P8?K_iCnWj}9TBXiyT9??VA7zTAj9?wuy8@rLLu)TQS!iFS z!P!)Bpj#-nHPr%|;{*fj+oC zhQK}r@&rn5AO`oJ%WtuplBzMtU3F)5wl`9Tr@REK!xlNiwH6dH6{U}R`{Q(xl0 zhFUT?@i6>~4HV6xSZPB+=|$m>FqZ@1%_$t&A*u06o=d*bb{X8#59X(789XdxH{_vs zVd^{=Kq@SU{_U%i!{S@963A zCxuBhmQ~LFDo1}JfjWI#D|CSk`+n&MI11@~=R><{AXYf`cY_cEt%J^hb^)_IRxN&u z%Y;VLh{QEhg3zCLL9f%KfX)=qlMtN*vs=bMUYi*`ZFRhPx96*dU}bW9Z_{1Y@&Wv| z&N&&Z_$+_XRwd5cR``c?clK*_*1$JdGA21H16?DSC1mr{0s+cLlIt>UW{g zmT%|Fm0*HY>}3lY^SLTj$#Vaqst2t8UP|6jqKP&!b3*3J2OYGTrj;@ea`^63J-R{K zdjsm~Els8y9sU5B)ABFt<^ z0Ti5Cx~rE04ZhyPgVbZ5MUtxsEx?k&JEE#j)2UGZ$0pVy8u0tHcr*3A@hd(JhSv_Ns1T8Ab-*|)rp7K&Y z@0v>4o-rh-lhx~+4yk$7FNlgZ_6=kz2^dz|Cc30c49I@RJzL0JwBZMhxgRNRRCiyF zuZOr2MAa=F-M(Jguz16U&qEh4he67A1U>CEv8FpR)~ICF?XUA_BC#wdZ>pdtT{`7yk1iQt#3a0*(`oJ>Q-t7VeVbKmH|4fs%VlGRwr!i$O;kbzM6PbLj*S1?sQ@kq3K(G~cAKh2td{hZp=! zQr<*-U!9=;-$$4MLkE^~S!6E$JvcTR&ih18F?6RJm^btVzxnp%cOEqU81iRl?c?(XjH?!g^`dkF3h!QE+S2=3ZI zL!a-=%s*?*RbA9wt-7e%d%w@F?Gaj2`y}KU)Tu^SHShsR?1RxV@A02WG#FANr2(AS z7<4&=nbO;K9n9H=WTep3!YIuf2YU9i#3KBh@+x&>cVerVq#^=W|f0Yv(;;4(5l`Og=V>&b$u1? zR>`!{VhJbil8V@HQRXLE^a)UvBzQ*TlO?k(j)Z@?h3-~1g9hvRz~d1-L$%Z1L7LA` zVllEFza^%fhiH*qvA4EZZoqf=mzjK`NYq%i?R?byCgwKHx0-*cLb-R!Ew>9l7wP7g2!6*QgRjp zbtK{?9}9NKGB>tOKn(hRdQ_)0M=TPOWN9Mwi${^npb8CRrbcu83i{w#PZ0&s=Dbu9Y`+K9pNFD;4wvW**<&mI(&rxNve z7^~m8%M6=_{$}?#G7p1;xov8HNB&xPYF=jdp4Bny$W=#J4U1OYYP~leG_B#oa%XLT zri@0U%!NEbIRjHd$R>ma4!;b}C$BPHBwb55{fZ`QMpp3FSDILu^=FH7dD)a}=*|i{rEQXz8E@?#> zmNxl7^;zS8@iM?C+3Ipm3<9f*VrQ}S_MqN~m4TI<7Jk#niFG#O(jy+^SxtBlKX*eP zJ43=dSigfQyDv@EcRE09Hd_WhUL{B8mGB;RWgrP{)`V|M`(1w@-C(o7%tk(yWQqgn za*%B@%!Of0+$6W!XiiDCM1hd(K(QVWb5LoZ!?7G4^`A8r#o@OTafQM;lRV z68G@ZLu6ZcGFEq^k?_v5t$$uj?%a*j7Wo z7IBfyus4amIF})XRp)ncY!|cYIi|`ncipC0^Ge*N(|rOzf5-2XuKP)n#Rkjnq+1s- zV++nYxGua{&G>x1TKFR0PvjO@5Q zzK2>GnFG=nFOgrJ@D9`YGGRG#O&%`0-OY-y1a)4%1i6IY30PTXiq&Dl#QC+u5m3Jm zSiOwC@SWX2TQ~kee|q*PEkqnX=^{94 zKW{PZCk|?hTEaaPpJj;qC4jsh1l?bbno@Xw-X$yjt#Kaj+7_|~NHf4ZN&lDcu9PWc zIBQBQg+ra`7Io}r2&;aSS?2fGF`czlG)(hPV+jVCR1BHZ_@iEM`UUI4wi%R5it2-v ziVXQSg*vp6!UBtw5f=#N#oj|=8ye|s-g`N_L9JpcdtJX+42#jO8^?2G_+}R{m~rg7 zB(c5+**}Fl|91DN|C~3>%a|KtE_e;nKPEqXcVJO%!&eO5|FD~y1)!&R9r6Z9z@?m> zirPM?qE551d84FFo4Cf+%=jz9*c^7#v$^CByX0$x@{4^&pZTuX6fTe;*G%)E{^fh= zj)0oSB&x^s{=P>oKtnBRIY4J(esp?}`JXwQ7J9*4L8R>7jjf9Ts^a&bI$#T~bh>4tzYu@O5 z^~kmk#Wlmi3-H-!rT0%0*oYb%v}xUY$}Q6l)q)0$HMJ8>{SksV*s zTnZ7N5%k6Ye>lR_;u*-w)AMc`4|8zHEX~0HqUU&98*G3|JvzzxH^j1$_eVv2`qOeD zvV=P+o$sxePj3D|%e(qn2e~&E1s`oSsvnx;h>e6p_2za zDsdRQOT@g!7DWC^aH40F3kLK62dvH;U)u$O)#n}U`AxERi)`fseyqK=Q_$ z{S;}ir)KNSYk~+ds-3<%CApzGVG>c?g+UeezgWYep$L>6jGqz)FP;=P>~&ijhPRPrvc{7PpK8N z>fxJV6d$Zy;DW0w}9LTco%m_%B&GJOK-(l1UMNETINy@%~8xpW<6(Q z(mI|5Mz5n$;eXF;uDIDtHbr>9n5^*wCSx{4~9Pk(9`OQFsH zb#Kp|$}vr`=3|z3MUFr~mawtY>IB^HTE%NU=uV#YM`0=^6SJNW^6GCuJNz0S1VJGc z>!ga_U({t3^H3RJk?oN_TufS&#sgmCci-L2}BiXM=@Z9O|grq$mD}w*9(76%GDQ9j35tEu zL=ZhAOZf$J-ptL|%$!iw>vsBd8{pscr?YeAVjE86aCv=aNYdX0M72xTV_YuOuCg9| zyD%WPCi7M`kC%{<{Xu(m2J4DD*T~QuTXJIhZ?MaZubA}TgAYAzD%It*yCTg z;O`yWuXx*jKj)6nUv{p`$dBYH=SKVQiH;3Ji~I94gIt*D=#ATYboNo<0{kR?STfg) zK281#C>uOqHJwTM!k8~;GU9ez{CQ%U6g%S=6I}DrseBklgN-Fosj56t*ES(wH1QNMOVf~|Dt+;AKLfo{$kvb2(GwdK$kM~*C z{E)z8$CGuH)IhP~0^`f4BxW8=t1J-{<65)$Ra6rLrh}26mGp28%7+e9h$IRd zFj+_VMf#U~gNF7|Y^5^|O(R0TXVO$Lg129B%T2COt>gMtCi1FF38e0NSo5%HL^V4( zav2jem4yob?0AQ97`;4Mc1v~Ht1RB#{6+yIsbbJYb1x9e@xCl3UKO)L-OO$t@n4tN zLo=O53n^r~WSXjse+73i22j{dW7DZ1e#k1A4D{wfzr^o6hAho7HU?s}m|bel%(ubv zWj9VeRH1Vvb+bnnUy0P{nG1n`NC|ZI9P@dMAB!}& z*l7Z(QgG7E%eOZ-apTEcx;HQ(foUs;(LoG( z0w+2M;S{r-d#Z|juZ;#VdTgS94vW#ICfF`9w!9m-^dbJ`l9OpuNFB<5ys?)05d&e1M7q> zKm78p(Wjn=62YM*l2tO2jNFZSMBaA7K+!*6yBkCVkDiOe$5&}X?F+`+&+lhN%?{jg zbsqSnG>cyNZL%5hva7W2P?8H(kp51wd=*n~TY_&Vu1y;7qB5Q+nZKWW4JWcNN0tF$ zuf6>19g1GKy4mJHw0R|jqepg$Y|ZE89dPVA_NE0de5%&)t*6e7r+5wNpMG{2$$P;* z_~*Y!BQZo_&INxRu_+jx2p^sZFMTsFBk|odba+S*JoDid4aL7|yR|1X`zluQ7ltbV znGfFyO3U7C*i|u94*ZKhdmoFCTs1=6ZSLHX+e!dIj+R#7`T?9AZElv)E>{Rb-qX_% z;_Lu(ll6&d5g=c1ZX@Wth0Efb;eEm-%ZMgb6s7i%XHGAL5^A!F%P2$Y^_%`pWR*-zO z16+1PonUxaR4V1MJe$thdT&!ZaeqeQcy(J5QVYD0@K6pSSjptpW*X`24L&Wk+o4|W zjc{m>*PXZNI9nvkacS5ry5@KOlO8_**_f;{$1e`7yg3tE&%-r5wThet{YZi6 zT6}3lVNoN&ZTBe?Q?p3uhuP9IJM z#kfo_Y?((n%olxNDibK95}_lOsTNnT)+MlSxe@Zn)3z#9Ga*U1q<}{YGnh)%Ab1*g*xK>-g_sUYb`DfE{ybt$O|Gg}h=DAUvVxya7i(!{SP_=PCjC%sKMtWxrbg7sb% zSeCRdG83b{GFq&I0Zh8P3PvQ}tAOH}>)+A4c{%CySibYL4X!jF$hc$(ngAlOpx9BR z6N%-Iw$E;JtMc_zW&<=voC4r!WCYy%L)hV;CRY;(FREArgc(Jj`>^yvEICy7#r#sR zv8`Na^FH~8<|Ane|D89aD7f&G!`O^kT-TIP6mXp=O2zJRK)f7Ra$Yv+x9TR>1OK2%+ea_VL-vYWG{g~;moi%7~G9hllSax+Kt)Nu!GVp)FMek9SMt7QQ`|dlZ<3(!kSQmF% zFEq{;z9oEC;jBYM{V()DdtWY)U~_7sS<5V{6%90kPLBLwaBu#Y+Vb5T@v$xAI%yz- zxJKP|fyB~1YZr}ENNjyHK2as&lM)&keEkxM$s@}_A}mFT)jd;@2`apUyav)*I8U>n z+KyxW28lRczYe2IT$y3X2-^EKf=jh`$s_V?T0h$_Nhip`c&p4vsir7nlP{H~>hv^< zd0aOmS7^;%>Ki2>SByaA(KmagAT3hTDLs%XLC@xuR;uK;G8#moo(f--uoPsIbXmaY z=)zt^1louG_chwxZk3T-FLL|m~TAdOI-Whxw1haoR%f9`m7+<9z~5oX_p; zMx835(v(Sq+bT&urzNw>mZWUDv1ClkyIz1_-_$-wQgio{X{`& zfkc_Vi;-65P@Re5koQDdo#BxPVQ<7%$A$hB!atBz%i3Gq?~8wgePAT{-iiIy2L3VS zQ8a;;D0$+1wsY)yZIch&?vcg?_E2F&+vULX^}`8cPcCKw8sRefSH0bVO+z~~%IQF3 zMi^If-)v#~cp67MyIp`l6-o>CPfND}QYtRgAt{n?CgO+f;KPvSg7*;}eN`N2!4jj# zGMQ%FY*2z=KHKK zn+af|H z0YaMP4mXHt0a1C(+m~skrWK_H@7?QOfj`4)5m2q+#u%j^?vT8{03{uMc)N6PGl~<4 z3ZCxntlNdd3q48ZN!x(N)RSue1!Y#HwFA$IiApY&Dno`J#|S+AUE2qFnG*zqysO$g zh{xu5CAEI3x|+QPr86cp$7{JQgGwH^|*YCJf*9Sop5c*;@2)%sD*7p&K?jEUdo z&BJBP9o^q=O@>IYWfrGZ={+KVosulvCdIb{tF+<;IPzYFe>5a0CPh0V*{RW+);=rm&@1DldJaR5q{rvSGn zE13LPtG7@q*s6t~h#j?cR3TsE=bh>wBa5p;XB#%(<6SH3Kfg#?D4!C1r6M%F`uo1y zZWnTeib0&LX;&g!IR9cyx;n8%5Olb1j9~EkaBB_(AadWNzI(Yyqpg~C0C34X$GipT zMSnIB4_=+q0I@u^4=oepe6StS9~ZFmj?xSl;!Eoj%`*2+j5vInpNjf&%!FE|SO(0j z!)|^?aBZFu$e;vqgbV$aIJ|I1U#A=~eiq)qL5_qahrEl4UE4)&vS45dzvno7JXXU4 zw047qE*p3aoC~Up!3h)yrh4KdR{@^Rh8aIpV z{v$J2gA%I)I70TC^v_Nd84OTqV`}3@8TD*Zqh?P%6}Mu6Bgi87aesFw+3rMQg%lf1 z5k3floePxKaMe#T*>#T-{H?l!m+jX^xim~SbvfsmI{D3RCXy)*6)DZJyDzU)W)5X5 zJyCR$iE4+AJkV;rhRuS`k z&?Jx&>mT>Ea+%&LXIe)X=W#RvG#~HT0MjCAnms+f1r3itpMIH+FKVD#as5@^lalt* zqFL^E>cH{NK(XMC8!PXD>1{UGiv zecxh{NCi7yXS;cwZ6c@FUN4qbh$MPiF5dU&d*ECGu^X|Jd4}0}jE*K}n3@{b0t0U& zaIYEx`mf4KkUd<~It$RQ0djkg0(L|5%UG7suel!;iXj(_3y4u6c zJifX-FWY@Hk7DiHXy0h=P{MamfiMY}TZfb4ac@mO3+|P!+p^hN#`2Q?#LCU$5wm>e zSG6R#@$*e~r|#!G>3)A1j^vO|Iy7ZpAjykCmYPz?352cj1H?d0UAMYL*TWlYV? z6P0z1IO}KS6qSD3Ky4o5vUm<|!Ygz#YG_q^lDW5jsXecB?A)p1;yA)=hGQ;i@wH>` zhVJ2D_;f^>Q_0NKAy(l#AajGWW6C2Q@&k)pwy}3r1x+l`@4T=suB6Y1`Bu}Z#!@lo(6k%{#yf|2KJhTRlq&Y?e9 zQ_4JS_M1cK=F!YKFVR0m@h}k5ylcW%w*t8mR-`FMIl}cRVty8Qgoj?Dj=3(W_>5j*wVov)&okKooeJu*UjIf&6)SUIw zJ+Qa%VP&MPfF!bv^A+45lJfoB&Lw)n0ZXk49t|E1QG9A-(aG%w)Thi5TyJ;;7LZ@; zMp(4!{vQ19keaDTf^Wd%X!vT*`YHApvMHZ5og&hh4M_7^M=qU`u9dRhNI9tR0w)Q> z?}-f3@1@a&b5fVYq8Zq^2tqWY3%e26NCI!_MO9EpqKlFmX-+_f{^pjYurM{HVY56R z_pB|+xpi27V~Z{PmQ{|(8;b-kNHg;(V0(!Z(Oq-Rv;T9Ol8j*U7x`6Co4(1kA9P;Y zwzrB!Nl=*8Rujg3bgrzZR=~6uy~MMQ=2GPmXJx3BV8Pky8)CO`53CQ0@QMK~_a#C5 z3^S;(n{$4uzX5TBj!Df&Q?+&zJRkgTx72|3NoYlBmp`$+rT<}jcQE{KrZ*zN|6U7neGOFf zm!q+rWU;8QR3i|ukW;H8P$?s%IcajgWFTk6ao5;T(3*1-9@NNcug%~Pro~fvSjt9e zT#L;0LHmoIFN==dm94LPSG^lsp8#(Vvm>3ELhBvrF@-T1`||W-WQ=;(Grh6egg+k3 zty-Oa4i7HtCdX&Zi*jAgM|+^thwH#O9&{&Vs1XJSL2a3n$u|v|z`R%JedE#xJ_c5v z{<%^DXCAujbWLjn4CcZ9L5<@ZZRWI>a*9q@1)FlIWAPdw7A0=koYw0@7l)cd{h4r^ z%zEOz_EhWU=!$+jInz7L(s0Nm`tsn^dgtKPC-#+mnV|hr)APcw#Tx0s29Jce1M3Ro zOH`=P++f^A*3B5sKbngITVz(I{E~lhU)P5ZD7YJi;GeCmhWzh#|MdHpa|HoVRtHpM zhG8L%7;)nb5{!5A&pPZ^dEVNfm(Ke!GpNQTQq!e$u#d%)cU4)=wEN%iJd@Sjwm-=k z`jei=C4Uq6IK7x4Q)|(0$;U_{V^4Z0wH^oqx#UGQ#(!2~>?09r=N-1FAQM6EFj~fT z@xeO}o=CimPX2kKIW_Y|%b?oLn~s}ULgIw`5nexJqj&B!2r5rqF4->nPkIpFNF6(h zKAz1t-=^^IA+-HV2F$gyudDKyFN269MiY7Ud2$ArGkMb6T@t`P4V*sG^vUcNW zLx$?QOuq{pJ(f{`^ii3%kH{ImxdR_xR2o{v-9>1Fc6`iH1dGU;(tU)}Flkk_{vmwq z6510YsQd(f3wDYP#fnbsQHHh6FC_4lbg6;CFLF(cnOf zI1`l~3`(!@1wdU897tKg(%wuO(Cl~s!1>8k+sEa<2%SSavUT4-)J`bXy-w8#5Hxhl zeC(gRvd$xKi++&AxI@wGe24j<$Nk>gI5HzGfK9V0JWt4`dK&PnKr)(b{MH@(A*mgU zl8Ey0^2mPOzOY1>j1Yk+=ircF30b4HZ+6$)rF@tdWB`e*?a?K0stp3uo2N{)QiK9- zw7u8=C?Y5~vtr$H8!wj~_y(Rf?i}n+ANuDb;*$8Z&132s(#gfId@NtNG zHnvWLTeUY}K^dkm=$a27)-k6KDnu7fw>s4B_KtpS!ynexBF^bQHIP!=e!%!*jIl48 zZWkA;mZzZ`yEjhAt(9wGPt>;e^A<~3Ju`r)&ITkNPW)7sFZyaGJI( zGKr8ihlY0u?0%b3?#(tK;--{0j<2yTf2825+HEZVHL;6n#)$c8BqkPi-`PR*@u;Ln zT+NcsbO2pDOBO#nrEl-ZbF$Ih_G|e4?Rjob)%fGf5T^H$G(~M>hWEG`h`+Z2IXq4Wx)w_IQI(7)s)Qnp?k%TF%Df( z7#S^-ZFrxv8D!k#5O#hmPI>AU-Ylyvuc7p!vl59Ir}KML$oKK=W_o$5yUD_7MCfW9 zHwgy4;bK+FknS)v2YY_`@VJu~YxjT3%kD2HnMNsfqam4;^wDIdz&LOahoRQ<<)MYamycPdZ=@Y#F8l*)VdluVjGJXhku+ujl<)c+x(JK2U{Kv(d<7}9cZblDYI$?QW@0jA}I1s$c z_bkS^%rwF<+3EPG2z%A=cK?O)ewp*V*Yn0urhBkx7h5HBE3oNpUtZ~z@T~{!ea>^n zw5PI7NRo7ZW71PSo%ciQoUs(T8X~?)>iZV);Z;@sV~*TFWW*<`_KI1dr_5wUW^rF} zWLGr}UD~c_tSs2>Z3atbOLwtvyf<*fx$l(;a{BK*;TGa7B5_@)N4xGV(GYolu)*#voGCwN(ef! zIMN&U;XExxpLm{=MjY)mO=utQLw>*WN~W7#Jw=R={nO=LM(t^$`pkp1Lu{l-9aLIy zMnh`E;~kgg;S+4^iswrdSlDyR!Ac9HuiTXaPVIYs3l6~dAn2}4b)CbZiG80q_Pbis z>pcjXi)?6`e`I`ZHQEgqslU2hl@GZ6+gmwOHETM$2(Hg4b$SZ~hmzU0BmBrJ(q(Sx zv%^U!gMQT9nFa0yVnVMYjgPy=q|u_<3oW)IlM@NFd2YVF7-+dTRY-1BB37+kO&!iZ zU$GT^94gel>Ks*S_qEvfy)SB(wy%ZGzZ^8I1x*HV)!QS#v6yeXvZq1SooO!92>@g3u)&X0w%WH0 zh`ml{#J>wMiV9@|Rv9Mupa0^@)4FaYoGH&Gt5o(>{~MzEM;XgxJ+m-l(n@{)Q@z~u_Xzqs zvDAAT$+R%TN42~#dmc-CfjN~{S@gY@A?Q4#9{t4%8pI}8_jcJ9H-cmy)#9w*qm>a5 zEL56aKGIO)wOqSzOL_4eh8{>K%9dd?XxxZWyFhulg2L~EHn1K@$zn>o7sQ<-}fxnKj-R|aaaE=&+j{xGnBFj2+xC*&D7vGs0DH}El`5m6*vye3vCy4;zb-l4Jb#1k{Y5`B15!ZLkJ?dHWq9R>GL|Z|ptt1m!V%NFr{kO8J*`xFQs+bpqqf?dQvajF zATU&Y4G9`Ij$nR0fabI=%IdF688p$cE8R|>7;NJGugxqrvQi$I#;6S$5U6|0g3mEY=Hhqf%J6>R&(;sVFxJWrkyxQ8M>Mrc*Q+j ze)zH~X$%V30C}DmWD)(ObA?Xkjnn!e8Vv!hR;D>)C3YEs)JC;)I~35;gE?7ljW<7; z(qwJjq!Ci$3>#=xnt-6-sMBO7|Kqzi$T3$9O_2Dqh4%XOEQ3SToeV!c>u8_#q*&x= zlPZ?40_gfHaF_A!)XEA8O8zmI`J9GMw~AQT@sv!9j*!$m26U8e9T{}$r#xK9$M}76 z7m!Qps#DpW^_;`yh`8vK`Pc43poTXbJR7u?+|k#4_{0;NM0T}C+t<qbL`S2u4|Py&#_y5P zoniJ7m5gyoki47|vv2wf?3V~0Noyf9Wqu28Gv(p!f|hVd=!`9T)-8@tc*qULGaX7 z9~kI-7u0dnPP@JKLwm&76XlkynWtR(qS6>~{zLCf@ZoHcGf6OYLn!LCO4S=)v~K8f zK}_q2)l+QTnf!6+S4o>1C~b)9?qrhO2xBr?y&nvn2_}k3kK;54)m(dPg34u0(cX=o zZHw8~^l}PPs@%51JB0DQzrjPhU(NeRH`zztYAC2Z^!T+e{I#HI`$5nc5=iZW;#jhD zy5=8$)eQtv^jROu*_SBCLukL*i0=j5`!|o9pmyw_I^1jG%jvAb8TB_va4 zZ8RdpSu*Q_C-XQmi37bHDA2&r$0<8$A{{9?zmrYiC?0*Pr~hlw zl+1-h^4JN>+4ki|umK%;#q}@cE+gUpb}xY<6tjrW)J_`zVSJ&^kAnu}*ZWCHmiwzC zQil}!pnXZD;8*8!V>BKht7#w)@g~iYwGz|0d|Z2sd1aU0Dzx2JYQXBsEnS`K&Vt`w^7b$e*?QraThqhZf{VdsaH$uo8F+oTV#rJ^mMD-7{AW>b+bT z&|Rl}>k)latiL0B;ToC?d2v7@nSBMl4E0NbXx4SOm? z5!d{%bQ$R_Xz?HC?Qc!7{3U6!8Z(k{WwV0KLe|~6E!`Mj=uhg^L~2a4{^K9CPkD1? z_^u+qD>Qp5dzZ$c-i0lDX<0_wz-vgPFT}qM>G9Wkc}C!}O7iSQS)T_$g%tAi3&WG5 zS0g{KON7IcoXh1{YIaaid9f1x7j1?*{pz*9MBy(Z-N|=juV-BqdvW4uSp$Q>gg}kF z2IDGf)+6Z)#2)dn(zU$-pN9JGjre=Ja50cjGEl^WVb=t0rc`~Q>{guj!$HskUF7U} z6&qh-ae2OEg%UZ5;S5={sGo9`G9!8UGQ1y1>$G$wlzuEBteB&Mu|Y;vc;S78@2VzQ zxC3FD>%mxRkcpm{Aia=b>6@VjEEcq^S6(&vOBtR5`mQ3@*o=?zv*t-3<=g-B3gMqg@be@FU3%5)`hRV>m-dLW% zz1X4V6A*+E9lAzODpB-bHH;vh@o(&%=iXTBqKC?;^|d1gJ!};oz0?ugiH$XbT19B| z2XAF;m!mrA2*0Yc_=3{utLoiR${fO7MKuKWC1dee__kTEJmfyLz})agrDD*yhhA#h zgiKj!GqFJ|D19T@R#g3IsBsz&Qm9V%z@Ne}+l=dk9;MnowJ^xEfi~-f_c#m;rW5XA z5IanaLk%GC`2r1GUR4u?-BhwL(!8#t{;&{n{)VC8|1UMmlQL=D*S8w1tl1-uEn>JD z^W0yvQb~Bcyq$QV&5PEp94IhvF%nc~JK?ul5ZBJQ9o)y1Az1#cGVL}Ru~6pOuUl=m zELlHaoSglQ0R&23uk2|jGzKu8O<9)6D4ctxlE?9WJFT1_76G^Nk3(AVgA4$Q`Og^L zAq8%9;U;h2MUU>SQos$B?sMt^_=VlX!Q}%DD!&t;yXpIG21WGlo$I?yK&5-k zFxd;6e>3&fPsD5Yjb1l`#@#o9pSFUZv_Fu;P^ec2R9)$A{+6TPJk6+i_^kX!YZKih zKC2otYwt}())2Un2so}UKo^}DAO4zye=}dGs2YsB6>~kEh~2ytxa`~T`Oo=AW2BB| zb5Q1Ujm2)dC1MLXDc5FJU}fej>6uf?Dy>K7{iWcWpfiwW^>#S{b~U4I@DHKr0t7KYcLH zJwCKDItucJvtzx?ar^P>6t6-9DddIy8a_2OL z7@6EkxjG$r?9PU`TtWW# z+wP#Zmmz@YEp$$7%CykF!zY<*I~mmAD&6ky0a+tQ-2`TP0{ z$E>AD5_#Tq*KgrE^7AmI9ESNqQ^QH8+RjM#Vlc{7GTh-Iw&fc3)F6((WKc52$dxME zrxL;DbS(LCvCD8KAJhXbP&0L5w>-sP6G8pH3(pzsBzb!GoC-l^pbGKNh&jHt7ozB%h6(*JQ7_BA@RtW=l061A zY|@rak}8+hUJv~)(pU%YB9!VQWv|y%g>PSu+P{##7GeiS6zzHxujKkHWu$2tBUon5aORZ!Embt9{Y>&bCWk&KI;|7Qe=JdurH^a!-K5W8 zDlta}pO79@@{x^pjRwz1bb(EuA3O#1L=#>Zt=)3H-8lU=_E0#vcZuCgArr4$5){w+Y)3DZuXBy- zS2Q1`FH<;}R5}BK0NUrcI~w!?qF;La7@x>LIFo$YbI*rAULBrncJb|^aj@xI-m>GK zXF2W{8aGEUn6hKs`Oi-XAkPj>mC(O%&9gu>1&HYB*G=6UpU4{_Dv73h`N1@&Ftn@) zbX;<1ZFe7)au|CI#Mhqg&Qc*0ytC?o>$%{RrK7znpgS;T>)+(PFp#re9@8OWD4>=T1|r&Vho6ce*S;$ z1Lq2r|J&aCzwCqm+h_Ve_JJeK|FIAJo<&!J!9kduztb}6WTmm>`LEoK8pAE4GY{6_82HRw3*Z>*9U(<@{{^|jFb=}%=TFVZ$J-R-M8MGM@o~f9DiDOAs8)YW-1saIE~o|e_A`t`FSRdaq+KvY+4jdi5Zmx@oxF@ z1DLVFJkd>L{0-o#Smgoo3?!kFK)4@(?)eT+qvBmEn?zv#gMqi zt>@@9%L^H?=%)hqeMp20A5!kal;bk11|`%mJZ~GjcQs=2LRB=DxMy zN#Y6l5Z$$B2YXxCE4#bkRZUtG{`b;DZy4C+fA~qb2foY5`1sYt&2z)ln6pE08vIxI zPiZt1_d%dx=HGBEI0_rwQvTqG9Ed?EmteF<`IjnUk=?n( z86cN=v8jAs4ZC64gHF zN!v<_h_{cGh#Bb2jX^3)ltt0oa1RWYQPums1$R(gorq&(YI^_|rLmh+Py!ZEKBQR5 zpl?ALmZ0Gqi`~`Gu`!z(FIBJx`F5dCS;9L6yC|NQes7^_ecUnmOUyr~KrOGx5MO>j z^P6ulA3VdE={|+SQwYJ;aTAqy7JQdGaND~eT;@c`8~zslT5Lz6(ux!}wbORmn_}MA zto=F~QzCj8wXx0aS4<4;Y8(vWk%JbCPBj70LSS=3Ki2KMJp1cJln-3Gm1=BExXvip z9qVN|eZ^ZlJ~>2)`|qUH1@yI=-rDZw{%TxL=#|Qt=}myOp#k(m)2$I3h(EFX)*=D> z()(XosQ<-OWF@2qarM*Wm^1Cfy^e9R6bCkJl4loA?f-vs2r?xUhSr#UoL_Y)irNuk zyGF20T$O{BSP~=FyjstKN@>37PYAfSQ_hJGYdm#f&3`0Mqkn&oNr+&z6B)V2$Ca{oHgUV*mb1)_wBK))Vk(#WGu+ewv<~Tp7r=vXKmD!I+ zBrUG%kSbZTYQMLrt)l1eco2K%J)_TlpfsVNjvX8(#RlV}wpP(6a^HTOP=mwpwdklk zWST?fEuvcy+I`(CVl6FyK?5^Sw$}qC@u;hd6=9Jcz9W^+IYPOZ<(5}&a4z~18-7B> zzt+Lj|7PinbKv$7)q7ZG;S4;$o6bklL%aRTPUkNkVz)UO(bbW>b?4fOJx1}bOn0Kj zF~V{wB5&_RXFNjC#`j*OiSeV*NCRPL7Et)-f0{_XXJ;15f~azi_VTZWQU51HJdW?e zBU4vb)VDAGecu1{zBa}w-p=xYgJ|hL57PBO-{wHJ)iKw(yD02YxBlXIA5JR0;XQJj ztfsT#;*jXmlfE*VKXG0=cnQ)wchfah*W(|p2={%jcHyzEwC$B0=%}+rY;ObAp9jw) zpMk1NQNv@sQseFE^DXq<5?2Jf!v?2*NOFR8jgK!m_bSAz!Sj4^s0&eSw$J{)Q8b4a z&*YtIk^?~-e^QMK16|I|5SMJ3Nx$6eQXWHK0~v1nb2Xuczk_WiDk1_slRoaT$3KdM zpJSyycA@P1I>=A=9xXe=pYULNuwR9TZ$6T(2-nDEgpI}oIay7Btn$R^+|7D-hNc%% zq8&JGkO!hyl^Bu#?BkOI)q(`yB6iFh=?$jReO#n|ad}5W;1VU5+Id6;GO3~>=}N*m zlB|IfaQ06|MDYOx!K=js{9Dx}zM_k_PSa~PQNASH7jVp1NlpWnyRLh_p!_~G;x&%6 z{Nl``0KJfh4bV@&kHC`s(s5g^P*xOy$yKt6W-Z6RSf1uSks6YbEb%O0A=`0LU(L`U zJCyW~dQb)(^XrEZkY_w4g#XcAWz!YFVI2Xv6Em(U&b~t{adx!=uL}+D4 zfeCT74*QjSMw^qXuq%p#5_!OoTdnRQjXL!fPtw&qT)Vvc>Imf2v0)`k2uDpP7PzLva?zV|@)33oX5cTdMEZa3;*|A_jZI@E(&E>CO- z`hdtm);a}zy_iqKG!bD_2OzhiHOG0$0v$8Lpgrm+c1F!d?ukr1C8Hs(W zp7J?-okBpMuxo;^l7NY|P|2$^y?lT6)*g&pS(f#n@Sd8}i zN;-rpYu%nbKtkW0V7Z}w#J{;PvHmXl=PtZBkF<}ldy8Nn z9m~r!^tmAV%1kQy|7h-|n)TR6KZX@mGyM6B8g}Pnk#~}5A8VLUUo@$ExBN)xF6y7l z2sah#(bhPOoKa2}2(7+igWxYzrov9==~i!vt-58^pP-dv!U;Qd)}>_3)|I$v?- z`>zZ6P7j$>T0$LM&Ek;#qfSDv-H_Jo1v+q~xHgP8(P4R!elk0Pl}oT zUhTKQydc>a^XO z_d^f;bD-Kb!yWnPcYP$u2=V8&D%I^lUQ{@2+!Kv{B)g7ouYtHUT1Y7Tx`=o?YG#zl zlTa)-SZ;m|^E|Jo%G^zDu%Ro=n*63i_=}UDH%5>!ZN@p`{si;K8l6ZcKb-Gn%$W_x z@f`WiOvd=RWh-1)Cr}@Xflnp)PvHJwX0W};2z{eP+4E5<>Tmkw_d4{2HQ~4KG9#Z) zM(^Bk%?k6eo{Dii`loF~Ni}(o1f?w1A9h(f@W%DTNZ<5+IHD2#sQHi%=uDkYPv4LI znsUB>&w)6&mVMb@jQ2WLnWlmM9P{h(`630(%Z7tHH=-XlL^9<|Vcy;JR@!y=2>Lj5 zUlBfyx=r!2<~fV^GFa>3n}%~}jQjd8H4=vTwogl3Cg4`tESpLJ4Q^${5iDJPOh>Y_RV*2{iud_s2?UV02af zvL@z~r#e*)JUuiRf5LU61Mk6U-LE~3I2Rrnymxqox;S4^bp1#$@{w8S)7&ir46YAH zc^$y}u(K%FK)zI6d$&0Z`PWV@=N?1f{o$UK`Th+FOmjy+?`qJ7W7a9F1pN7c7WeE~=c-hoT1<-dqM2QSn%;TB^yM4z=I5qO{ z>GMw{Y#C2KX@WX_x_-x9dnr0ZxJvtZ7ibpdUROqJxq$AKPX>=aUs-c)|B`} z5az2P;h;@Zn7jQG-8;8n?z)`HFp}Jbd+LaHEg$X&YJt^)If5j3)@E@EeMi1#E{|?l zpn>ZxAIkYe8tkZ$;Z8dW;B@lKVqKXgINvN`Fxa69JKVZ!eAj6~%V1hbcnUz@HA%16 z<`nRwe7L1X(Sq36ytqxy06cXX>N}4E=<%@AqFSjCtXNgrV2ym+Ka(^4g$9#W;aQ*1 zM>L#Qje=ucJO2ZhSgNgAMJ)T5jl%I?`@g^O=Bq z+U~s!@)Qt&b@%0!HQ~JJzJFt1snFwJDoJ;z!JbbOIr>*MVdVaWof%@95cymCbT~T& zEIu%7(QTyw{l?tBeW)i@F5>o6+;5E2MpTu&XyDtToB0QI(XC~^V;%aLBv1E`b*Pu* zLUkUOW%RQh{~WdUUL?$j z9v=@woGbr|wC=`y!rIR`@4GrLtyjnx%u!4F9`eJ;LuO^GOX`SI*qe>Pgl97Q1IifLUM=xtZ@y36u&p5PzvFk5G zj5Q4sTRB+IxzRxLxx?kG%NoF*t6+HXraG{xxCMDOk4i>^SxBKlif(K+Xi5>E7hnpTnhqTM^jH9(uCRqlYEj7(Cj*cNZQGOvqcHEp36jkEnCHT)EA=Z;M@C6IJFNd({k^B~ zry9KX!^H}8VKWvwGjRrx# zf_i70Y0xpn*(#2E4!^CGy%~jsyBt>rRVoNL)0nUN5$|nTY;-sWvHK~PbwBZ z1nz4;S%UPCA1)Vd9`~WX>eS9pxghTLv7dw45N9>Y5`WGo8a&*w-(;^j4R}IE*fS;p z#)J%7ccO3PaMR5VIn{x5Jhm&BbuTC{@D_a3R)^f~-~~}uOuTIYyed3Vf23$`- zK6NH~v5X4-J9N_jBHwy7w0*y@pe}}F-dA;h0B7w20?>lxdKMX~l zBq;2>_W*O=*P`p)mB^!r#DVe$;5dG0_frpLlul#vSze zmfC9Td;-qOY)NcK+)P~#xy@q^NM(tf+mCz3hD%vXVVH9zTb3?rG2^^BF*Ea>oq+i& zyNyD)kIIO+FvTHnx}T*OtD|4Vh*;~V;NJ0~&&^c@^+EDJIAVf$vv#x%)#B&*yQB)r z;NJe~<(lfezce_W!(u*xpDP{usx?A@2Jg6PtGY@6-fhbh`7I4VneJ+pISjyU(D9n} zJAkIub@u{sDop-oWkwgHLQd}0J!4O)uq5Qg!G?3^w_X3+qj(;6Czd#N>|07YkFgSc zQ{k_%WghCgYl!ps&fNt3Cm9^0bP@ae7C-jKmIPSR+U;EAMa&?&@k#d@K<)rEkd&G$@>B2S9lPPo@29$mR}BKgQS5jWohVj>%u# zi@Ln^kF&A5mIimm`M=$9paIKsyWX)qG>|fX{Qia?&f|BI`9blRM=1isuLIFX)ULS9-hC~^ozG)2>K-Z^*C$H z|Hs56bhcn#?68@MS%crdXf*J$0e$13cWJm4?&V(F4_gEx|1*d72Xf(DQ4`L1_!{|f z+>%ZHn*rv)y*=h}$P+mRu~XkshYh|C!_nw3v2u1ZG z?u>}PyJ4aA>hC=oSQ~W$5HA*LM$$dhXI0uq-<`;}ghhjb`zr+KHy(d!GK@ZVkWX^h zkp^xO>-QX9rULVF?Yp;(G~5$6l1#XNy~$#Baxu^W-j5Ps`7?;gzS$Y)4oigm%890}-wIA^KW8X6#>FOJBKeA|> z^3g!v5#tGa-H?X|7{3*s>{+}jFr|DwzlI*xWSO*oJtxzE+?Fp7Smx(9e;(2sZfoJp2N9CR%%ABaT#o$f4j{~3>cKG-*-|fkNdt?$<8|5uQ{izHtxgw58!^dQGE6H3~71Gd31b#l@a?Z)YqMjCSt!% z(cnMJ&~0xS0M2}=et(0L0yF$qcbO|`f=flEztHe)drp+XD&y{+!A`W~F$E>TcTE45A`+#^D zG7tO4ARckjCrg+RkAp>ukqn5(z?owqeCVf<1r_Hyxlp%-s*i$^=aa_o{@aQBYxrgB z%1E5gE14<0{A%bE2~!(fktdQVeL32QOZj;(=}er%;{%<;pK-p+N)iS-=qnXz?AE@> zqxo;IjznUfmGSl}c#S+bt#&QHUIpj(%JfVe&gGN2w|Gk|v|#^Uxmbx$8lX05n-o`~ z4tYQ8ByP`XfQ$5>$xd@BWG?wqt#F@zLrYf9!9BLA)}?R`zqdlA`^kEHJlAY%@?}TV z!@5!h`6hhbG@K>;9}TqB9xndETAV(9D;DvX++EFb3-QP;<|O{D!TIBzKG?=UK(mb1 z18XJ%dWPz`4A4j1%BSMy5SN;-Wbj+esmHe0Ol=k*;BA&SU#WcSl_&`)#8v6~TRyc3KbEimCq=Yf2{9tk3!*`GX4Q(%^Sa6GUg-<9~E$fJp4+1*e~yFi^25z+$2WzYLkLzW1R* zlVG;GzAp^|LV5+OkpM&MD{Pswv!Q(HLuDfVd7&x~#+T2nLXf){( zrg#cayI6Dv^R&Q~iNpKM6M*sL*rg#R8c2%p8-yj(z^Jy-i67_g)XL=E4Y(g@%_6|s>|Qmw6IcE2K!yHN=WteRbsugxqD6(@fb=D*;9wLyi4~% zBI06u`)9;0#ATwPxuyVa&g!cFDop*~LeXYq4M7zN>4wu(nMFeb|n* zPHSJT4c6Zw$%6@4A1}B@k73=ybCKT#@sO#v<(S3#s9rWH4snsHm``yY$DsW4d!!dDG9$;F5&AF`5hbqk#E!x5%dY64cQd=bD2WnCD$78RZ3%sOR#j z;Qg3Ca=hxYopGKzaNdh}kG$|aF{NyZ_h;S7wPq{M{a|0lZ2kSn$9RQ>gi0Dr`AM}z z;paY~%*w9*e~;;_iA#ZeB(OAct*rjN{O~hCHD_it%1@{Akb!P@-^9XoImy!1q zAYmeqVwW95fMQ7OvAgD&oBJzSeH2Le@oQV23F7xtE=wl+3JIM|NrPOENO&;mR(uxc zmgZ(pzCnEd`va%t?qL43*uSMP7r)oONL*kZ``0hL|Lix`Ii;5m7qAX0Q!AOU&$S+` z)2lr3v>`&ou(GK*PwvIa_}|4AORN>aXKw^xRnlWD$j7QD_oMnd*0Z%ou4o`GhoWCx ze2dj;B3)({aS^=6G--}_cz7(jIH6u%e{9?0i#j=xx^c^M^bKEqCU}RwI-xLAr9vU# z(xdeFXE;Z*Hnjv%lQEC!9X;|0FGg@47e0#ktd>B+F6zA(Yw`K7 z--D-80@0TWUT)&%A>qrjD_pk_&z)L2O=ofb=q&73szClZ^(5TnMSZ#}&r@cx?*ZNZ zQ>*pg=4{0|tk&`uudVWAE!E0$3G2ntmt(8?iLsunSdZ2H`{HA6tQ$Q?ngmxLJ0+H{ z>M8vCi^4rv@Asa)VUM-bPa?G(s|%gyMk?Zw-nlsIkNoiNYB&&#I%$k$kze)Iw;tqH zPRwb{`F*!d(Jwqb3uPT}{yy9_yX7(RU+d(F-!^IZK9R4MoGv6>9^_A#*C(NIo1APZ z&KDcuF0-N(oI?(C_8V~D8q}QHnt^jtXTuwAQ}mgzJ7HomsDm4KicLxMW#);;oeMbE zZZ@jy4aa*pZzHa@+W-HrdY_Ouxr$Fy$-2zyI5Vyy#-S$^IL8F|Qul{1|h|(xU>CB%Ckv zS0-$xFppe{bubyn`+CwI_b5seeW&c0%Ifc#TB6mvoc|E8w|b$_3VET-l1o>+g*o|P zyWD>`2a;Z<{#S+l#>slwU)xK-cm5u+MD*8PYyWsW!q0PYvHUiSe)PrPF6s{YQ~eOj z_VaB2dmP5!dpEAuHFDY7t2!DDW=LDb!=HOkz$zX?Mjlm*SnpoZS6s%r{k#<;JNDhQ zt6p1a)o-WOZmjyPuq}I(QA9dA7DZNbs?TCl3$J(#w?r0Jtas0000005K&1000000G(R* zJ6C_$S13}XRAeL}ijqy-B_V_o6(ZZ$-g|w{IUf`y5v7dCE<*N-WR#4Ilu}lT5Gie* zyT9OhuBTt_3pt|h@1jv{~b?D*uBSR?nPFE{dSjtiD*sbXPE??`N~KSzeNC-gvt1U zL;?o4mskI~LIA5#ag13E0iWV4+juV$aFy+saNPp}zK2|U&h&|Zkg$fu7A6u>S&kY{ z4iFG0K!Z~UBnUk~cC zkT5?zdpZrzH@}nVcGq?i0t*zEckCuXSl3^lRf>c!w98)t_mMC>t0Pv6`*b$Krdbz16awvz;KG=+P-e}sFIc*UrsnSci8Q!Tvs zd8+NqTjRzESp09*ko->q28O&x?r$Pt9WzaH3C|ak$07IoI0>blne30b9sr-Rc-NWLboC+$Z zXU+UK4-&c>6wOUsNVwSD)W~5-Le7@WxfexA@X}#15yX8=c^$dp`-*_6J-%=M;(Je> zNy`~2B|!7~dQq7id{4!mjT;dUlOs$G{qcBiq1Z#~5RWYPM(!fSEnUF&76;z{f4^hi zeEvv)ZjxAx7Ve*iJo!oH3GyRgV4<^@0JVyokbeC6iHGdWTI)!-d2PlvPZ{@|oA(Bn zBMDXojQv-VNEi}L`)%1k!lN5orDEQbuylFZ)o}*}Lx#W86zt%06ux?lf^#A3l9^wSu<**`gHZ(u!M+jVZu}Ia{_S4>=`#LaK+9s7 zDg|p}*ygt(ZYu9G^Y18-@MO|H+J6Plv3jp$5c0{QYjIGr75BrtBhvvfxDTj5(v1c#Iv0*pMZih-%r|A5wPP)dCn&MJ=VAa_IT6*$kn95_y86p^2P0r< zZFcH_2_5>^-WZP?C!wVieEW+jSS5Av$F1a}0W!!J_MsDO_en^ob+zHr zrr=bbW8S*g6x^LJEgr`ERy+|=!0k_gxutq{l~)8r1ZqCffk=q`CJ-udG7^T5Uuh6e zj)1$#-PIns6zJ3>%KaWDL2q)O{hs@D==pO)uIWl7yyzA8Im;XgEQKsl0)rH6xBPWl zVvdCS&DY0?FOi`7rhmKsTm(pdJ~*hVNP!UR0Yz!t8|pgmf{zdC&W3$*C2}M@PCe&V zu}lEjB)oGc^5spP9CJGoH3KX5k z*}LrE8(d35sNh!xqfQdE);A>69*_{tqj$G7n1l$uAC`Cakg)eDDYJz4S@=LAJ`(xu z@ZXgm=D7d+OGHG&a#26oTFuU(zRKyz+b zx$`{78xl@sF6Ig;&|##ZY0YC}I?S;Ym)?JhpYKm^+wp}2XSVZ2T*!l`nMwA~a_FEx zYVyJE4jme|n{1idMTg;P{d8jk3b?9Hc^xlDf7q76UTjQ4>Aa@d&J!f;>0^=h4<#Yq z(JkZ--Y*YJlj#X&3YcF$ux>!UDeYh5Qu>Vq(EAx*WCgIL)?VRnX?<9Yld zOWY6Ba)TD2!^39iOI`4coHZRB_c3}nf1n^_?}Mfw4+`dZ`%drc zB_T4RK;0Ad-z;!$S`GJCugCjx0p^PP<^9E@sQ>NbUK+80J`irN6}*=Mi(!Tb49 zSRTj|g1S>}DfQ+F<{^f2F-7}P$Ng+S*x~c6pQ&p`bx}|$u6=<`ItHq^&V~iATmZN5 z;e&>oB0zQZ58?10e3kTDJA_p37bw-xfoQFAl*PPyu#%qDz8i~gBs^)4@2*0_)V!hwJp=ZiM01MkETGJT53}frJ&w$ z+r{laNPx#f(l79y%;mo?@4Q6Ot>FE8 zi{6;mcwVl3x0ZsB*1Kk#@cuThwGb~x9K>4$&GjEgLJ|4)Qqw^a)=S%@o1}(A;%eIR znv5vu`W#enODPK60__i$GSVUKdy`t`ZxSAC=Q<>fe(KVCtA@Ls1o}#7yd?7aS$*`N zFXCK3l<${@xK6om6bK-2A4eaDypO<~w)k>^k6N`e0p3j`!?x-(A*F9hOV!n9r@|TV#1-9RAeaTHH;nRDCBcsTVam_1L z49E|s$_EbYxPO;x4ONp+5AH7N`@}xOz0Ca3n1tt#=1fwbK_4h_c>Ux${`^DH9@V{( zVKB2lNP$Hz0Hjsp;$$^~fRE4$f3!CgxbHdXIeUkLtmp4`u}w56;N8O<&`*Px>G1hD zC+1Tl#y?vh6L9RKw$3xeC)3PGfVF^xq=M;-XI_(V&g5X;MF-sHvjQ?==#y)u)j3*m zPq=c1d|T+~1H%JVuhADc$sJtkOmvWVVah%h6a_b2gVxtqM8ik-zObbabhtV9(AsJEuDTF?MfGV6<2&qu+mb}zog~4pc61#N_PZN>3w@q=e`(tbd1O#u z7+!Jtep((A05Uo_w7%%b%i>Y$yf@^^gFa1Sm;7SOvZcDdZwIvi9XHEbUdpPXze0@@( znFbkIzd!b34-&Y`|ABJF^Db6T6(gSxF#geTMc%Bo{#*DlAN_S?l4%F(V`o-z!vs6> z_WldmAk@c2!4WpI;7ACXi}lUciiB&+>PL2?{%_n}d2HQj3U+;wNT?8t0K@kdtqtqw z5ZXHu^k)wRBi}4KmA&w}@4^i@upcYQs2s||UX`$DL@y3N{}L>%UMfL7ZqprF!98W1 z=sK%__*m%Zg`5;6z<{t>EpaLUgkAWkYr5SLJnjaQ zdUblv8D7v5mN;6e;s+BPUYl>m_`=7hA6BpI3V>A$m+R@?A+YnvRne0g;V|3kxtz3_ zfUzwxuUK$jC01M6+C3*Bh>u}l0(*$USYOOI&g~$MryJYc%{}K`t=EQGpA%VJLdFU8r||jd^i;J zWN5w82>@ZPyB3_Lu5e&Ne-k{hf#b(-8oc>#32Y299p>(~@K(};r9ROaxJpJuH%GdH zT!zQQqsNZGM?1Qs@0>fppA0sY1s|~ZOhuSK3V?{|a~c`LAs|wonrZu(2B9%5Z~2=@ z7}2sysI;cwMPa_WjSX|)PZ%#}BJKNL5nhCy?EIOpnP0q{VhXyCBE z3*0U~?4~7W4obDbp%!aQA*iap#B-Ys9F4VPbM&ZIaQkI;TAB5Cd-+%lg1Xc$~7vvl#K*w}v@mft1zVKR5 zQK(nDa^Wwo!*ft?xqaK0y=QPo>241|DyfN0N6ydS599K2g~ktY!cx#;7fXOhVD3ZD41>$E3C0QazH88*spVU?+?Zt+bjHqtuPYx@adbtU8u*Ww4^M0QNKAZ z2prDFd@-2ikXWirgQqcdYOFoM;5T|-Z1tcw*qTKy%*HqX)X2GC-C_emd+x4kws3&P zzNn$WZ#J-ZTc7*#vJt#!h&#&Wc>+AGbR+j~GX|-vt9c6~&cP5Dcdekh4OC4h^*Fn_ z!8re@T6e7HkX)+96tJGFh`(ggG>(a_dr(j?xK}isE zt8M!x@4L7U1(u~^ch*s$`KOo1@;wFoUB+&T$a94)^O^0KUyV#XQh#DzU2nkD;(fQ{Z$Sw9w(|PPS=uP}yN9HR#udbAgXC{-3iF0e z@A!f4Fi?q29PEw=gwB;9hEQH_2-Flcs4jPcgBzm^6$5NR<0$Jdd-byrq8%dV`B4Lu z_%@`)d_4-@(Yagmei}k{{#JGMA`76+ik$bbumKypcsKS6C)n;(yt97F6S8iycZ&b; z0fRrZA2SmEaBzR%hen2AI6kP->GCrSk}l4Mo$sQ7x0b;+YwU%zR*emRu~!{9)bHH% z&);|l#+o_)<%R+x>>l>71CcdT*cU(Z?VCNb74^5m%l*|X0n^XMOB%Wec-eWvpcDJ` z?u~6uOPF8R+-PVC3Hg_E-p@F`MPJEkEV3RXAy}yxc46*KU;CoW`6dV*epN~PZ_dw8Df9+)a_1mzFNm)t6cVQ_4PuvrGMxwW?bgPsBC z>pgwNbj%d=?n$~S{y7f;ioW@YyBuKZw{*g50}pU|^XZYDyEl03mES4%+z0jwXRUd` z7zna!KYFpg4gm+xK6sAX`uU`uHQY;P`G5~ZPcIV z4~Me^)&xj-KwWEsLr%RV9Q1yh6{~X`tX;EhcwZg`_L1Jg(Y6z?aLH&X@_`8~iPmul zNn3!_w>zo|8TR1)rIGu$hbQcwnsK^%$_K>0N`A1;_J)CPIl+$F0U(uT$YQP=0yo~D zw+p9-gWra7(9R)1duD7bw~_>b?4b;w2HcCl{81;&#T^^yJev^@U$OSqoU53l3s_It zV^2QY^Sd*47v{FL3AZwS6ClL1|DN*`0%lgtKCYM};2yg~>?Z*{?;q1?856|7;$`{Na(be8nGg#oMlD++VQweF~qScECI_1s;-m{#<*dmWy{G#QT=56p=rwX3 zoPKa*he(W7lRx;KPznaA0QjX7e(7|mKP;+MEgu{Ugu%Y@YYJ2-bd3wWjuLFPeortYXg?CNDGY&YnTI*8D`?Q0d6m)dE(NoD78l`i1k^b!X>DqxgSqp> z$J#OqZtInuDA_`R)|kJRaR=(%9tH^=?60g7hMnf=1Wc7v76*$6IJAY~e&!?0w@V|z zIwJ(E-NPgjjCmt`wf37dMa+0Qw>y~MhKvraF+}|kKXr~%9{1%6Rba9D zUoUboTs--mfUgxI<8{3R)GFK<`r$@E{n7oG`nJ=cQ~k&E`H?VKIT^QSi42Ep17?jZ zWno}uo^8sig*{}l{Lf94UBiAZQZ;dRuuUE-X_ZgT30AMQ2N`iA$Z&@WAH$Ntp7FxWnccpR#@==H6TfXaJSEoq2{JG-;I)>{HN3Xfkt z#en{+?6bgzdYhT1%l91hrm=HCu=N@VCwe>;-=Qw0sw)X4ATBx=yOu9vE*2&fZ(L}{ z{?`8IQ^9xaNn3<>$lH@Jxt|KzjXo|F-x}G0eQ!xgv+v9a0+KGAHvZ{LgGX;$izZdW zL5H*FDEDXxxJv+kvv2?$QDI?;f9L_bWcTe-ll~6^#0AQtq8y-oBKYiT0WUZyb!T7X zgJ9TJW2~Ut8wMdQUqLgS2BHcK&H>oN-HreJI+{$uXR|i08Vd^IMm=-FoG73%-hLYw zN5JxydwJD}hrqDq=_SNti`z|sUAP}ZQRlL(n@~SUS)q3?3Fs`<>HfP!z^=jd*#mfv z*K+k8;%ca?lRQ;7ke9dHFXs;<9&MS~HZrJ7=J}%PjuRwY7rh*$&4M|n@!W2`VG`of zhCA*xldz<$cyb@&Xx+W>d$T0+b!oPy8{enz&d6yO(E`(mD7 zR^i;@1on2Z_9~+1!K(D;bmk{ha2d#bVDQ2gd?c#&n3EoyTD$J)e#})XzhtfCDQHu-ZC@WiK~2@yM4`(BES}yKzwIgk#?f9PlGh1P zlRK67hFH1iUb>_n&```KwdfuyTrk4MY3(^)Qnlf8^jo9ro6B zJa6BM|A+mwRhd`lGWJ%rfJb{1N!VIZs$7{u!rU$I?emz+R@X0wtihiPCI6_}as%`0 z8vZpa=xeSS@|LxGNH9&FY$4zuog3Jcx6?8uZIpoh924CKJ`->{c&@b{b>MJC zOZ856?2Xwyr?t_yMXC)ebahDRTYOPc7lgWVP+~(wH3?+T1h)_N!YeZ=D_QqwP#w(` z?Uxz^-3MR3@>ll+vR}E(Sry-y&=E! z=NUVWATY@pAd)g@P=77{g*-mD@#tymNAK|YF{{hEuut7myX96Jh40U-UwsPkAV0jR zKZ|(Wt)LtEBOZPaWup(r67X4%v+Mx!=FiunqZPO(DVN=Y$8Ms&{-rcU5f^u{-`qd* zkUzFVB77wT1nZgDht?1vm16w59r1Z_$i}h{`IL05Kv3>I0shT%afO(B7BYCw+aMmr zyApeOFxLpHO`kc0eEBWC@!c@ylO*+9vq!W@D5!n5`B*ZZW0Wb!Auk9{vK7cuf*bRTv3f}nTbx8%hD*b@IKrL8IyijVfUJD~qv^RTsl+eku0 zxwqd4>euA;CWcGtIIUSz*CGyVw?kdEa1O{Te(J!veqoo{cEm%_dF*sbGy$u%3|tN& zZOIZ1woFnH;_Vd`vP7JWn5l(2 z`?5=o?v6YPCMAsn5uesQ!1LE@N zKT$2w8`x(yem4rsB7oh~Vj-^%e_wA$yYwppxYM6`JX%G<0GGmdP23BW`0`CA*b|@p zaO>^IUURmp#LQR=VO1Z(iS~AbpNs#`P!ax8vN(0qhA4&PU&V!2X;$x39V#bE15l2}cv2+fb?L z(7$QIUCHtfmkU*g;zw~Z@(Ig-#aUO-+r^GJ%stEy-H-Ez&<~AMINO^$l>Kq;xY*SC z45yge+LLP#pO#zn2TF*~Yt7@XG{mJ>dDW3Om`=h`*S-aN z-sk4vXyi$?)u>=T>gaU&it7el612>IF^i;Oo-LbHcrQyq!>U_#+3V;){LV8?cuK*y zT0gHD)VpKB&rHKG7px>*R1w%kfv7UeEuJUHs|)TQ)?j|_;byhHWo#YGwytfAnHge; znVFeljM*{8o@QodW@cuNnVFf{F*C-@Y|qcxXRq^}z0Ue`rF%;nv(-{PHQ!O)J=86Y z8nr(!Ldbl*jxv@XIn@wPw^j$^UP5lY<#Y?A(j@uieF)qv5+Msd5b&^2`I!m4{~EvP z^Ap4PWR(pLcyBy4Aau^bDd#G7`E7Tv2(LU`{6|>P5?muOfn6y%iBbuJoZ;dm1_cql zmnTVuqB^GQT@Y@HRu%g@LU=#>72`EQvKa__saD`Pk9?it)H+tl&B@ z4!$X3!CcGj!#ip)t4%pp`g2h}+-%kCOky84$L2OLdzzCV6%E>ANlJxhix}_tfF4SM z&*bCz^N?B7EI3F0v;gp6lOq8hZsxXfNf*G&6w z#j%HOa$$DQH4OMsXDIz%PXhm$X!TxigZo#a6~@PA61oXfI%sHI~@b>KZwu8`>LtvZ7fjY;B$tlz|wUD6nhu zz6utvYvAHV$1oo0MIemI<|OP*&7^+lI^G(t7_#?jJqv0SB`K)8o@AbxVgcrbdlAW# zm&~88fdpXHf}>Y7epLYHXtiWpH~o9;iD>UZ2Mzjnoe(!--Oic)WQqDKjl>i!#K}|1 zt}ef}PR<5@-Qyz88mH7^L1k-CXgq%9iTGM>$gZ^7f&nx;&jexxGo_zF?~o&L5m8Rv zp4m?wrr^hW&uokPHlLRAkw9MwIFG7~oPDnz&?o(++GUkY-5IauYuX;rgvYu1dv;e{ zh9}QVFX(Q(C^yGzb8$((pU&-v69-NgF5^)d%^2bh1uh4BA z2`mHVCWW5|*HBx1`%Vrh$7~Z`6g9EWpB=ayoMzxs3A@PQRsu<8{F;U`G%*I3^);n6 z$OJu3jyEV;3AXNb-J(PNh^cLVD7!t;gS^X@nRLw5LtcgSbQe_d3pyrVumX1wy_*of zq4B#5dtOuOZZ-(6{j4r)6CHtrfolwf1!gdbo2=OX8pnKa;DT%lcI!3Bb}1qhLJi?x z9hZ{WSi1ZsKz7*9v6Y~S*)ss1JShuoyBh;nr)cT3D&zX1zZZmK5@X|LL~YVI2*ZZc zjuXZlwSDuL%#i$9?dtstbygv@pNz(<;9IhwiNKf%rfN>~DwZXS7&(4Uf~+cU;t1wM z7t%`fqNX1Fo2u*umHS@DsF&4{Jwgl{ZJd3gbN7!rS$`)HIQ-4)O~OopqD%ejF4Cck zM#{F$##ZZju=5hLVEnc%-%>^1ui#K`jG49$ZT@Nyyiu|5Ha+y{FW$^hOv|5g3HR^T zM=$sd<#3Ybzh4noZR{?r0(qJI3tb(*4MO2(unp8{H|IGR`KT9!aWuov zqr5_#*&JDYRHQN2?5|Taj#s`F8fTWUFIf0=A4!!xlU~z3r6;H2#YzB{SI&P}eZs^# zh;okvwsScK9ul7XO}=_%qBb2l==V*uc;ff*n5)u};ZPaE(;u2Qi=u|uybcVVMa%fV z$o79=w_9F5-MO>a7lY@th8I{rJ|kOn>7_flen<0a=6L_itsDMAOMardH|(MxyiUUs z27RC=IkM4m4AHlHZgjOOBhRAnU;#*AaZ7xJ?XRNQX~`m2eUCDkSHfEH9G$@AzmcM0 z9x2($myEB&r)ICtVK2|mF^5LVAXNr5L*)#-9sJx`rB{=@Mfs_BBizbUx;WUxz5C)# z!NXsoraPhR?zYH}IUm-Xikc+Xe_db9U8eiqEftvoZ+rFWDW61fH(y)LL04{{3D2BT zVB}}MSYxllLRoA!8cg$;s7M?!Qg?0|6X|w59c~4wg@qaq*kRnZe7p@d_0wl;=+R4| zsstEqKDB9gZ^8RW>L$>l?-^#e`AvZ@J&9Bc4ni(Nj}g_T%~&RgZB#T)RQ^WYho?ww z>+%BHlH`)zpEM=+2dH2hFDH~9Z60BqoA(!c08gvnkn0Q_-l#lb9G)8Kc8iX>B)^VE zn{&vl7Rw!f!-sa8nst{d1PU+@(xFe@O6iPG7(|E64lZaQT>G4+_bp$%)Z1!gowvLYBJr~9uU>HP!0bE;k+ZkT zR*SSpy;hB(8emP*!G69SAWJsaMD?cXyWzYx$WQ0Nt$(F40VnvW^0r7iE$Z?RJahB9jbu7+R*5=mH*A?h%W#PPt6t*m zcj+5>v}T4a9vvLqPez6~O0~OQOUF082Af`R5%_#-u5P_p>f;!1eFB6ceY8}(k8B`# z9S7!~dZ0V+tCyX(a&Yv7$Z9ySw~&PA$yT$mAJhIlik#nAl({c+%3;0i|0>dUbtxh|*$c zBcR=tX=sE?rx)e;U%xi9{HzdG(ZB%+Wa*jJ+WDlRW&-m8J1D()K_jK)?X1AlQH+ON|X2*?F^w?Kdsw#1xEY4Zto7ChbWr9}iE zxLk+L~6DpzhXOP$CY3j|Z(9mLa3 z4f#lL%x#{{A8HhMvER}tz!-RM#XA8NZ@{xVk(mqi2Qpd@1@*z@_Pu&GL{IHC#YHQa zhL0lBs^(yGsJADpfnS(1lsR1O7vOHCw5kLizjn9cxgB=RnF|ev?aV*%GQb-=%~C8D zeiE}2-b|ox%j=5xK^!>)a=v#G=*3lm=w!wVQ_jIRZDzg#OFZzo4FDzi@>n7=Mz68O zfWy#!*{q_on!u3-o#3@dsuwu#Egeg-5Rx+9c!KV0E*$Nt$7H22NYG4uevVG#A*Y0i6+LqQ&K%T;o|v z6!`>xn{kVMw!G+>s(|jko1(VMfmy{Laa&8 zrybH1_O_nLnL=4IyPQ1^_xC4(>{jNLEqHFnSSh|;B&cD-i$z{ue7wxt)wq!Fi_&I5 z3%~(%|e+~Bp?Z zgb7k+m8%Nzy+bkutljkq2|egZ!+FCW(NkmcabvLMfBV~Bbmx~itCxP5kmIIGj!H?p}?Jb{Cwg;B=y%mD>R7~AdYc!df+E3ql^(l z%zFic!5t-CR-+y!QstR9!W07ix$7RqhYbgrj7bL-XN&&Y+?RT-?gOiz`XCmqT`c_G zG$=w=W^iOSq3U@R4C0C-^0`n!^fBE)Z`)k%)A9&zaEBrPG?9~Ma0)fbc#%Kc>i)1i zYsZGmg$An)Zd3R$UcWCPX)!ZP%(jt+WX@77Wm{RkLWxGJ^jgaW|x-7Ly?W{5UT##%4x z1q|GZw5?gfgAUPnRWL+xOK`toocCtT5f91I(L*wzIjtd0&z<^JhbD)|EfAs)!%c>ye8~4ok?^jb3?=EM zrsfqDN3m`^yFHSmdxrI?PfL5G8=-|qJ33YfP^?2ClnPJX-hHl!4!Pr}szspowXYj$ zgXuh<6xp+$#p|$6B85QTOaKELPTXY$aS?+QRI9bMV380u;Vn*jH>p7IOW$;%bEZPI zIjSZdzWx^G)1BIS6GTX(nGW;XTJo_bgItiwkxN{zGN+h-qT?VOBE7+qv`Pzak1*(t ztR<8*EaaB)QT&yvLd+f!iPu^xMEfAe@S&IGq-(E@VWcrcxms!3Bt;x?^H#AomNRQT zTNY`ZCyDR&*nq9LH7tBT_wi+bk`GU)(NH4`Y(SDbHpDI8Hm1JixQ`+!XVG~bXS^_E zBHbuva1!d4=6U{`I;`^6>Ef+Pc0^DW!}!IDcT|A=0_qr*D6{f%iz%Z(803a~sGh)Q zlH5GTwr2`^T4YU(+OqfXfPnli%{-%@I@%+6Gag^yjl8cv+qh!4jii=o(wOJzOlp*7 zev$U4kdt=PE|Kp6ovngjkNng^wX+vO3sZ_&`TAr}gBf9>VNEKTe(|>D2NrDuuQsAo zz9dbOn@@I9P@u_6xwMG5PcA0pRUpFImucgokupy$h~RrKVeM?_k3F2{{Z^r;iS5Aa z0n~*vCVh>oZ2m4!022pOKJM0{U=zve^vW(ml){%vx9mnt?7pS4$K4 z%L_$8+N>niz{A_C$B z42$I@I+4q`x`ng311O5ZRtY0%D4oQ_rhWZukzaNVMf6^{?7XbEzZqn^pJtbeYgSQh z-Xwk)4m_e{Zk0OCqHf;xxS9fgt0!$wD4X^q=Ru9$h?T264gE^BQkCxh?NniVE{iCNqw-SI zOq6$M5{^9Ko{3S*PMl=GAb3x6BWLvm;%KgX>!mvOcH|_g`~GLE6+i~Q6nflQYOyun zx(k%5Ak2?%vh6t_Y1byYdSYz5Y*LWtrS@_2CuPQtNAhTGYIK`0Vn+-?cn=r%L#gHQ zNQzY9XS>JbkaBO8Imh;gUL3#8*$e7!tBIyejM~PW+XYE&-o%KPuz@;+DYf0NJ6|5k z14;T}Z{j_jES?J;Dle!!Ho7P0KE6Bg1Er5q%t}|Tj!LzDzjsGRWYa+%U}Zn$%WqU_ zGR6l~p-HNz66VEbp+1g9uOjO1GkfouIxmeXZ=W@CfXWRJUS!5n=tUU3HV(O$lsMTO zXI`k$R-zfrb4nrTYI}-@T+^Xel5yUhp4)Zz$^GWtL>wgPNgtPW=7ufS$4U}&+pVs8 z*(*=##QM=SLxjJI8O$L?`F3Lm)e|-6J^OtCG41QP>F*6?e7=Rp)!zz~&wLpYT)8{J zcRxv1i}@8UkF(7X_>7qE&ns<`mv0w~<@Rt@&>`~S;?oqB6Zmm2yi__KB){ChrYR|O zWchI4f%XJ);QKhMsKZ#N2=W&@(I{mg4chA`%d|johIIZwFOTY%o1w|HR)ukS>V>mq z5@SQg!Ong-U=8>bNd6Q|LBd3-Y%2b^3qsnFK$gBIZ&Uo^+d~b^m$>Az`}Sd?ys)03 zwhR6I8dIlnWmR$*)u-p+eVRN4;=2JRqb!Ki4(DsC-?s{D<|d^YCZ{0Yq88&7YnGQ6 zEf}527ZKY%=c~SLVeo=vr(g^l2qO0j#6eTW{+I6&a;3l@$ferNGjsxbkba?q7x&y` z+m-HJfNY?wux~hm?XP4@`R!uX#P1AIy=28;DTt6dPrt@DR_5%wcjj}Fhx%}40(hmPHvtwXfyYHMi@bJ-Z7)|U ztehf3$#AV`wiBU7CiF)$L0wByx^xkH+sbhwwlg$;c?^np%S37r*r_4A|#YlCYl<6uX;`v7b%hs(>?AUWY1Z`?5tJAnmPp0KIA5$*r zUMWh8TRsQqH%)2_QM~+WZcX-YSFkXf0fkYgK(x9a=LyNCNk$J&hm`0)0u9VWY?odl{%})nI-gCalr;V1S0h=i3P)s1SYs`ym^THZ8?Va~V;Dt*?)JA^MZ z4uhgXEA!BM-HJ?)hv+$d3hz2PVSwb%h}fPt(v0Jt)I%q@PD@E(W*t^RKObX;n# zd{&$4?H8mb+gCxBC1?EF#^Vpz-1*AyK=WGTu8g6;cB+hhP1_4 z_OY5p+j;8D!rK_6cCI*?c~C;`_b|>fkuUKE-?AV9 z55EBO;Vvs!(XFzIJ&ZekOTOz%S`lQ}9W3N=w0uuW0{+|0FQ!`sT5_!66#c4+jlD*` zcbDFMeL}PmaQb@p$BZ(&zJlkWD2jH(J|r^#VKf*o8!ovU-4!Yt`Id)W!+ZmxcSGvi zbFJ#0*V8y0ftBZcs^vV}gGr(v8*GmDjuS8jl^pHUYx8(J#ZMc4Ln{)OQiv&M_rWwB zL3BGk3f23IZFYs^a`Mh&;CJiqN7ruQ`mwSQYjP-QE*nOmX(w=Ni?S$8Lk^*qzwIH) zTcaFR*117`T|fm*a&Y-u*i>}4rsK9lW#_Ksf4{s44ZGlnG0!2XEdQ1mc~VnAMvJ#9 z)$_K-yjnSztD`UPw%21T;KAE`=xUTo$Ez;^ieZXna-ZUPdMgMzKX5|O+`cW;>aL2` zf`r#M+Vz}%ajGY@Z6<0rj`anTSKhcoZ3{Hhp_Bc@9Ac@3z|MO*!snWAnIK*$OTIAo zm-dniw%P^?dLRI5E0CQU$#McZJ5Uz++G<4?ZE?CoQXDnF-NDKfj=cACmcCNh#P;yD zLVD5E0aPycR}XHp<0YmCOQ55ZdAn(8nX?xWwnln55;+UEPQ};Cd24soEid;>=Pcaj zW$Ry$ptsoK4Aj~ywonHgV}Btb|62~ z+?u$p-$ayyF=AU%;}J)ckrknuoomNzy68kdH`A@aVFz9`Tm`(aNPCJcZ(I`1)cDW! zBS1nh1Iw#d^bvq_lB{<_wX9e5XwR}HwmwV_ntY%x*=^qQ%QzA2L&2*XMdEqMxz#kE zs&h-S54mO|90wDzF`_>&ZNDw!nWZ7x z1=b=@>j4{TR2x7qZ#T~|B!_g$!x=A;d3S=I;=V81`E}#~Kk`=wEE^wH!xT}Q=Fp#1 zmx>J7=yZ(-Ck8u}Vq;QS%5OFE1h1zbnqlCY6pTis;w zdZ(`EQ~FbcCpiLxh{eE+#vYU&`@x6~-s9#yd=SnJ`D_lI3P0V`|Jp61D5_>TekmM< zKw_QcFWwrt0H+sOdP8nWL9MS6l7UL1rlmfk-fJ_*xdZ?qN~{v-pjfO6$_ zQ{@w4F01WwUqJ<2Hmf-cTtdp+osH_i1eD%H&jKDJewB&u&^>#8%u)3mSnP&dR~>79 zx33nB9v8C0Lt>bhYi&fIG65M3)NJYZ@6*|B8uhkCBtBwr8TdZBfL!$qC#Ag&a+!l> zKGJk8{xzS#F#~q+CG&}CR8h)?=IDy?jTE&QalsiV%4QN+l@u`CK+pDzE%ugF_A_-o#w*;jK?l&sy=>?rPTZTp6N?{xkW zz^ic8ud)z?#Gw`Sop!;LbGPr0C`c)%UKr?({P@&%4{hetJl&@wi?($?P__)*){n>I zb+DP>nuWVY*29PD05~Jff>njh<-3k}%`ReO=Q|iUl&>{;^LQuF>Yt>qvSpBn7h~fI z0CE~4<8>nq&ngVaa>{GtJ)NBDn!|E+NFX1pfBk`Untjt*b*EoWCok0t#OhhZF^3ZM zc_rJuh&D{n9Gp~e`7JHYyO=%ES^BMB&Uk-=yby5kxwvzIK+`|1IuwEvX_%WHccw#P zqboL^VLA)WrFG?kS72UdXtyQXQi0$C;1ZC|Z9wKNt7z5i!0vhi71Q1M8P|vJoaBBn zv zbxu%NGf9idwG_bik0Z0_t>ot0V}4HS_ zeoFtSh8h@Y0s2%+G&}|HEUxNum=h+riWjGvM+13J>_ld{FJ zA3K_74}Qxzwj*D2?}Gv9+LLQv=TnTpT1=EtRy%btBo8j9gn(r?6;ow#+RcL?H~EV56I^&oYz0C=MLv*#-=Ve?;wP{UGFo%n1)4|m>%?hb(i%{ z3bWk|9@IT}^o>_sR$dX(2}n}k9*u^D!HfDHh3gj^u-~!TZC!m*l14Wu%J^2C6$y0) za*P&kM0kY2WcO6XjV?7$iKcBm64TM}60>%`yH&K=KELz%Q&kaoM3DAC@2}={b$BHwx>#Lk9W1T@BanM74nmMpS zQO+jh)&2=ggw+#~yl`NwpvYKxE=vTgz`|3V%Z*-cwLx}`GbxM&T=cEtxuUMrMamVt zK16%MfdH~Y3@osvn7;)`i|V9Z;`ZD~0xJufUYrpU6@X3n{L+bJB1lnCn*L0Ti+@^$ zgaz?cM1?Cpe=-W&CMP@9fEOS9aE?oLGI0pZ)0`cA9Rx!*LhN;`iozF`X}@Pr|I41+ zTU{7olz%E0Tqmp%tjF@5hH4Ex=4jtNP_;7+`Xp^RLGvMmk%&py{Cf`AXYp|5bq>^V zsU%i!mBL<>s)EdcELMs6#2RLIJfy_vr!=Z$(=A&HEcJ+doz1<}C5ebY6bcx=ONb06#d&IJ5Vu_2eHNw| zv6BgwEA1)ZWy>_C*2}{`Ikl||eR8Ji-sik{`ASXjeLvN;vW}wr{H>SwIGT!RCOu%i5#-yel}jh4zA!L zGf%u3K}1K@UKGqfupc;|UO(ZuNh4g;o<;dlL+Rk=D@pe2pbOy!D!u@6JuB> zatau*s^I(5W0gIoh2C65$Z~bX0kf579<}TNHf-p{C$A;}hS=MoayLeLwC4;gI{OAe zk*_`WPWGjq&Bi>OH!R?gFngRYUDC(qphgZ}IF!+_p;?uL6_t`597Ug4dDyjvrJ{)l z8ap5NSm`mu#yKmh+ESdEHh6Nji$?DRL%eg~$H5zi#5ip7S8J5f}RN9bQ^^ zx|dSNZS7gKvm~SU>(NX&vCuU7Bygsk1oi#;CZhh3+;3ELa(4NhQYU8gdQK(^l$*+y`fQXMpU5A`)1=k{C0b%Adz#mvK;)~j z#`F>n7SCNz2Sl9hcAno_Zh)pv_`&jnFr%Qmb9&_PeqX`n!kvh(AsC{|FY)39iM)q_ zJ&Qi}_M2>nhpPOPl<&+hD!7UX_Y+o$yB5J*!3q90=ax~n||MOhm>SBcAQB!CF#J$5Lx#~1|p?wnRHDMA+*^TDO zye@1gIOi!kLabttaPKA!S}n@yWoB}%l|9?JW^%>^%WIWy$|Ylqn4u(o^uKoeE*9;bBxStd*kTaZc7GbrUTAH#m^w3oxR1XMJ_e- zE!$nx$y$^yhf|1U4IpY#>G-GBQed&GDqf?SB}AGXa``P^AvS2M-cT8%KSfrF3!{3q zeo;t$XA{Zdt`xq18Jlvrs?nMFdG;W1 zfa0t5*z2Gkg`$@D++4w|nDf$;m$rPzc=SEQAzD6H$k(gTK^z~UAGd1u7@*YiwSHnE z)2uO7^P5Rv=&zhofvyLU9wWraEo{xrn4ncmOr*x7)?CxGWeknx&k%MrND09%M8Oks ztt1~cOn1S4@|7&X+zjxPq1Ac#0^UqV0}PSnyKwB+xoVcN7TwY$L_l7ncmazX!a>*y zi}OXs+d14{(sl`qgRK#{J@m;|w+p&xHeFdb){2B*?RRS8KfB$mn6zq?&_ly;0f(hKHuRR!9c3;8g441iv$?CT{iA!hp_*7qO>rs z;Wo0(@sl%&5YKDRyk6n3ss9((j0GeKSQqy50KXmu@ai#nxw&^e|It+eYgZi%q?=?1 zY6hpF-q!9#n)t0iSHgVw3oFk2@j~f^QLFS5JiF~)0WHA-;i@BlFa!f@oMrZLG~JlU zaPHA~knH&sMX9@Wp>_%G>z8UQ?nLD(@dl~xtn%Jny&BxpXRx1>XMPt94;IWDG;Y?};67qUIw zaK)e>g)h$LLN{ot_cWnb$X{+Q3j+_xo2-LF)f>}DB=apm7sOvTnELvr+jh236RAXw}r? ztIL*+3x#Te$H*NfRU#-94m(s8ZqUq9n+l~`z?c{sEyN%QMO%KpCS6fIBzLtQ?BYz( zd)<~v?7vL#=%N{~=lZO$*DmkCCO7$+rRlIPfH1`)Y3|gu5NM*TcUes3A6HS zat7IT#3)333Rxt_~+VH~1Ua!b5ytqLadEs7eYQnSY&@h*ua@Rv(#vB~&*Dn_ex$NrPvH~+A> zhIHIiQO1qsdb7AZKX|Rkgc%nGQfdO$-;J^X!OW#CwaL7;O&K+vziZj3c>@lRh>I9; z3v*4?@sXTD6_daLl+*4)Bi0vvjKgCF_2UwuP~5`b`FI_C{gxsRJd5>(gbyOz`YD(p z!U@`bUk*clyYY9`~c=3=5HCA~s#8hw1ch%Uts z*$o8w&us8M&HcB(vca+cM>e>Hu9bnMuATY+ObFlluRL&&(tjV*PqB{ikVl5)2jwfW z;xsaT6j&AJSj8LVR_5nqB>|Kb6H>6U0SR%is7O@Q-ymVJj7c#Iz*=yPxM9Dhalg{7^NI6z;%Q3O<4?_4!hKLlAkfMe)M3B;WGW&T1 z>JG}yBhJ5qkc-DdXXdrhBPg;jg2e1v2t@%3aoMcI1W@5b2|OZhuV@+U>D4Rj887~g z_&1+H<+;ND@)`caX9qh&fTf|X)nAG4A^-MI`v2HZXX$Y&S;(;ea`XxJUrwTf(yJ5) zZz4EDaDx0z2%sfB@Gl{7{}y8JVDNVxQva89RL<38Xx6*jeg2yav{^@=|0zQZU}S1% z@9_UjM%J&|-9=P1{J+WYUn-sbO9t%6Y={9YbpIc!pvrWk$}9K%b8~=#(SiS2Os)`acIB&_BNbfQ_BClcANamA)YW@Mn(x&Qot}h>m^q;R6hE}eAs^?ydS5^|Ac%FQgHVA=sx^ma3A(p^8p?HK!gDH z4!RbG{{&LW#RL8V|MS26KYf7zgs{e?s1;~}fan1JE{_um1jOl|5M%NA04Qt_kUzVd ze+>KI%?A`s9|T18+dnaX_DKJ;3;O?N6x~0Vf0Xv8WbvP(#Q&Rl$o^pdRlxY~H^-mV zH~(5t^Rc)Ftxw&)(K>m3x&fkal6SDt=;H?k)@bbT#52)46|3Adv1Nh%Z`4a^H z1h|k7ga7X_{s_^#1^JJV_;>P8)cO;W{*eE9^-umy{yT>K9sCn#{u#8p1NnH||2yjZ n9sCm+{sLkDL+jJOz`x>zyfoy$t~Uw@3drck)8c*qr}O^@#UijM diff --git a/tests/_data/stalign_reference/energy.npz b/tests/_data/stalign_reference/energy.npz index 53bbbf7a9984fd8d74da0d514a1bf467029b1beb..b06eb05764ba70b1bd117faccd5def552536f326 100644 GIT binary patch literal 32094 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 H=hOcI{{r3> literal 32217 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 diff --git a/tests/_data/stalign_reference/primitives.npz b/tests/_data/stalign_reference/primitives.npz index cd8ef0bcb18185a707e43f171f0c53983902724c..92b77fc1bebac7146def5ee3524b00d87823e7e4 100644 GIT binary patch delta 80563 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

%#Wn4&29#!kiu?KZcr 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 diff --git a/tests/_data/stalign_reference/trajectory_n1.npz b/tests/_data/stalign_reference/trajectory_n1.npz index 23d393cbe3721a22428dd8d7eea640d8de7d1c82..604b19403243929f4fb6493dcef058f38486e452 100644 GIT binary patch literal 10243 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 E0px4|VE_OC literal 11137 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 a&ANZJyP6W>Ul!1R24(zn`>~iG?fe&NP=jm$ diff --git a/tests/_data/stalign_reference/trajectory_n5.npz b/tests/_data/stalign_reference/trajectory_n5.npz index 750ed2a325cd25442592e179c69a7e32140fed8a..b07e7a7f746357c8a324917e3646c4381c29c96c 100644 GIT binary patch literal 24184 zcmb@NRZu1UvgI3S+}+(B8uzAgcWB(*b>lSd?%ue&ySux)yKa2Ld^7i)iMi+X&Of3e zU+UqPD_2!St;kZ8f%wk&?b|n)e=gE*cT0l`U;jF2-!Q)E>pM8vyP4V<+8LYb>oeIo zcz}IF`NsagZ~o`}0{^dClq_ez@Dn8@)!I*NkdkCRZctxtFqsIYNU{-HKAw=TH7<7C z&A;@#W*3=_T!0=~od7BN(Q1W_@5cLb@_43Xqhdy1zcu&TF%277j>iNw0BG6c$BBU& z0CroIyR>X-c;sbFxTs*0)l58g$K7buNCcMjvH6fvpkPc_mr{my1g|`;u3YmLO9tRKpIF9NhI3T#EZCg@_>if%t zrx~>#Wdrt}paZiT{6)m($H19!u5kzF=pMsQyJ-tVaS1C>bOYZqT;{L?Lz#8pjh(Ie zOSV#pb2s`Em}{$HbS?6jvEg*=n!=vUD*g7~Oq|TI@J;p-zC@X&Z~V;NvQFef)r5;{Nnd~K_x|g5ck7jIQ$*K`13MkQk{v#`PkE%xkr?my4=sGZXP+)R zXC4j!@?sfh9oAq6Wi_7u8LD5p^*hNi5lH)RryE}VDi#H_4tk#!AIwa{D>~fUIT>ua z@$zQ^!@YI-N)e*A|I%;fNdScF8oR50VV!DUos9UUk9WoD_Xk<)k$1T>Yd3I5dFFB&|GT1hzQ^ zRw4K+p7=0t1}|I_+#y7DqAn}q`EerBpR*%$Bw4hrvE7;#=)Bi<(#H-r7@um$Up3bJ zfAe(*xfP1Be(lf(&N{;(xd#at)~}^I8T4nMS7M5RK@%}dy!SI-oz@x`J?;#Qppi6c zR}W;0b&vH3HLok{vQ!U@B;3YmxKMK~#gy|Oc#w)^ede;472 zx4rd0L}2~52qga!q2qrn0@VK%K{`DJR-eBe>5pZ!8_d)kc%D8x!HY>>-iwnFM-UQ& zz-^&o?^%KqQIKAiyFtIB1k#LeDg-3fd$&FPt!vzwSC7WCd_Ba@-OcmhBOHl~9&V7H z#=-1oD{pQ`C+aT+d;i&u4bn#-6AY+pWp8w(YeTz(n)^SS2Ei+B{<~?&eMFZACm@c&39 ztnch%Xk+?sqS1em`2J6chI!$J|0NicO!9$Br9yYh*Fb{S55Io|yGgau+4NjmBz;)J zzItH!05cHt+cWN<6R0@j~O&1|UEEhm78#7J0(yok5{m&O@g>-Q ze;gIonP~oBX19Mc_p1LFY@YIe)uhx{A=R(<;VTMEMw)Tu6bwsY3S-*(B;_%6WhE0@ z=}3!2EIL(+d>c(qnPs%05@sT1UI=>rijb?Z)3Cf0uM|Y zIV@zpzpMX@&N5z@5f-bUyxUDqJd!lxwngWt5ebklsVP_ES&eF|CYrJO6En1|CY(#J zsNhzUg=Z5G3ihhTqR-)$GkYmLZR;O(@y=ZBL2)Tb;n|gCA8c_{z4`|U?o+zm{mW}L z7+MiRhN!&T6Fs6KFf zGj*FoGs{!72(y84bo}=gi@@dGZI-?YiQdT5-ziu7uU1=^ig;oKmf^!ApHSx9$fx-Y z9vB?|_W-%^6#P7b0<@QR0$A7+?`C*rgZoEj7t$ovGLQi>PB%t;mfp)s z!z3Bkh^>+6VeyK^CTFv;vk!L_P{q9IF2|(*sghK+J$1xO0m)$ji+^l91#Jszro^k) z`2H}z`tX-=C{gzS4XwX{V}jKGz5>33O52(X=6CwA5LN5O{D-b81B$`$YU zZJ@dR-B~SO)`5=&iL!c#jI}wY?^$A>+v8Eny3f%IFACSCG1cY((+D+LY&r=$6Ug3tbf0)D&I z?CK^ ze6uIa2(rQ-&;D|_NaB4O&*;A5eI9S@m8^u;g4tEn8 z62Do}w9k;bNrQ?UO$kd86o>2w>z|9u{vUg z22lTinJbxLHX;5?0y=uz*!VF3BjzmT?e_YEzrJhXE{LxD4or%QP&1=EgD2a*-m!p8Yl0 zoiE9|d>V93bGtSqv#*7{tF8RQYOp#6XK$?KtZeYHA2+;j5m0or4 zC+yx6D&oYjPc#fq-pk$%A)KK{0p^fIE$-GzbGew~61tsOR+N6x9Qb<$Po@tOJ4$5+ zukj_L)-KNp2cwFrguB>Bm+oW%ZY(^=9!ZHND5S$R5%o~K^#KVnx{H}Q2%X=>#sa7q z@fd^0`6|+S26D{xphvV3rmGUNOaFWOvZsw!227JpuPeF3Fpm4GN#1$lh4Di{)^+^* z_p3a??j8}Zas}?Sme4L>WpQHIoMG$B>oQaFYyb&nyGw!FP7}If<#qpNt$lV@CZ>l# z5!q?&ittTqYjy_njaz-^k2hb30?(ChTGHEbw{JVD^|a6tk?e)&mXl>XVHK>`lfyJr zkJa#NK1YCAPp0ro>v^vWBW(JsCL*J?wxQ$3q*uTzde0GOhGSThv6O*AoQ}Pqp{M8l z9ljRyrRAK}=aosnrowzQA9*%S;TtD^>(b8`)lAf>16k;*h0n#BYAWztAX-ldIAPg} z>!;3>6@^X&_nS|F8?|!OhVLruJWLN)NC$GiU?j;zO^fY|ui}F+jUB$}#)Bz)pthg-R!JA!6g}~Fj^|t$v~1A|U3>IsTyamw zTRs@}&}W?jXa%-<^<>|22Op)2$^*2O*fra!fX%Cx`j?xX=K`46%y7W>cP55Unr7|7_`8wwv7?m=FN145kPF8h{4rMyl^yLFq|ozp z(GP#W?+t@I{eeY-VQ))mTFA<*df!%;A%cIm95H zc#%T3B>{`{+^*Z{B5t$YM^}RJicDIc@_2OYW*E`Gl ztcOJ3;e4LUW=1ZZq-o3Yy_UPO_R zZ?qy&OJtRmRAiM70qs z#XhoR)?S{7pMWu_?x89SrOn2{wutb;3>{X?-V3FG0sV){TtrE~zf`71NW2-b*Ga7k zz5E*?u9iKgzh+5V>`>2o96GMf@n9BsyyJl`JZ~?hJRi<*7G(T${a7^t@9{4|Ks8hD zNM~d2g=0FAO_ouIIS*f~zfCpNLAYS8nEpnogFDhP^288i41zz#gM%KuB^>Dr6IZi6 zE`+&0SP_{&7;#Q56lbpEtxWnuncR~B>r*9vU@R;^PU`A{`l41}N_f8L4{RRTk!&(w z`YB$=wasbYpazm(BCQ$k9t-G=uKf7>YA2dVrU)EN*?c0GpT2qT<=MwybbF#a-Lsd} z{jU&IORfssVszn`&&$on>A$|;RiEf1bEf(NCSur-$hTT4I^^HlU~n_t)oL)gT>6}I z`h^yxB``Ebrf93=VTv{Dd*%tg8*wzMcQisMgiY!sI?|{{t2=I61u8hM4`nW?%qp+@ z0eiFEUgjInH>ZyCh&9;?rq5dpi2!S)6Q&Nd?!Q4!7vQd)dY6tE_>TS;v&z>6dReO) zI3P1FKBcKaT8$w)6ezA-*x&Wt&*;2Dj5XXL?HLD1eO)r{$pCNZQEzZ&@U9x`&nz+1 zxigmVWGV0C55VNODL9Yd$|2E|Lc#6tJ>d8Q$bEtIoK=}Okk6(X?=ar%dvu!@t;N*e zSxFJrGgC++ge!DePa#NDNC=ag>AsI}`)9XOR?_O`Cwme$h{d8;be$WV*%wg*qBw@1 zaWls7_D_btrzW0f7NUuy>3PNKNmu_^-6zRo(xIN z=DkQR8?L-6wX&?>vKH|wE|$<{Vhs(xrTvzwiQ`;c@7`ZS7la>6UyaYYy88W^Rjsbq z^?f)-s{Y&^ng}@hf!J(BEg$8hsn|9bY&kW)vLeeJzWC6I+otT3wj|53YBMF4v~p%v zX@ftEuk^la{GDmv_Dg>~u>1gpyAs#4Bs*|b%krt|;)WswB(B%pVoL7I{UWk#eZjCD zlNCod3A?d=A##;R+8t-^q5nU^Yz7=<5)u5WiDDUP%dS z_P}63)b&)Kjr49NbUBt#@A)LJ19^&%oLVQm;%-ZQ#~aga*}Dnq;CUZdjzti~;zx|u zzYe+|ZB_1=am-H}#EA#nFEl_z1hAamPB;BT(EY zfAb+LT&#VxQaoFeS0-Y1%u48BwgNJIOx%Zt2k?txo`{XyNJq{x8hP!T%}90T*SQt_ zv4;c*6w<#XuZFeryGds>(7TI(FkJmvLtByGNK@ZZ>KIvdKm36d0STc&ONdx~B}S*d zk-1C6F`4$2W4XcZN8+A?d%~@rJ6Y9dnL*hL7NC3^;vjg4f~uvFJ-oShO7c2vU3;c$ z*bKt{PlURm0#pzRrp}Gh$wnk_$niiMIV%5SscH2%vJ#k4zRi#qE%Lgl#!4FQ7KU4` zzVOZ;j5*<;R_+3S=q(QQ7BelfJA!_IC@>}Ih~-s%Zv@Gx&=QkD(IO1GEk8HbBN&_! z_bq&GIg>oKuWIj_6uVSD4=<`cZ#iE&M%--RAG}AdxWucMa4b^}8<0qgmF%>>G@6802tXMkQA?${6x(zQ%V`AD5WFFk_*{0|ApnEP0 zMuEQj+i&uui^)lLvf`hAi8z50YsfxBq%S;vwOCSo-M}*2G20Oh^jl9jAG9Gt_n8=g zZH{cPz~wdStr_cmh)5kzV|lpP-hqsEeR2);>WB_x1JsLM5e~&Q#kUd#hgw*7O@Ib)sV-gDwi_&O z(IR{3LgL5GZja~EiG}C*2Z76IJrxsJe}&?^CR`x69JiicM*sAhrX`C-Mq)dlBw|PL z2=Hq->}^30AP3lpBJPmONL;1Xae|r*)X(_WW8E;yQ%p4nZ1DaN4KDKd2? z!z^tda9jdZDbHsrQbs~m;zIrqzZ9_NgbG~lG}j5gF;g6UoY26{!jQPkx+BDsX+v*h zB)-%A9mEm$+;{t@=%2sU?cYE3311`_J22d$z}9Q3y_`d}dsmgEI#_u9*`n6qk#6uD z-7BKza_JXvj=I~wBqYgy0Vq!fy(R|?HgaW7^y*-!497MLoN&w2%`h{;%Nq#cknEyVg1l_(p%UHJZjplu2Vzy%{a2Hg>_v^`0@r4* zAq9qKXFjqPEs@0zl0Rw>cCON(^Nij5b_EKi2!_GR$i{HKz@l92uoaDv^0EPqjtbO- zE?0EKTyvyZw78|GS9Sx7F~A&uVe&L%em9}Yg&^}SbmSVB+PC-AJ&vV2&(jUS=H%O$ zXxeCM)=Vb;PqIqRMs2phaLP5l1!Fmy&zb1~79S$meO|`!W5PJ}O;arM=BSav7_ZD9 z9i(4!gZHaKocVu0T^sC)0=ZuoN2n4K1JkyW>p?TnP)0pvywwUPBwAk`3c9FSqJ0JU zJ7#P=VIW31Mlm?Hu&hHu7}Eoi1pQ7Fb^#t}KpENAhNF$>OU}ov@L~I7jHH^l=WK{;8H_Z#mn~%- zbnB^_{hMzs3g+r2MKCLnHlVdZ&zi5Yv)XrIwBQAS(ve>w!=K!7b zOzfj=@!Tu0C=^#5A_}+5^l?!Fu{s&w-(APC@;wtXYmEC4v_H&^M00rSmRw_iX)LX2 z5E4$#F$mK+fQve+tSGlWiWHWy(nt-}N)TcpE%+tbR>P8K0pQwNJ(> za*-36ddRV&v=*X*X3XC*PK(PlkHJog5nRLVTj-=75z``l< zt#*h`hmerf)}w?FxfZyS1iBvwA{GrYI(|oP%E-j%ehesSxa#AYJw~@C`@R5cB=GJ$+%MZ>p zH#zxs+C(EG7tIGrbGWj&3gSB~)+Uz`RlY?%z~Qk^5iV6~^_? zhcY7o#bO^Ws|aUV`dVkhQRf0YXN%${cQ4+PMn{LSga@d1!R)`2(q`@S3dpsoboUxW z8J|95w%78vquX6Q0ODsRI`jyU$~8ug3Lz^Gne!O@^4fl~cHZAgqTkz23!2hYwWzw= zcmrZDK8Qz&YC@X{LI+Wimvtg0zPjmlz=^>X3@0;SKRUWKbrkCpn{p(+pniMs1V0Z{ zLx63q*8Z)vGo_B)3F;>DW!niw&7IM+)u0l7IzNERAP_xZ8V)Jup}Id2uRdvr{`-EN zb>SBUs}rT5qypjtZ-)L{0@OqY4(#W2Cw;)UbgiKe0;2MA@o*^I++bP+BoJO>k@seY zWeV!7g1nB|nd+aFyNQ35~$>ABON zTiyDW#2HqKyJEN|qjP({Pd6)w`xIdlDZ#;BZX$_~fB|RbAHPH)yEkSYjVgmk9j!4j zGqK$g#)u$F-^$!rUL^!fo(3I1Bnf}XT~28%1~SszOv*Pp689Y(+q&hj5<2{D<|YHW zV7MhY4r9ME1wOk|dm9WigkHk^>D7pV|$h8yO*K9uf&pHyQU2E-6VM{mB+rspDH$* zu%0VE&_TuCiUtGhunWd`E<*97(TD?g0ne%9AX2%}INRUMsNFa3 zzmVN>Z)d{am;K8)pc{NIo_9qTHVw5bsbfGM41nYw{7fOUl9G(t;|r0(XLt!-Uk@|( zHtBgsQt?N&BVFi6Zs(%ubhmu+EB%)$=&Ij@*;f9qZfU=;CnK7VKOO8S+5>rmelugarxd8$21Y%RaDv;PU_X9v$Do19|%DiYv~RkC%mrY=dW z$mqK&-B)EG1sH&~bvW|G9nF^JsvHLB0f>EP(?`T4Q*s#`qGQ~+wQ|%=+|G>!KFvpRb6n+pm4waed>fSDv&Wu7Iwl(%P_%GamLrXd;wI-G?DLN#5 zn!vIt7tD80Jo;Ep(CVeKKcn)yk3z|OubuWx^OG;;;(~I~`1Y)#tMz=+xLCkiLqD__ ztm-5&c5onYh8yQZ+3a8b4MIe6g~lCn#|o<&}=6IiV+w-(ib*fRr5#rv~C9w4^`&KSnF$$2{!!Djv7P_1r%12y9`%P*e6gMSH_&tK* zNrC?8U~XKCV+|LNsH11aohK<$xFu;i2wIf@EuQy5k&75T>LN4NKj0+g=$I)|wDIUI zSum55)S$o4w6G2am2*YD2wt7svHBt00S~oIDIlgtgYH)4tSFT|?|V>y5Kk8AQQQuZ zE6tTRq%suE2AbA{!ISFQ@K`Bc8ZD0yNKF|ERKC5*FY<$*r?5(%KK0<<9(DVV@Q-3V zQ$UJr@`Qo_2{O|59& z9fOLO+a7LA$!hm&>43`q3~4eufPp)o`u& zsuW>M3Y&_T2FkH|IIUQFU3WXZ@K*swoo!RK)w_1rNTP`NX73}>%WSK{!)%q8>=ekI z;sTF!Tn_iaOsO(S?+BTrjiADFDM`<|R$*R-WCt$6zuK}<0jq2_sQ70jrw>a+G4<8K z>J9Bz!<|OI*zBj<_19HThumY>ag$JvL5K}7_GOZAC^IhO3{(!6Y@R@|K)9hxhF2>; zvc1Znn6g4!#t2(@>vFRnFn&o-k#{#mBolAz=gE^S{Y|VY&TFJ-7zirM%KZhT?;iXK zA8AoTy(3I3T;_pEI4P<)qQABP)(P;|U)Ik-Qy)bEfs~xmC>) zjzr&jC}Ibj=I4feXP<2Op9p(!bi`mTNfxtW0M=!3-0qOW)k#e=%9>*! z1{$bnA2U+5T4rfz^T?VWfn;*&D&}EH^$^+2CurPfk5swxcY`^|iTAk(|B`)4iDhq( z603H}EZ^#A!0#Zs%i)u}&svV$ zGeB}iKxlsNG22vmO`1p7hHof^xn|sBOuLQn&a{;k6C3Q--zmx@JUI@{o`Et zB1#xBjYVj!U)cYC^O5YFLGe+cPdPAsAIpxEJ)xa-??|dv%VH4~;(O773ymy_I_*Zh zNok-M3#;4})g%qOH`={ZSCNEIO1(|<{z4Q?;@Dv~aK|wcQ_^S!> z7>reS85X^Fr z*C{|TD`!kiS^(-~kPol%3q>>x?SwnSa#kG}o@2>6(2|d=v!i^@Dl?z@L9QJ2Goc=o z;6jMSBmmd$ofvRsxzGQL%aZ1I=NyUgYLLs2a|EP6dltsi*Y!V93Amhdx9+lQT9sC_ z+~HM#(CzLI`zv*V@3rPtZ4qG^ zBD-DcwKq5|3#VJ$IvBNfE+R7DXplkA#RJhmwmXXk)tYmMzd`4kz2J5@QYarsve=ef zkO}c#5c0jt;5fgI&rU`B?T6L<=08C@14yj?kQZ;SjJs&Kq@B?J*vnh{upH)%TCbSZ zTFc;&fZ1#E3fZ6N)04(84;9*}xC6WW*WEfT&yGM9Znu91F&%Y9?Nw8p`pJVhf_)(& zzu9^i2fo<7i9Y9_SlwP`-UjsY8E_$}V)kB^hu}lc2|e$K3WA~)_Fw7cw{IzrB!wje zSkpsXL49LV!SB$iiGn3Oymew9 zDQ-UcacgHbKJ&Wx_Yat4+E5JG_wL73{6wxNyZU}U-u*Y& zDDyyTJ~D$S-am}v3nm~C>x&}w%^<=3x%(I;d3N1xBh0&$c(X%-nMlowB5nQ{r$Seg zf9sZ+`pOzYSgvpL1B^bGkw9a5o==|gckJsGi`+#&xJRs?jv0@kT-5koATsZqzV@tB zm_W8cGswnkM-t!7L6HA*nTl{^bM3J~Vq8%Fx~&7*JhpAbt6{pMNtD9T=6i@p1rtt6qXF;pMIfJgW#=F{TVhuYA&)cb zm$GfzU&l-%3k~Y&+qr8wf^^Y4oebX%62b>yv56EJ`c2IB)8zD3h)BC$|6{CUQffIr zH>d@h%h`nnB4z;Qm1nN3!}^<9#R(1Uy^_;YbD@MryY)XCGMNp zxu~0{UUQD;-kv98820nSC{9|^!Q=bmb<|-4j&2ko3RzMFJCB#`+Y^}X2g{w&pv<;zOrhKk%a&w0=LWs@v zj_a=d4D2_E=J~5>1ck`z8=$om0GzK(Q{+IWPs-FYL55A{7X1RYP<=g2{Erw!e=3)* z>*r@$8pfbSZKAu_;}l@6EdG;aa=CfmLW=AlU_DL`nK2EoywcZ>#wyr)jB%*u)Hve&w#*V1MVsSORSG&H)LS89Xy zE){a2V!IZ$p**iaEjewZp`CoieBhBb(MIe6av>TFFK+12XPILW7;JL)0K1PG!QY?^ z`MbkDb?{@S<89H8xLyS9~a^cFvoi;P{i zqSZhV$F@dt8hz{BeW_ohF|_=u4ficWb|6jP>n^coKD<{KEptfR#hJfbW5FG^1-V5Ggv}dkcIV|AQa-V3xmitB#SdHGHhYvn zE@SZ<7hCp_NDRNEI?HT=zdc}vsN;{G2@^qlJY2eP#6PhyaOM=n!{})?(;AJIJr|*2 zDceHN(3cI-@GsYgNgE~~A+0*Dhu#kn7&B(1XXR7jklhoNtMD%FVybH1xv}%W`@yP5 z6W(J^7j+GasKDon?!ZkQzH_eSr|L!pQp#5=wVfPwjIFn)>$_S0V|@C>msah*Zr1Qg z2?O$TN)2w1j9uvVhMmMoQ`X>f`puh+^r?fM0AKP`N~WZrSJpM&eCV=$N(qt9K4o{U z>2DexLNTAD-?NOZuEqV*e8Yq+mN{u zoJgrG*rlfpUlY`=CG*i{Z-hm5UdXPQbg@@@a@``4z|s3p+>=#-@V(@W_=Q)w#7Mb_pb2Ht3y_s@BiGV%1osfTeGb&LU&9&Sa-pq5P}{X0|3vL^>v#z& znF67ej0HB>W^jJJKSmM?EXbhmW(gEu2)2)R|q{ ztS26;SFTD-=_6$imYrH|$W2la#B;HKB|Tp6xA~in#vjghMu7TzYy2i9hU$yIFDmX~ z$S*A*zySezvT<%IbNK_>b8bhAe4`VT)-O#n>+X9L9zRtV)(Pxe5z=rPl0mtwY7v{bd(02OhD$X%6 zyCva!|K>y;v8MZ3|5}F*x!iN4zI-mWBoe=s#hEqetA?w9yw9?X0$^eP2A(fURLCzruS`S?`zg_*;wW2uo2 zp``M3F;cePu;!;^;``c`q%RT4{%36TYPQ>ZLFEYv+DRp!DK}3TGn61rQN!cgm9nbV zg#uFkqHhC+ah_D8rJb3RnFET%0_VKC+w|_;uiqc@Xnd@IO>v_gSS+q%hpnEzeezgH zOnLeo4+5&KBZ{&Mdt&)1;_Mh_`|HfIbg;vu2E-9l%%#-b`@zics z=#$tATx7pFRXfPgoLcnr!HDFCjgNHrhPc=T@NGHi*~7p6|+q8WM@ zYfJ|&6R!*D>kfx_N|)%$iopTzY#a<-rL?21hX3Y*el*Gej#Tr!wBF-M4jGX+3|)Efg;blUr*MA*d~p?{5p*jytTK zg-L7C4GY97HOuV13v9777vJbL?oZi$eB}p@F7jM?_1{sSLG|kyuUd~OLOE)vI3XkP zN+ojbsx+slUpd&uyV?EQtN70sz1sjamA&=vzhbBOOpCcl$>ZG)4sxYdA6%oWT3vU~ z!>t28DuJqUIWag5*W!+#ntxu)ll|nql-(JNu~SBTkrWBXKRrQ#jQ5W)s_8W@;Z(dKfW{?t)x;KbUWYDbh6T zC~=Q{h56XvI=uci6c%e7=pE#o0|&)TrUKZ?A^-!~$$}_+aHcIyOnpt-ZFZ> z@vkf7CNZ7$iSKl;aPLH#^F=l3w4K4=FQW-{E+SUIcJ#jliH}wwVw5&G-S0gh@5mm_ z|CB*YP4AQrJw(skr8a7k>`c}qubhi!mNN$j0OIZhjcjyP)R+tP;qI}el;f0?ip24V z|J>#=zb=qBp{(wJ%FoVW;Ar_ldsM*N&9kQXl|V2a4!1S%Hmu6-=l%pT7JoU(i-tsl zqX)iFxT>jiZvCg(wkI{NbuqklvO_9IgGy7wcN=0EY$O+|Rk{AdVZZ~`FbNa9)MRY^ zo5S3#w9aNf%XMou!Ypx4Zwz8Imd|S?k7GIRWhZ}337x%mTah>k%GGft1t(ODeGZmi z4Dw$8c6x=6M98lBHTiAY5Gg<`=wNW+-Jq-OpUq|^78h=%mGvyg7gt%z+@(t(jw)XxLw<=q6Kmf?hNdHv3`HvO6|-oYu?>|xM|UAi#Avn zaCz#fA&i5k*P3~9z^J(Fo?sqx=_5N|h4`#g^7iI12Oi2I&#wxx>^Md5_ndAvK;vl= zOcu=D-e0q@Q-BM8bZkMxUIo4jSt90l=_NdG77GUTApOXBUxZ%S0_hh^3B)P7O*dNn z^NAlb@>^O1d-Emdvq3;E^!m4Dq>cr3V=;cSAkU^h`zv+F^;%2b?-R3cJf)aeQhBt- zkK!+^iy72|+76pH{x#W>}U4P~=z8;ii~pAUDu zh2$7Q@Qx$j`L_F{$26IxXi^WJ;w*A}%)PclOpEU{x#76(pG=1pQFCeUS>Hd|ffW!Q zm-)j<0J!q9&jjKU=fUX}2Ea>ZfH)sDjzteY$<^sAyL=7fq2eK=y_2ih)9mW1Q)^55 zj!)zwt)(b#TaH1he8T-($lI~8jq0SY6`N!`+_?AwFdm-IbhjdA5aa=g5%OjHBE*4C zI#$FT_`cOhE-@!^9A-zopxf?%ODL8BoSZpQOvxHswQi2Z?`yK%YnaVer(LRiZpRAn zvcG&^u|LTANY;Ax4_xTK*SvaeS>*2mlT<;m9kvWx+fM13KSgxjJ-$TMnN&gECGrAI zL~9)k)!t~1&_j-tP-vyZ&lyytzrQf^VA$nXLEMq;=w1v{&}+yE_5L0}yW| z1?PXSpEP`of5PXObK>(}6&i(xKH5hzPpztnPEpJxqDt4Mxf0k8p5+B#t+S#rNnM_2 zPgRaI&g79>Z&loJb7)R$WQLHL|B11l?_8hpzqL&xpNQYw{17f`gpXAfWHvVw97SFY3F$q{^SVq8p4z z+0OEk3yewkmZ9Z?y;gy4+Zk55(>xf&>Vh^&Dw8Z4udWj7+4PbRlxuqVwPZ(c6nDGn zYtdwkhDnC!I%7Ume!Iq|Q)i<_7@!fc5}eB%+}XE=4)x$_VZ$V7QK5q%(=CH4IC2r6 z2nwHU`xSH}4gyi*xW>VkhQX|+A?JvIzjEQSrzoWVXr6Z>)g3>198Zk++SU#)Rxm1UlA>(;dvWv`3Mse+t4aa1$hL0{h-d?q@{?)R|Ws0fCA zvztrVPPyygJl%t>6VtVGrAyDO_Ls?%x4NPX;lc1isB~m$x1V-{kvd``F8QkPej*5A zg7{M43u7X_$4=8`1O|62;kjN&E@?pdWB;u7=MUe1{wnF=*6<%+Ape2hAvIfIt)KgfEhowM+e;xV>DP=fq8;TCgMcmYWAuuUI-ovUwo`g`IUgry8 zPZy(Q!9PK!vP+Gsh|x%g)4!xG0cc$6H#*FiQ{A=hpF*|{QuQg7%$DBavnCQ#jNatq zc!-Et#%$`_c~c5v2re8{yHusw?kthvN0XG4N+B@N&nMyZ)Aqu@!xbDIgp4kVW6j+= zzBvOS-jr+mFsH}Os!Gw%D9!Y%L4QF7C?Lvz@%om0_1g2p12zH25}2b<^BPGM5qo&^ z3yn9Xl*RV+DFD;dK#y18)az1mnATHvja7AD^n?E!oBlL)iL=n*->7)pC|WZFTJ$Zc8b}3k6av5Q-Klv=Agnu>ytQTA;WT zcZX2iU5ZmEF2P+26faJ3cMtB61Ml~}XHMVq{W(9*GqYw-eq3wav$CE^X3yT2iuc~J zW?8M`pM8AkEJ7CCioia`L&0=Pq+7+sgtGazI$J*0KRFC@sC_ zRD=r^zv}1bDP7Rwv`AZZFO{Mj=U56Ub|ICBV}XCT$PlYF2YZX@7pWIBw&TEe9Nh}9$!FTm0r?bBzpf?S(pX7y;G^kfLoHL#Dx28?s}eh-0O+U9LFP~ z=e*z3LKQ#DTAv^4VQ&uGwTcg?)Og_&!BcjM%uLU`PIIbfaZlwyH_IFKe7v>ituGUS zeZ%BhH+0u!uLvhxa-*CN&{5}fUVELSjZ-aZ`isR-pvu4HWuA@0rgYPCuQd(x3Y;B? ze~XedG-d#+rzvC`b3+4pS9D%2lMk?D{bDUn#v<{P!C zR~)P;Wn{KoI1XAA?eD&DAt%vb`S@{vzT01M-cxn)DYge9d>C3KhSyNjY<0^kAAVh~ zq7_#V_YCN7r1F^otQ(0$z5Huyzfz| z#}P!4%;+?d-N4}`#}QhZDD%R%dM|!ye=W5{G|VG#$6<~AM>Szr<#~OTJcms_w3-Hi z%1>!h%8675MLZNg>s5-^W24o)B4b=PEDIV#1c-`FX`58qckP+JX8XoZU9ST z7Zew~tX9IHM&@Q{BXFV(}TlqLWJjSS?Kz^DGv`qw9s5{0+=Jm^i{I~M7uW0!~b8bN4b0_(SMnfgX!fuUc zv4&+YleUZ3le##Q{oZcc%vqq!No&tyC^q zes4qzgoRh^fLTs4va|<7qYMqi1UvZ>XpZaqYU~>4TS6!-Pr}6J_MH1vLB;8j)^%tp zDDCUETl7|D65FtvJPMKIoSgxcqLR9(XLQ#Ah}oN39^Ay~1JDRuL&W$2*u_@bUqTV< z^<0Xbpsz1eRxB|uh~(8*qRPY&AgjnYYlZ%%F}ZHzQOGwcn?|MNM0J1$T0R9)B~sAY zaAH6&xPGx#lF4pI1|x>PGoZ(kZ=x)j{7mc+;v8Qq-9A7}>jv*g=KWDj_gt_%vH8RW z4_avppArH&Mpv2FuXg){1Q~9$uJ^6{dXIR$sWiJWvB+LGQJBi)GQ;1?3CENsofhEh zEv72JDuN(ThtrQeIt{a$9+io(wq3Cnpwz~`C2HZsDDBH~OplojgyaY-Cp8$qE4Moo z!6?99&(A8vdXnn|jG(m{%OS+$L;$wTjWULHXKws08I#93Wh~cRKVO-npihN^L#DG1 zs8jqRJ!-Co1RxMl+tuCAUIOMkxbkIq^`Q1NoHW-Vz(2MBCqNCwj;n&f(ARxY*S$3( ztlmw=#fh1ARdtb7n&sTpL)`#Z5h@=W?pkh*c)~_mm4z$0%bBR^YzIo*;c{C-!z69$ z$ZvAI3C5g_+=?Oeb_=@!9@cW>y`uOP-c^gUC@)*oPSb0t53%m>r}GnD$Za@Mr&STN z7$$J+{k3{!0hKsW%4}ZYE1izNqA!yr%t_#n|0qlgR>f3#y(QWm+cSUqo^Zu;Ex2~? zCD!?tv-BIdbEFvd9#VQvq;v9Or-p7!D}qNoItC=-FR4*K*I2L{&@;Mni$u<3hBUEF zug?Zx2WHnRSL+$-)Cbq?JP>lnhyq_$2ycdMv55NP`Rq9SPI<{wQmxfGsHKes4PrY? z)yjJTay9V)eD(3+l!<#k$X4z+ydb+wz$NoIN}atE(@=Ipd)|{^!Qtc_LxBurE%IrLxeW%tbl@aLy$uJkij}Ped-%0x5S&sLUO5H&HsU5bX0?PV1^)J5~bJbaW?~4LsjW zDqW`!720xRv}rmH`C6u0v_Cs-)8ija2qG*Ugfv#K1LboV+#r{Ieag{(kLe%e9IV&r$xGKL)&CR}3W0!uekRRk0R}}Zqu$05O!aM%%DMv9%OO9O?K4;)gl!;Tw*lr9UGkVw_<7pbyt=#t$HCv53#y-suo|iX8LhW|{AJ z_jbxy@me!rf}WNwzGc8arZQLCCh|ck=+m_LoG#IO#^~%Az3f4WM8MGun?Gw(^qN7E zbFLJJ+{pwacr_94Sgu8|qCiA5TOpg3EMI~4BoEER9>7-!3}0Rx*yceK-q@$Sx#fgL z_{$sk>z;_R6IiI+s-AvDc^S{7280bLxen}sl?=aJUv~06?6XicWExaYn?os2n9-b* z0(()z7*HEBgwK3$$5%Mpx)g>X{9I#!11Z@+<`6j%W{D{$D^mzC3s7N6k?O_Ow!Ma7 zee1!iKUQTC{@~lKiu+>Dwe+u!=2VF(UE;>Ma?s2cY%Zp3@Fu+BKB5(T&}&>GCGj+!BAQKC0T;k*njM@ z*rYRa7|CKWGme{+!UB*fRoy>TQG~`X%-a&ZJyYzdw{FhvWScj67Od2)I@6U2S{X4j ziMnA8GM{cRNt|lo`=)A7_eD#flPmRH9FyfXT6X9o`xiidpDWGQ#|S*>=SuqtqCr-E zn@NWb^a4oU$vkH9$K9?*zgaSub7zMk;Z(j)mv*q>Kgp#UR1j|Q&uN8J8*89U;1@nW z(n;4PQ|R#)$fP0qZS8%}gIsML0`a&9C~#A5_;8$?*_wm9Gepjz%Mz7ZBm0enCF$bw zJ^}Mu`?RlhaTo@!g|*x%@enSBqv9S%6U7RbdW%92TPp+6t^K)Uh6a9$#Xws$h77_j zFW7gqQm<$PAQPqz>d{%-$F)1`(qb{4DbO zP<6y-Rv^Zwr~eqo-#`H@kU`0%sMnZq^#V)x3cqNX;k-85Po3(B| zl1PoXy!K(cv@D5%&t^jCBQvx0SSu=A6PrbR_911~me6tPH#|*>W=YSfaxvwDvo#E# zRh?c-E;CbmJ*}nM!9@aT2=x?UBkZ5>>fRLujZ7H7JsKP0oVgfgiSf_-Mi>fspJC6R$9V?9ak zHLJt?dyrpVm!yrP642|L0Zt0T{X&+7Vy@PA$omCn(yL$|ca~Xp)B8WR5NgEp8Am_*FqzP<-aw66~UYUkt z6RvNu>y81_gm1xPYXVtVPF&D+5f1<*LB>l50nClgZ#6fl4Al7FzOp&+GlqG4)(fG? z9E$sZEO%Po5(8)KVQ$I2ba% z!z7o+590F9-Fld!rf}zI^JO)xEHKSljFrZV6>GB(_DaR zZ59JjA6eJ-lU(IQj-{aQy9x)aEcxC`5WkJS^E#NrsoKH|DWC~gIBsRz)9}^QD{0+) zj!kz726FrSd?G-M(f;bsN=7v~snN&X2KmGTg6wzy`qczH#b~HclpNP1**wm-Nz-(& zEMVJj71(g+vPz1Gcv+NJeAJKG`*jxW@SyPnlY64Hd}cjZ`76`M#}_vymA;>cSvY*_ zv+a7?e#sHQN#f^5c%QI&&#EH=4rEivW8tSh!SNyXP_-8wpuRkU4`4Lh+HF#jRn}la}OYNCCl+e@HFZKN%N`RUBi8RNqBzf9-vpmNLY`*xttNJhf~` zuesz`xM4CuNFpR}1f^&!A(FY8H2v+Ix}eKYa3ozdDYO&WYPl`w{iEG11Zx-049*wp z%$mzU#UNu+w|gi4vIGy-cqN=#ANoV-+t$ftHM7+wSNiP=3N8lY@0%jG3*H!~jY|8g z7d>gH<|(BH3j=%b*KHIs+Qo}&H6P7iubH2Wof}ED9_9@oQVY z%kS&G_9nA1P%nDl#WqeP<%QpDZx&|pn-0OVGM9W0>&JBDy=fZ7K;MDsc=pH(9o3nD zrKf7Pau>3514LwnIJ+6ZrK3q$Th+H>wD~Iek2$Tg~wJO-j@gu0jk4 zdjT;qR@0Hr=Qgm#*{zsHned0vb>wR~=Rjnyc1qnD0+`&-^&C`_jl*EzfyVp(vgEAO z5B_|oIRNb5udxR9H1-;Pf|32V#c><0ohxjg}v(L0<>#9n*{89mMdFdV4E zZqIL{7H|*uBtsLx&~VDIcb*jwwy-=7QK<*rLN#nwU7t|*k`_U6hCl~D$}AC*Jl8=M z;jsqxo;yS)oHpTpA83M&kU&FGT-2J z?WSOXF`w~a%k<98rPa5>>s6R#;(los`-v(Ta}5pV_j$9Th3%?x6m#IATM0>wA+3Vo z3>NPNv(>Y@1fxuH#kwDrSo_V=QsqpTf+mW+`(dTfVu}O|H~Yni_SKbfla4 z%tP>pmZbLDd(y2Ve>as_q}R{`{|vQh^hO_i&5?|GR_pkwtYjhYWrIpg6t@AQ>7@g0 zuNmK3vfvfW`CG5k1+&%x{U>47k3*+_u?v-6R>q47af+l3;ane;pcY~XSJEc{ODY`= z&E&T{f7vBM`@}iTyv^g0&~LgeN44rEm$MsjguKt*4-VG$l(5+rW3C~hQq5;2JEf&n zX7OBkfLw6p>89i`B)iPdL7}`s_hmSnp^$KMve%ZuxWiMGZfuoPyCw0R)cu)C=9-E& z7CLYdcJpWWacVpJ;(iW!j50Qc&v!43*Wn!LMZ*_W#tJC;sJ(_XGMo#rKfRW+ji;`E zdo7>bxj0pze~T?CU;RsH`L~R7w~Gy<^&NreP-)*Qk27p3Kme7OCQo}u>jyu5p&;#6 zsS@fx}R>CoUPKdo}{&2r>K*64OJZS^iH&tVG zk{Aq}k_t73oOe&(zB%tGYOJVUhy(IK>Q+0u=ftD-LU>m?&FA6vX^-&!Lm`&|f5t}9 z3*`PIw$MNK`u`SN%`y7_EB3=j%qV|q!S7|?_u_B-fA8%5#q%!@@&A^D%S!&q-wU1Z zh0XW!=6}1x|C+_~q2JxCf9sw9N!=Te?+wTIw&OpkV3fb8zkJC5r0x~M#&=Zu-9Wt? zf0yS&&&Ln`B^3TAavr7@%)9Y-c|I(-L;i=0 z_n+Xs`}V%0?!iCTGJJ9e-XDm6BKPXqd%NttY4%Tq1nt3tzeKeEMDDx#ewF)nxSRIB z%k$x(+Jk@h_&=HZrn*0W|C`DAc*p$5X!j@jKQo`y@0h>Nx2z=UUlB0xHVS(8brWjb G$N4W|#U{f5 literal 26751 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 SD+0#jLMc4HZWH~dIR6D~OLpo2 literal 26564 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%0D#}VdOc(OVu{<4F3u!bwR$=b3v1pwEk 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 50` +(`STalign.py:1233`), so a shorter run leaves that branch entirely untested. Three findings were real bugs and are **fixed** (D2, D3, R1). One is a deliberate approximation, now measured and budgeted (D1). The rest are places where squidpy is diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py index 4dfc5cc47..190054b70 100644 --- a/tests/experimental/methods/test_stalign_reference.py +++ b/tests/experimental/methods/test_stalign_reference.py @@ -89,17 +89,21 @@ def clouds(): @pytest.fixture(scope="module") def source_grid(primitives): - """Upstream's rasterised reference image and its axes, as ``((y, x), image)``.""" - return (jnp.asarray(primitives["raster_ref_y"]), jnp.asarray(primitives["raster_ref_x"])), jnp.asarray( - primitives["raster_ref"] + """The *moving* raster and its axes, as ``((y, x), image)``. + + The query moves onto the reference, so the query raster is the source -- the same + role LDDMM's ``I``/``xI`` and ``pointsI`` play. + """ + return (jnp.asarray(primitives["raster_query_y"]), jnp.asarray(primitives["raster_query_x"])), jnp.asarray( + primitives["raster_query"] ) @pytest.fixture(scope="module") def target_grid(primitives): - """Upstream's rasterised query image and its axes.""" - return (jnp.asarray(primitives["raster_query_y"]), jnp.asarray(primitives["raster_query_x"])), jnp.asarray( - primitives["raster_query"] + """The *fixed* raster and its axes: the reference.""" + return (jnp.asarray(primitives["raster_ref_y"]), jnp.asarray(primitives["raster_ref_x"])), jnp.asarray( + primitives["raster_ref"] ) @@ -566,6 +570,119 @@ def test_affine_from_points_survives_ill_conditioning(primitives, record_propert ) +# -------------------------------------------------------------------------------------- +# The whole iteration loop +# -------------------------------------------------------------------------------------- + + +def _run_lddmm(primitives, snapshot, niter): + source_axes = (jnp.asarray(primitives["raster_query_y"]), jnp.asarray(primitives["raster_query_x"])) + target_axes = (jnp.asarray(primitives["raster_ref_y"]), jnp.asarray(primitives["raster_ref_x"])) + return _core.lddmm( + source_axes, + jnp.asarray(primitives["raster_query"]), + target_axes, + jnp.asarray(primitives["raster_ref"]), + L=jnp.asarray(snapshot["L"]), + T=jnp.asarray(snapshot["T"]), + points_source=jnp.asarray(primitives["landmarks_query"])[:, ::-1], + points_target=jnp.asarray(primitives["landmarks_ref"])[:, ::-1], + niter=niter, + **F.LDDMM_PARAMS, + ) + + +#: Relative-error budget after ``n`` gradient steps. Single steps agree to ~1e-14; the +#: allowance grows because bilinear-resampling VJPs and FFTs accumulate in different +#: orders on the two backends, and 50 steps of gradient descent amplify that. +_TRAJECTORY_BUDGET = {1: 1e-10, 5: 1e-9, 50: 1e-6} + + +@pytest.mark.parametrize("niter", [1, 5, 50]) +def test_trajectory_matches_upstream(primitives, niter, record_property): + """Run the real loop for ``n`` steps and compare every state it carries. + + ``niter=50`` matters specifically: the mixture-weight E-step is gated on + ``it >= 50`` (STalign.py:1233), so below it the weights are frozen at their + initial 0.5/0.4/0.1 and that whole branch goes untested. + + Note the affine offset. Upstream builds ``A`` at the *top* of each iteration and + returns it, so ``LDDMM(n)["A"]`` reflects ``n-1`` updates; squidpy builds it after + the loop. The fixture stores the un-lagged affine as ``A``. See ledger row D4. + """ + snapshot = _load(f"trajectory_n{niter}") + result = _run_lddmm(primitives, snapshot, niter) + + budget = _TRAJECTORY_BUDGET[niter] + for name, got, expected in ( + ("A", result["A"], snapshot["A"]), + ("v", result["v"], snapshot["v"]), + ("WM", result["WM"], snapshot["WM"]), + ("WA", result["WA"], snapshot["WA"]), + ("WB", result["WB"], snapshot["WB"]), + ): + error = rel(got, expected) + record_property(f"rel_error_{name}", error) + assert error < budget, f"n={niter} {name}: {error:.3e} exceeds {budget:.0e}" + + +def test_velocity_grid_is_the_one_the_reference_used(primitives, source_grid): + """The trajectory comparison is only meaningful on a shared velocity grid. + + Upstream was driven onto this grid explicitly via its ``xv=``/``v=`` parameters, so + if squidpy's construction ever diverges again the trajectory numbers would be + comparing two different problems rather than two implementations. + """ + axes, _ = source_grid + built = _core._build_velocity_grid(axes, a=F.LDDMM_PARAMS["a"], expand=F.LDDMM_PARAMS["expand"]) + snapshot = _load("trajectory_n1") + np.testing.assert_allclose(np.asarray(built[0]), snapshot["xv_0"], rtol=0, atol=1e-9) + np.testing.assert_allclose(np.asarray(built[1]), snapshot["xv_1"], rtol=0, atol=1e-9) + + +def test_converged_solution_matches_upstream(primitives, record_property): + """500 iterations -- "enough to actually converge", per #1243. + + Elementwise equality is the wrong instrument this far in: 500 steps of descent + amplify last-ulp backend differences without either answer being wrong. What must + hold is that both converge to the same registration. + """ + snapshot = _load("converged_n500") + result = _run_lddmm(primitives, snapshot, 500) + + energy = rel(result["E"], snapshot["E_last"]) + record_property("rel_error_E", energy) + assert energy < 0.01, f"final energy differs by {energy:.3%}" + + # Convergence is a statement about the registration, not the objective: the affine + # initialised from landmarks is already near-optimal, so `E` barely moves over the + # run even though the fit is good. Target registration error is the honest measure. + clouds = F.make_clouds() + landmarks_ref = clouds.landmarks_ref_rc + before = float(np.mean(np.linalg.norm(clouds.landmarks_query_rc - landmarks_ref, axis=1))) + after = float(snapshot["tre_mean"]) + record_property("tre_before", before) + record_property("tre_after", after) + assert after < 0.4 * F.RASTER_PARAMS["dx"], f"reference did not converge: TRE {after:.2f}" + assert after < before / 5.0, f"reference barely moved: TRE {before:.2f} -> {after:.2f}" + + aligned = np.asarray( + _core.transform_points_row_col( + result["xv"], + result["v"], + result["A"], + jnp.asarray(primitives["query"])[:, ::-1], + direction="forward", + ) + ) + displacement = np.linalg.norm(aligned - np.asarray(snapshot["aligned_points_rc"]), axis=1) + percentile = float(np.percentile(displacement, 95)) + record_property("p95_displacement", percentile) + assert percentile < 0.1 * F.RASTER_PARAMS["dx"], ( + f"95th-percentile point disagreement {percentile:.4f} exceeds a tenth of a grid cell" + ) + + # -------------------------------------------------------------------------------------- # The ledger has to stay in sync # -------------------------------------------------------------------------------------- diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index d1a48ed52..08fdb1af4 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -1,7 +1,7 @@ """Integration tests for the public ``align`` (STalign) function. Tiny synthetic fixtures with ``niter=1`` keep these fast; they verify wiring, -write-back modes, and the key guard -- not solver quality. +path resolution, and write-back -- not solver quality. """ from __future__ import annotations @@ -17,6 +17,7 @@ # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. _TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} +_TINY_IMAGE = {"a": 4.0, "nt": 1, "niter": 1, "epV": 1.0} def _adata(*, key: str = "spatial") -> AnnData: @@ -26,52 +27,44 @@ def _adata(*, key: str = "spatial") -> AnnData: return adata -def test_object_mode_returns_result_and_touches_nothing() -> None: - ref, query = _adata(), _adata() - result = align(ref, query, method="stalign", output_mode="object", **_TINY) - assert isinstance(result, StalignResult) - assert result.aligned_points.shape == query.obsm["spatial"].shape - assert "aligned_spatial" not in query.obsm - +def _sdata_tables(**tables: AnnData): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import TableModel -def test_inplace_writes_explicit_key() -> None: - ref, query = _adata(), _adata() - out = align(ref, query, output_mode="inplace", key_added="spatial_aligned", **_TINY) - assert out is None - assert query.obsm["spatial_aligned"].shape == query.obsm["spatial"].shape + return sd.SpatialData(tables={name: TableModel.parse(adata) for name, adata in tables.items()}) -def test_inplace_default_key() -> None: - ref, query = _adata(), _adata() - align(ref, query, output_mode="inplace", **_TINY) - assert "aligned_spatial" in query.obsm +def _sdata_images(): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import Image2DModel + + ref = np.zeros((1, 16, 16), dtype=float) + ref[0, 5:11, 5:11] = 1.0 + query = np.zeros((1, 16, 16), dtype=float) + query[0, 6:12, 4:10] = 1.0 + return sd.SpatialData( + images={ + "ref": Image2DModel.parse(ref, dims=("c", "y", "x")), + "query": Image2DModel.parse(query, dims=("c", "y", "x")), + } + ) -def test_copy_leaves_original_untouched() -> None: - ref, query = _adata(), _adata() - out = align(ref, query, output_mode="copy", key_added="aligned", **_TINY) - assert isinstance(out, AnnData) and out is not query - assert "aligned" in out.obsm - assert "aligned" not in query.obsm +# --- out=None: fit and return, touch nothing ------------------------------------------ -def test_existing_default_key_requires_explicit_key_added() -> None: +def test_out_none_returns_result_and_writes_nothing() -> None: ref, query = _adata(), _adata() - query.obsm["aligned_spatial"] = np.zeros_like(query.obsm["spatial"]) - with pytest.raises(ValueError, match="aligned_spatial.*already exists"): - align(ref, query, output_mode="inplace", **_TINY) + result = align(ref, query, in_="obsm/spatial", method="stalign", **_TINY) + assert isinstance(result, StalignResult) + assert result.aligned_points.shape == query.obsm["spatial"].shape + assert list(query.obsm) == ["spatial"] -def test_image_not_implemented() -> None: - ref, query = _adata(), _adata() - with pytest.raises(NotImplementedError, match="on='image'"): - align(ref, query, on="image", **_TINY) - +def test_result_satisfies_align_result_protocol() -> None: + from squidpy.experimental.tl import AlignResult -def test_missing_spatial_key() -> None: - ref, query = _adata(), _adata() - with pytest.raises(KeyError, match="missing.*not found"): - align(ref, query, spatial_key="missing", **_TINY) + assert isinstance(align(_adata(), _adata(), in_="obsm/spatial", **_TINY), AlignResult) def test_public_surface_is_align_result_only() -> None: @@ -83,37 +76,181 @@ def test_public_surface_is_align_result_only() -> None: assert not hasattr(tl, "AffineFitResult") -def test_object_result_satisfies_align_result_protocol() -> None: - from squidpy.experimental.tl import AlignResult +# --- writing --------------------------------------------------------------------------- - result = align(_adata(), _adata(), method="stalign", output_mode="object", **_TINY) - assert isinstance(result, AlignResult) +def test_out_writes_in_place() -> None: + ref, query = _adata(), _adata() + assert align(ref, query, in_="obsm/spatial", out="obsm/aligned", **_TINY) is None + assert query.obsm["aligned"].shape == query.obsm["spatial"].shape -def _sdata_tables(**tables: AnnData): - sd = pytest.importorskip("spatialdata") - from spatialdata.models import TableModel - return sd.SpatialData(tables={name: TableModel.parse(adata) for name, adata in tables.items()}) +def test_copy_leaves_original_untouched() -> None: + ref, query = _adata(), _adata() + out = align(ref, query, in_="obsm/spatial", out="obsm/aligned", copy=True, **_TINY) + assert isinstance(out, AnnData) and out is not query + assert "aligned" in out.obsm + assert "aligned" not in query.obsm + + +def test_out_may_overwrite_the_input_path() -> None: + """``out`` equal to ``in_`` is allowed -- destructive, but explicitly asked for.""" + ref, query = _adata(), _adata() + original = query.obsm["spatial"].copy() + align(ref, query, in_="obsm/spatial", out="obsm/spatial", **_TINY) + assert list(query.obsm) == ["spatial"] + assert not np.array_equal(query.obsm["spatial"], original) + + +# --- SpatialData tables ---------------------------------------------------------------- -def test_sdata_object_mode() -> None: +def test_sdata_pair_of_tables() -> None: sdata = _sdata_tables(ref=_adata(), query=_adata()) - result = align(sdata, method="stalign", ref_key="ref", query_key="query", output_mode="object", **_TINY) + result = align( + sdata, + in_=("tables/ref/obsm/spatial", "tables/query/obsm/spatial"), + **_TINY, + ) assert isinstance(result, StalignResult) - assert "aligned_spatial" not in sdata.tables["query"].obsm + assert "aligned" not in sdata.tables["query"].obsm + + +def test_sdata_writes_into_the_named_table_only() -> None: + sdata = _sdata_tables(ref=_adata(), query=_adata()) + align( + sdata, + in_=("tables/ref/obsm/spatial", "tables/query/obsm/spatial"), + out="tables/query/obsm/aligned", + **_TINY, + ) + assert "aligned" in sdata.tables["query"].obsm + assert "aligned" not in sdata.tables["ref"].obsm def test_sdata_copy_leaves_original_untouched() -> None: sd = pytest.importorskip("spatialdata") sdata = _sdata_tables(ref=_adata(), query=_adata()) - out = align(sdata, ref_key="ref", query_key="query", output_mode="copy", key_added="aligned", **_TINY) + out = align( + sdata, + in_=("tables/ref/obsm/spatial", "tables/query/obsm/spatial"), + out="tables/query/obsm/aligned", + copy=True, + **_TINY, + ) assert isinstance(out, sd.SpatialData) and out is not sdata assert "aligned" in out.tables["query"].obsm assert "aligned" not in sdata.tables["query"].obsm +def test_two_sdata_objects_share_one_path() -> None: + ref_sdata = _sdata_tables(slice=_adata()) + query_sdata = _sdata_tables(slice=_adata()) + align(ref_sdata, query_sdata, in_="tables/slice/obsm/spatial", out="tables/slice/obsm/aligned", **_TINY) + assert "aligned" in query_sdata.tables["slice"].obsm + assert "aligned" not in ref_sdata.tables["slice"].obsm + + +# --- images ---------------------------------------------------------------------------- + + +def test_images_fit_returns_result() -> None: + sdata = _sdata_images() + result = align(sdata, in_=("images/ref", "images/query"), **_TINY_IMAGE) + assert isinstance(result, StalignResult) + assert sorted(sdata.images) == ["query", "ref"] + + +def test_images_out_materialises_a_warped_image() -> None: + """A diffeomorphism has no SpatialData transformation to be registered as.""" + sdata = _sdata_images() + expected = np.asarray(sdata.images["query"].data).shape + + align(sdata, in_=("images/ref", "images/query"), out="images/query_aligned", **_TINY_IMAGE) + + assert "query_aligned" in sdata.images + assert np.asarray(sdata.images["query_aligned"].data).shape == expected + + +def test_image_alignment_recovers_a_known_shift() -> None: + """The query square sits one row down and one column left of the reference.""" + sdata = _sdata_images() + ref = np.asarray(sdata.images["ref"].data) + query = np.asarray(sdata.images["query"].data) + + align(sdata, in_=("images/ref", "images/query"), out="images/query_aligned", a=4.0, nt=2) + aligned = np.asarray(sdata.images["query_aligned"].data) + + before = float(np.sum((query - ref) ** 2)) + after = float(np.sum((aligned - ref) ** 2)) + assert after < before / 2.0, f"overlap barely improved: {before:.1f} -> {after:.1f}" + + +def test_warp_image_rejects_a_point_cloud_fit() -> None: + result = align(_adata(), _adata(), in_="obsm/spatial", **_TINY) + with pytest.raises(ValueError, match="fitted on point clouds"): + result.warp_image(np.zeros((1, 4, 4))) + + +# --- path errors ----------------------------------------------------------------------- + + +@pytest.mark.parametrize( + ("path", "match"), + [ + ("nope/x", "does not start with a known collection"), + ("obsm", "Expected `obsm/`"), + ("tables/t/spatial", "Expected `tables/

/obsm/`"), + ("images/a/b", "Expected `images/`"), + ("labels/x", "does not read or write yet"), + ("", "is empty"), + ], +) +def test_invalid_paths_explain_the_grammar(path: str, match: str) -> None: + with pytest.raises(ValueError, match=match): + align(_adata(), _adata(), in_=path, **_TINY) + + +def test_missing_obsm_key_lists_what_is_available() -> None: + with pytest.raises(KeyError, match="no `obsm\\['missing'\\]`.*spatial"): + align(_adata(), _adata(), in_="obsm/missing", **_TINY) + + +def test_missing_table_lists_what_is_available() -> None: + sdata = _sdata_tables(ref=_adata(), query=_adata()) + with pytest.raises(KeyError, match="no table 'nope'.*query.*ref"): + align(sdata, in_="tables/nope/obsm/spatial", **_TINY) + + +def test_table_path_on_anndata_is_rejected() -> None: + with pytest.raises(ValueError, match="but the container is an AnnData"): + align(_adata(), _adata(), in_="tables/t/obsm/spatial", **_TINY) + + +def test_bare_obsm_path_on_sdata_is_ambiguous() -> None: + sdata = _sdata_tables(ref=_adata(), query=_adata()) + with pytest.raises(ValueError, match="ambiguous for a SpatialData"): + align(sdata, in_="obsm/spatial", **_TINY) + + +def test_mixed_modalities_in_in_are_rejected() -> None: + sdata = _sdata_images() + with pytest.raises(ValueError, match="mixes modalities"): + align(sdata, in_=("images/ref", "tables/t/obsm/spatial"), **_TINY) + + +def test_out_modality_must_match_in() -> None: + sdata = _sdata_images() + with pytest.raises(ValueError, match="does not convert between the two"): + align(sdata, in_=("images/ref", "images/query"), out="tables/t/obsm/x", **_TINY_IMAGE) + + +def test_query_required_for_anndata() -> None: + with pytest.raises(ValueError, match="`data_query` is required"): + align(_adata(), in_="obsm/spatial", **_TINY) + + def test_align_with_landmarks() -> None: ref, query = _adata(), _adata() landmarks = ref.obsm["spatial"][:3] @@ -121,8 +258,8 @@ def test_align_with_landmarks() -> None: result = align( ref, query, + in_="obsm/spatial", method="stalign", - output_mode="object", landmarks_source=landmarks, landmarks_target=landmarks, **_TINY, diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py index c7d7d657d..248fa6fe5 100644 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ b/tests/experimental/tl/test_align_by_landmarks.py @@ -21,7 +21,7 @@ def _adata(coords: np.ndarray = _QUERY, *, key: str = "spatial") -> AnnData: @pytest.mark.parametrize("method", ["similarity", "affine"]) def test_object_mode_returns_affine_result(method: str) -> None: - result = align_by_landmarks(_REF, _QUERY, method=method, output_mode="object") + result = align_by_landmarks(_REF, _QUERY, method=method) assert isinstance(result, AffineFitResult) assert result.matrix.shape == (3, 3) # affine maps query -> ref @@ -35,7 +35,7 @@ def test_object_is_default() -> None: def test_anndata_inplace_writes_default_key() -> None: adata = _adata() - out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace") + out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, out="obsm/aligned_spatial") assert out is None assert "aligned_spatial" in adata.obsm np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) @@ -45,58 +45,62 @@ def test_anndata_inplace_writes_default_key() -> None: def test_anndata_copy_leaves_original_untouched() -> None: adata = _adata() - out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="copy", key_added="xy_aligned") + out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, copy=True, out="obsm/xy_aligned") assert isinstance(out, AnnData) and out is not adata assert "xy_aligned" in out.obsm assert "xy_aligned" not in adata.obsm -def test_custom_spatial_key() -> None: +def test_in_selects_which_coordinates_are_transformed() -> None: adata = _adata(key="loc") - align_by_landmarks(_REF, _QUERY, method="affine", data=adata, spatial_key="loc", output_mode="inplace") - assert "aligned_loc" in adata.obsm + align_by_landmarks(_REF, _QUERY, method="affine", data=adata, in_="obsm/loc", out="obsm/aligned") + np.testing.assert_allclose(adata.obsm["aligned"], _REF, atol=1e-6) + np.testing.assert_allclose(adata.obsm["loc"], _QUERY) -def test_existing_default_key_requires_explicit_key_added() -> None: +def test_out_overwrites_without_ceremony() -> None: + """There is no auto-derived destination any more, so there is nothing to guard. + + The old API derived ``f"aligned_{spatial_key}"`` when ``key_added`` was omitted and + had to refuse to overwrite it, since the user had not chosen it. ``out`` is always + named explicitly, so writing where the caller pointed is the whole contract. + """ adata = _adata() adata.obsm["aligned_spatial"] = np.zeros_like(_QUERY) - with pytest.raises(ValueError, match="aligned_spatial.*already exists"): - align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace") - # explicit key_added overwrites without error - align_by_landmarks(_REF, _QUERY, method="affine", data=adata, output_mode="inplace", key_added="aligned_spatial") + align_by_landmarks(_REF, _QUERY, method="affine", data=adata, out="obsm/aligned_spatial") np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) def test_write_mode_requires_data() -> None: with pytest.raises(ValueError, match="`data` is required"): - align_by_landmarks(_REF, _QUERY, method="affine", output_mode="inplace") + align_by_landmarks(_REF, _QUERY, method="affine", out="obsm/aligned_spatial") def test_too_few_landmarks() -> None: with pytest.raises(ValueError, match="at least 3 landmark pairs"): - align_by_landmarks(_REF[:2], _QUERY[:2], method="affine", output_mode="object") + align_by_landmarks(_REF[:2], _QUERY[:2], method="affine") def test_length_mismatch() -> None: with pytest.raises(ValueError, match="same shape"): - align_by_landmarks(_REF, _QUERY[:3], method="affine", output_mode="object") + align_by_landmarks(_REF, _QUERY[:3], method="affine") def test_unknown_method_lists_available() -> None: with pytest.raises(ValueError, match=r"Unknown align_landmarks method 'nope'"): - align_by_landmarks(_REF, _QUERY, method="nope", output_mode="object") + align_by_landmarks(_REF, _QUERY, method="nope") def test_non_finite_landmarks_rejected() -> None: bad = _QUERY.copy() bad[0, 0] = np.nan with pytest.raises(ValueError, match="must contain only finite values"): - align_by_landmarks(_REF, bad, method="affine", output_mode="object") + align_by_landmarks(_REF, bad, method="affine") def test_bad_data_type_raises() -> None: with pytest.raises(TypeError, match="must be AnnData or SpatialData"): - align_by_landmarks(_REF, _QUERY, method="affine", data=object(), output_mode="inplace") # type: ignore[arg-type] + align_by_landmarks(_REF, _QUERY, method="affine", data=object(), out="obsm/aligned_spatial") # type: ignore[arg-type] def test_spatialdata_copy_leaves_original_untouched() -> None: @@ -112,9 +116,9 @@ def test_spatialdata_copy_leaves_original_untouched() -> None: _QUERY, method="affine", data=sdata, - cs_name_query="query_cs", - cs_name_ref="ref_cs", - output_mode="copy", + cs_query="query_cs", + cs_ref="ref_cs", + copy=True, ) assert out is not sdata assert "ref_cs" in get_transformation(out.points["pts"], get_all=True) @@ -134,9 +138,9 @@ def test_spatialdata_registers_transformation() -> None: _QUERY, method="affine", data=sdata, - cs_name_query="query_cs", - cs_name_ref="ref_cs", - output_mode="inplace", + cs_query="query_cs", + cs_ref="ref_cs", + out="obsm/aligned_spatial", ) assert out is None assert "ref_cs" in get_transformation(sdata.points["pts"], get_all=True) @@ -161,9 +165,9 @@ def test_spatialdata_composes_with_existing_transform() -> None: _QUERY + offset, method="affine", data=sdata, - cs_name_query="query_cs", - cs_name_ref="ref_cs", - output_mode="inplace", + cs_query="query_cs", + cs_ref="ref_cs", + out="obsm/aligned_spatial", ) matrix = get_transformation(sdata.points["pts"], to_coordinate_system="ref_cs").to_affine_matrix( diff --git a/tests/experimental/tl/test_align_io.py b/tests/experimental/tl/test_align_io.py index c241d95b7..e575aba5b 100644 --- a/tests/experimental/tl/test_align_io.py +++ b/tests/experimental/tl/test_align_io.py @@ -1,40 +1,25 @@ -"""Unit tests for the I/O layer of the public ``align`` functions. +"""Unit tests for the SpatialData transformation write-back. -These exercise input resolution and write-back directly -- no estimator, no JAX -- -so they cover the ``AnnData`` *and* ``SpatialData`` branches and every error guard -in :mod:`squidpy.experimental.tl._align._io` cheaply. +No estimator, no JAX -- these exercise registering a fitted affine on a coordinate +system directly, including the copy semantics, which are easy to get subtly wrong +because :func:`shallow_copy_sdata` shares element objects with the original. + +Array reads and writes at a path live in :mod:`._paths` and are covered by +``test_align_paths.py``. """ from __future__ import annotations -from dataclasses import dataclass - import numpy as np import pytest from anndata import AnnData from squidpy.experimental.methods.align_landmarks import AffineFitResult -from squidpy.experimental.tl._align._io import ( - get_coords, - resolve_obs_pair, - shallow_copy_sdata, - writeback_affine_sdata, - writeback_obs, -) +from squidpy.experimental.tl._align._io import shallow_copy_sdata, writeback_affine_sdata _PTS = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) -@dataclass -class _ShiftResult: - """Minimal :class:`AlignResult`: a constant offset baked into ``transform``.""" - - delta: float = 100.0 - - def transform(self, points: np.ndarray) -> np.ndarray: - return np.asarray(points, dtype=float) + self.delta - - def _adata(coords: np.ndarray = _PTS, *, key: str = "spatial") -> AnnData: adata = AnnData(np.zeros((coords.shape[0], 1))) adata.obsm[key] = coords.copy() @@ -57,185 +42,6 @@ def _sdata_points(cs: str = "qcs"): return sd.SpatialData(points={"pts": pts}) -# --------------------------------------------------------------------------- -# resolve_obs_pair -# --------------------------------------------------------------------------- - - -def test_resolve_anndata_pair() -> None: - ref, query = _adata(), _adata() - r_adata, q_adata, container, element_key = resolve_obs_pair(ref, query, None, None) - assert r_adata is ref and q_adata is query - assert container is None and element_key is None - - -def test_resolve_anndata_requires_query() -> None: - with pytest.raises(ValueError, match="`data_query` is required when `data_ref` is an AnnData"): - resolve_obs_pair(_adata(), None, None, None) - - -def test_resolve_anndata_rejects_mixed_query() -> None: - with pytest.raises(TypeError, match="Mixed AnnData/SpatialData"): - resolve_obs_pair(_adata(), _sdata_points(), None, None) - - -def test_resolve_anndata_rejects_keys() -> None: - with pytest.raises(ValueError, match="only valid for SpatialData"): - resolve_obs_pair(_adata(), _adata(), "ref", None) - - -def test_resolve_bad_ref_type() -> None: - with pytest.raises(TypeError, match="must be AnnData or SpatialData"): - resolve_obs_pair(object(), _adata(), None, None) # type: ignore[arg-type] - - -def test_resolve_sdata_pair() -> None: - pytest.importorskip("spatialdata") - ref_sd = _sdata_tables(ref=_adata()) - query_sd = _sdata_tables(query=_adata(_PTS + 5)) - r_adata, q_adata, container, element_key = resolve_obs_pair(ref_sd, query_sd, "ref", "query") - assert r_adata is ref_sd.tables["ref"] - assert q_adata is query_sd.tables["query"] - assert container is query_sd - assert element_key == "query" - - -def test_resolve_sdata_single_two_tables() -> None: - pytest.importorskip("spatialdata") - both = _sdata_tables(ref=_adata(), query=_adata(_PTS + 5)) - r_adata, q_adata, container, element_key = resolve_obs_pair(both, None, "ref", "query") - assert r_adata is both.tables["ref"] - assert q_adata is both.tables["query"] - assert container is both - assert element_key == "query" - - -def test_resolve_sdata_requires_keys() -> None: - pytest.importorskip("spatialdata") - both = _sdata_tables(ref=_adata(), query=_adata()) - with pytest.raises(ValueError, match="`ref_key` and `query_key` are required"): - resolve_obs_pair(both, None, None, None) - - -def test_resolve_sdata_rejects_mixed_query() -> None: - pytest.importorskip("spatialdata") - with pytest.raises(TypeError, match="Mixed AnnData/SpatialData"): - resolve_obs_pair(_sdata_tables(ref=_adata()), _adata(), "ref", "query") - - -def test_resolve_sdata_missing_key() -> None: - pytest.importorskip("spatialdata") - both = _sdata_tables(ref=_adata()) - with pytest.raises(KeyError, match="nope"): - resolve_obs_pair(both, None, "nope", "ref") - - -# --------------------------------------------------------------------------- -# get_coords -# --------------------------------------------------------------------------- - - -def test_get_coords_missing_key() -> None: - with pytest.raises(KeyError, match="missing.*not found"): - get_coords(_adata(), "missing") - - -def test_get_coords_rejects_non_2d() -> None: - adata = _adata() - adata.obsm["bad"] = np.zeros((_PTS.shape[0], 3)) - with pytest.raises(ValueError, match=r"must be an \(N, 2\) array"): - get_coords(adata, "bad") - - -# --------------------------------------------------------------------------- -# writeback_obs -- AnnData -# --------------------------------------------------------------------------- - - -def test_writeback_obs_object_mode_returns_result() -> None: - result = _ShiftResult() - out = writeback_obs( - result, - output_mode="object", - query_adata=_adata(), - container=None, - element_key=None, - spatial_key="spatial", - key_added=None, - ) - assert out is result - - -def test_writeback_obs_anndata_inplace() -> None: - query = _adata() - out = writeback_obs( - _ShiftResult(), - output_mode="inplace", - query_adata=query, - container=None, - element_key=None, - spatial_key="spatial", - key_added="aligned", - ) - assert out is None - np.testing.assert_allclose(query.obsm["aligned"], _PTS + 100.0) - - -def test_writeback_obs_anndata_copy_leaves_original_untouched() -> None: - query = _adata() - out = writeback_obs( - _ShiftResult(), - output_mode="copy", - query_adata=query, - container=None, - element_key=None, - spatial_key="spatial", - key_added="aligned", - ) - assert isinstance(out, AnnData) and out is not query - assert "aligned" in out.obsm - assert "aligned" not in query.obsm - - -# --------------------------------------------------------------------------- -# writeback_obs -- SpatialData -# --------------------------------------------------------------------------- - - -def test_writeback_obs_sdata_inplace() -> None: - pytest.importorskip("spatialdata") - sdata = _sdata_tables(query=_adata()) - out = writeback_obs( - _ShiftResult(), - output_mode="inplace", - query_adata=sdata.tables["query"], - container=sdata, - element_key="query", - spatial_key="spatial", - key_added="aligned", - ) - assert out is None - np.testing.assert_allclose(sdata.tables["query"].obsm["aligned"], _PTS + 100.0) - - -def test_writeback_obs_sdata_copy_leaves_original_untouched() -> None: - pytest.importorskip("spatialdata") - sdata = _sdata_tables(query=_adata()) - out = writeback_obs( - _ShiftResult(), - output_mode="copy", - query_adata=sdata.tables["query"], - container=sdata, - element_key="query", - spatial_key="spatial", - key_added="aligned", - ) - assert out is not sdata - assert "aligned" in out.tables["query"].obsm - # regression: copy must not leak the new key back into the input container - assert "aligned" not in sdata.tables["query"].obsm - - # --------------------------------------------------------------------------- # writeback_affine_sdata # --------------------------------------------------------------------------- @@ -269,7 +75,7 @@ def test_writeback_affine_copy_leaves_original_untouched() -> None: def test_writeback_affine_requires_cs_names() -> None: pytest.importorskip("spatialdata") - with pytest.raises(ValueError, match="`cs_name_query` and `cs_name_ref` are required"): + with pytest.raises(ValueError, match="`cs_query` and `cs_ref` are required"): writeback_affine_sdata( AffineFitResult(matrix=np.eye(3)), _sdata_points(), output_mode="inplace", moving_cs=None, target_cs="tcs" ) diff --git a/tests/experimental/tl/test_align_paths.py b/tests/experimental/tl/test_align_paths.py new file mode 100644 index 000000000..44678da36 --- /dev/null +++ b/tests/experimental/tl/test_align_paths.py @@ -0,0 +1,166 @@ +"""Unit tests for the ``in_`` / ``out`` path layer. + +No estimator, no JAX -- these exercise parsing, reading and writing directly, so the +AnnData *and* SpatialData branches and every error guard are covered cheaply. +""" + +from __future__ import annotations + +import numpy as np +import pytest +from anndata import AnnData + +from squidpy.experimental.tl._align._paths import parse_path, read_path, write_path + +_PTS = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) + + +def _adata(coords: np.ndarray = _PTS, *, key: str = "spatial") -> AnnData: + adata = AnnData(np.zeros((coords.shape[0], 1))) + adata.obsm[key] = coords.copy() + return adata + + +def _sdata_tables(**tables: AnnData): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import TableModel + + return sd.SpatialData(tables={name: TableModel.parse(adata) for name, adata in tables.items()}) + + +def _sdata_image(name: str = "he", shape: tuple[int, int, int] = (2, 8, 6)): + sd = pytest.importorskip("spatialdata") + from spatialdata.models import Image2DModel + + data = np.arange(int(np.prod(shape)), dtype=float).reshape(shape) + return sd.SpatialData(images={name: Image2DModel.parse(data, dims=("c", "y", "x"))}) + + +# --- parsing --------------------------------------------------------------------------- + + +@pytest.mark.parametrize( + ("path", "modality", "element", "key"), + [ + ("obsm/spatial", "points", None, "spatial"), + ("/obsm/spatial/", "points", None, "spatial"), + ("tables/s1/obsm/xy", "points", "s1", "xy"), + ("images/he", "image", "he", None), + ], +) +def test_parse_accepts_the_documented_forms(path, modality, element, key) -> None: + parsed = parse_path(path, name="in_") + assert (parsed.modality, parsed.element, parsed.key) == (modality, element, key) + assert parsed.raw == path + + +@pytest.mark.parametrize( + ("path", "match"), + [ + ("", "is empty"), + ("///", "is empty"), + ("obsm", "Expected `obsm/`"), + ("obsm/a/b", "Expected `obsm/`"), + ("tables/s1/obs/x", "Expected `tables/
/obsm/`"), + ("tables/s1", "Expected `tables/
/obsm/`"), + ("images", "Expected `images/`"), + ("shapes/x", "does not read or write yet"), + ("varm/x", "does not start with a known collection"), + ], +) +def test_parse_rejects_malformed_paths(path, match) -> None: + with pytest.raises(ValueError, match=match): + parse_path(path, name="in_") + + +def test_parse_rejects_non_strings() -> None: + with pytest.raises(TypeError, match="must be a string path"): + parse_path(("obsm/spatial",), name="in_") # type: ignore[arg-type] + + +# --- reading --------------------------------------------------------------------------- + + +def test_read_anndata_obsm() -> None: + got = read_path(_adata(), parse_path("obsm/spatial", name="in_"), name="in_") + np.testing.assert_array_equal(got, _PTS) + + +def test_read_sdata_table_obsm() -> None: + sdata = _sdata_tables(s1=_adata(), s2=_adata(_PTS + 5)) + got = read_path(sdata, parse_path("tables/s2/obsm/spatial", name="in_"), name="in_") + np.testing.assert_array_equal(got, _PTS + 5) + + +def test_read_image_is_channels_first() -> None: + got = read_path(_sdata_image(), parse_path("images/he", name="in_"), name="in_") + assert got.shape == (2, 8, 6) + + +def test_read_rejects_non_2d_coordinates() -> None: + adata = _adata() + adata.obsm["bad"] = np.zeros((5, 3)) + with pytest.raises(ValueError, match=r"must be an \(N, 2\) array"): + read_path(adata, parse_path("obsm/bad", name="in_"), name="in_") + + +def test_missing_obsm_key_lists_alternatives() -> None: + with pytest.raises(KeyError, match="no `obsm\\['nope'\\]`.*spatial"): + read_path(_adata(), parse_path("obsm/nope", name="in_"), name="in_") + + +def test_missing_table_lists_alternatives() -> None: + sdata = _sdata_tables(s1=_adata(), s2=_adata()) + with pytest.raises(KeyError, match="no table 'nope'.*s1.*s2"): + read_path(sdata, parse_path("tables/nope/obsm/spatial", name="in_"), name="in_") + + +def test_missing_image_lists_alternatives() -> None: + with pytest.raises(KeyError, match="no image 'nope'.*he"): + read_path(_sdata_image(), parse_path("images/nope", name="in_"), name="in_") + + +def test_table_path_against_anndata_is_rejected() -> None: + with pytest.raises(ValueError, match="but the container is an AnnData"): + read_path(_adata(), parse_path("tables/s1/obsm/spatial", name="in_"), name="in_") + + +def test_bare_obsm_path_against_sdata_is_ambiguous() -> None: + sdata = _sdata_tables(s1=_adata(), s2=_adata()) + with pytest.raises(ValueError, match="ambiguous for a SpatialData"): + read_path(sdata, parse_path("obsm/spatial", name="in_"), name="in_") + + +def test_image_path_against_anndata_is_rejected() -> None: + with pytest.raises(TypeError, match="only a SpatialData holds"): + read_path(_adata(), parse_path("images/he", name="in_"), name="in_") + + +# --- writing --------------------------------------------------------------------------- + + +def test_write_anndata_obsm() -> None: + adata = _adata() + write_path(adata, parse_path("obsm/aligned", name="out"), _PTS + 1) + np.testing.assert_array_equal(adata.obsm["aligned"], _PTS + 1) + np.testing.assert_array_equal(adata.obsm["spatial"], _PTS) + + +def test_write_sdata_table_obsm() -> None: + sdata = _sdata_tables(s1=_adata(), s2=_adata()) + write_path(sdata, parse_path("tables/s2/obsm/aligned", name="out"), _PTS + 1) + assert "aligned" in sdata.tables["s2"].obsm + assert "aligned" not in sdata.tables["s1"].obsm + + +def test_write_image_creates_a_new_element() -> None: + sdata = _sdata_image() + write_path(sdata, parse_path("images/warped", name="out"), np.zeros((2, 8, 6))) + assert sorted(sdata.images) == ["he", "warped"] + assert np.asarray(sdata.images["warped"].data).shape == (2, 8, 6) + + +def test_write_promotes_a_2d_image() -> None: + sdata = _sdata_image() + write_path(sdata, parse_path("images/warped", name="out"), np.zeros((8, 6))) + assert np.asarray(sdata.images["warped"].data).shape == (1, 8, 6) From b4e5788b2d127cb3153512566571fa235f877143 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 18:29:30 +0200 Subject: [PATCH 124/131] fuse lddmm loop add early stopping --- docs/release/notes-dev.md | 9 + .../methods/align_samples/_stalign.py | 14 + .../align_samples/_stalign_impl/_core.py | 368 ++++++++++++++---- .../methods/STALIGN_DIVERGENCES.md | 53 ++- tests/experimental/methods/test_stalign.py | 71 ++++ 5 files changed, 421 insertions(+), 94 deletions(-) diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 4273e8685..22aa5da14 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -20,6 +20,15 @@ be expressed as a SpatialData transformation, so writing to an `images/...` path materialises the warped image rather than registering it lazily. Adds an `align_images` method family and `squidpy.experimental.methods.align_samples.fit_stalign_image`. +- The experimental STalign solver now runs its whole gradient descent as a single compiled + `lax.while_loop` instead of a Python loop around a jitted step, about **4.6x faster** per + iteration (2.20 to 0.46 ms on the reference fixture, so `niter=5000` drops from ~11s to ~2.4s). + Numerically unchanged: the reference suite still matches the original implementation at 1, 5, 50 + and 500 iterations. +- The experimental STalign solver returns the per-iteration `energies` trace and `n_iter`, and + accepts optional `tol` / `patience` early stopping. Off by default. Note the objective changes + definition at iteration 50, when the mixture-weight E step engages, so the convergence window + deliberately never spans that point. - **Breaking (experimental):** {func}`squidpy.experimental.tl.align_by_landmarks` takes `in_` / `out` / `copy` in place of `spatial_key` / `key_added` / `output_mode`, and its coordinate-system arguments are renamed `cs_ref` / `cs_query`. Because `out` is always named explicitly, the guard diff --git a/src/squidpy/experimental/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py index d517f630a..fd8bf35dd 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -111,6 +111,8 @@ def fit_stalign( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + tol: float | None = None, + patience: int = 25, ) -> StalignResult: """Fit a deformation mapping ``query`` onto ``ref``. @@ -138,6 +140,9 @@ def fit_stalign( sigmaM, sigmaB, sigmaA, sigmaR, sigmaP Noise scales for the matching, background, artifact, regularisation, and landmark-point terms of the objective. + tol, patience + Stop once the objective's relative improvement over the last ``patience`` + iterations falls below ``tol``. ``tol=None`` (default) always runs ``niter``. Returns ------- @@ -206,6 +211,8 @@ def fit_stalign( sigmaA=sigmaA, sigmaR=sigmaR, sigmaP=sigmaP, + tol=tol, + patience=patience, ) aligned_rc = transform_points_row_col(result["xv"], result["v"], result["A"], source_rc, direction="forward") return StalignResult( @@ -241,6 +248,8 @@ def fit_stalign_image( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + tol: float | None = None, + patience: int = 25, ) -> StalignResult: """Fit a deformation mapping the ``query`` image onto the ``ref`` image. @@ -268,6 +277,9 @@ def fit_stalign_image( sigmaM, sigmaB, sigmaA, sigmaR, sigmaP Noise scales for the matching, background, artifact, regularisation, and landmark-point terms of the objective. + tol, patience + Stop once the objective's relative improvement over the last ``patience`` + iterations falls below ``tol``. ``tol=None`` (default) always runs ``niter``. Returns ------- @@ -331,6 +343,8 @@ def axes(image: JaxArray, scale: tuple[float, float]) -> tuple[JaxArray, JaxArra sigmaA=sigmaA, sigmaR=sigmaR, sigmaP=sigmaP, + tol=tol, + patience=patience, ) return StalignResult( affine=result["A"], diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py index 96194ef84..0b7ded685 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py @@ -2,6 +2,7 @@ from __future__ import annotations +from functools import partial from typing import Any, Literal import jax @@ -11,6 +12,11 @@ __all__ = ["jax_dtype", "lddmm", "transform_points_row_col"] +#: Iteration at which the mixture-weight E step switches on (STalign.py:1233). Before +#: this the weights are frozen at their initial values, so the objective changes +#: definition here and its value jumps discontinuously. +MIXTURE_E_STEP_START = 50 + def jax_dtype() -> jnp.dtype: """Resolve the active JAX float dtype at call time, not import time.""" @@ -201,33 +207,36 @@ def _update_mixture_weights( jnp.sum(background_weights), 1e-12 ) - if iteration < 50: - return match_weights, artifact_weights, background_weights, muA, muB - - weights = jnp.stack((match_weights, artifact_weights, background_weights)) - mixing = jnp.sum(weights, axis=(1, 2)) - mixing = mixing + jnp.max(mixing) * 1e-6 - mixing = mixing / jnp.sum(mixing) - - n_channels = target_image.shape[0] - norm_match = (2.0 * np.pi * sigmaM**2) ** (n_channels / 2.0) - norm_artifact = (2.0 * np.pi * sigmaA**2) ** (n_channels / 2.0) - norm_background = (2.0 * np.pi * sigmaB**2) ** (n_channels / 2.0) - - match_weights = mixing[0] * jnp.exp(-jnp.sum((transformed_source - target_image) ** 2, axis=0) / (2.0 * sigmaM**2)) - match_weights = match_weights / norm_match - artifact_weights = mixing[1] * jnp.exp( - -jnp.sum((muA[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaA**2) - ) - artifact_weights = artifact_weights / norm_artifact - background_weights = mixing[2] * jnp.exp( - -jnp.sum((muB[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaB**2) + def _e_step() -> tuple[jax.Array, jax.Array, jax.Array]: + weights = jnp.stack((match_weights, artifact_weights, background_weights)) + mixing = jnp.sum(weights, axis=(1, 2)) + mixing = mixing + jnp.max(mixing) * 1e-6 + mixing = mixing / jnp.sum(mixing) + + n_channels = target_image.shape[0] + norm_match = (2.0 * np.pi * sigmaM**2) ** (n_channels / 2.0) + norm_artifact = (2.0 * np.pi * sigmaA**2) ** (n_channels / 2.0) + norm_background = (2.0 * np.pi * sigmaB**2) ** (n_channels / 2.0) + + match = mixing[0] * jnp.exp(-jnp.sum((transformed_source - target_image) ** 2, axis=0) / (2.0 * sigmaM**2)) + match = match / norm_match + artifact = mixing[1] * jnp.exp(-jnp.sum((muA[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaA**2)) + artifact = artifact / norm_artifact + background = mixing[2] * jnp.exp(-jnp.sum((muB[:, None, None] - target_image) ** 2, axis=0) / (2.0 * sigmaB**2)) + background = background / norm_background + + total = match + artifact + background + total = total + jnp.max(total) * 1e-6 + return match / total, artifact / total, background / total + + # Before the E step switches on the weights stay at their initial 0.5/0.4/0.1, + # while muA/muB are still re-estimated every 5th iteration. + match_weights, artifact_weights, background_weights = jax.lax.cond( + iteration >= MIXTURE_E_STEP_START, + _e_step, + lambda: (match_weights, artifact_weights, background_weights), ) - background_weights = background_weights / norm_background - - total = match_weights + artifact_weights + background_weights - total = total + jnp.max(total) * 1e-6 - return match_weights / total, artifact_weights / total, background_weights / total, muA, muB + return match_weights, artifact_weights, background_weights, muA, muB def _lddmm_loss( @@ -275,6 +284,183 @@ def _lddmm_loss( return total, (contrast_source, transformed_points, match_energy, reg_energy, point_energy) +@partial( + jax.jit, + static_argnames=( + "niter", + "diffeo_start", + "epL", + "epT", + "epV", + "sigmaM", + "sigmaA", + "sigmaB", + "sigmaR", + "sigmaP", + "tol", + "patience", + "estimate_muA", + "estimate_muB", + ), +) +def _lddmm_run( + linear, + translation, + velocity, + match_weights, + artifact_weights, + background_weights, + muA, + muB, + *, + x_source, + source_image, + x_target, + target_image, + xv, + kernel, + ll, + dv_prod, + source_landmarks, + target_landmarks, + niter, + diffeo_start, + epL, + epT, + epV, + sigmaM, + sigmaA, + sigmaB, + sigmaR, + sigmaP, + tol, + patience, + estimate_muA, + estimate_muB, +): + """The gradient descent, as one compiled loop. + + Jitted as a whole rather than per-iteration: `lax.while_loop` outside a `jit` re-traces + its body on every call, and tracing `value_and_grad` through the interpolation and FFTs + costs about as much as a thousand iterations of actually running it. + """ + loss_and_grad = jax.value_and_grad(_lddmm_loss, argnums=(0, 1, 2), has_aux=True) + + # Precomputed in Python so the two step sizes are bit-identical to `epL / 1.0` and + # `epL / 10.0` -- the `(it >= diffeo_start) * 9` scaling at STalign.py:1205-1206. + steps_before = (epL, epT) + steps_after = (epL / 10.0, epT / 10.0) + + dtype = jax_dtype() + # `niter=0` means "evaluate the initial state and stop"; the trace still needs a + # slot so the carry has a fixed shape. + energies = jnp.full((max(niter, 1),), jnp.nan, dtype=dtype) + initial = ( + jnp.asarray(0), + linear, + translation, + velocity, + match_weights, + artifact_weights, + background_weights, + muA, + muB, + jnp.asarray(jnp.nan, dtype=dtype), + source_landmarks, + energies, + ) + + def _step(carry: tuple[Any, ...]) -> tuple[Any, ...]: + iteration, linear, translation, velocity, wm, wa, wb, muA, muB, _, _, energies = carry + + (energy, aux), (grad_linear, grad_translation, grad_velocity) = loss_and_grad( + linear, + translation, + velocity, + x_source=x_source, + source_image=source_image, + x_target=x_target, + target_image=target_image, + xv=xv, + match_weights=wm, + ll=ll, + dv_prod=dv_prod, + points_source=source_landmarks, + points_target=target_landmarks, + sigmaM=sigmaM, + sigmaR=sigmaR, + sigmaP=sigmaP, + ) + contrast_source, transformed_points, _, _, _ = aux + + diffeo = iteration >= diffeo_start + step_linear = jnp.where(diffeo, steps_after[0], steps_before[0]) + step_translation = jnp.where(diffeo, steps_after[1], steps_before[1]) + linear = linear - step_linear * grad_linear + translation = translation - step_translation * grad_translation + + grad_velocity = jnp.fft.ifftn( + jnp.fft.fftn(grad_velocity, axes=(1, 2)) * kernel[None, ..., None], + axes=(1, 2), + ).real + velocity = jnp.where(diffeo, velocity - epV * grad_velocity, velocity) + + wm, wa, wb, muA, muB = jax.lax.cond( + iteration % 5 == 0, + lambda: _update_mixture_weights( + contrast_source, + target_image, + wm, + wa, + wb, + sigmaM=sigmaM, + sigmaA=sigmaA, + sigmaB=sigmaB, + estimate_muA=estimate_muA, + estimate_muB=estimate_muB, + muA=muA, + muB=muB, + iteration=iteration, + ), + lambda: (wm, wa, wb, muA, muB), + ) + return ( + iteration + 1, + linear, + translation, + velocity, + wm, + wa, + wb, + muA, + muB, + energy, + transformed_points, + energies.at[iteration].set(energy), + ) + + def _keep_going(carry: tuple[Any, ...]) -> jax.Array: + iteration, energies = carry[0], carry[-1] + if tol is None: + return iteration < niter + # Compare against `patience` iterations ago rather than the previous step: the + # weights only move every 5th iteration, so consecutive energies plateau and + # then jump, and a one-step test would stop on the plateau. + recent = energies[jnp.maximum(iteration - 1, 0)] + older = energies[jnp.maximum(iteration - 1 - patience, 0)] + improving = (older - recent) > tol * jnp.abs(older) + # The whole comparison window has to sit after the E step switches on. The + # objective changes definition at `MIXTURE_E_STEP_START` and its value jumps + # upward there, which reads as "no longer improving" to any stopping rule; the + # first energy computed with the new weights is at `MIXTURE_E_STEP_START + 1`, so + # the oldest index we may look at is that, hence the `+ 2` once the window and the + # one-step lag are accounted for. + warming_up = iteration < MIXTURE_E_STEP_START + 2 + patience + return (iteration < niter) & (warming_up | improving) + + return jax.lax.while_loop(_keep_going, _step, initial) + + def lddmm( xI: tuple[np.ndarray | jax.Array, np.ndarray | jax.Array], I: np.ndarray | jax.Array, @@ -299,7 +485,30 @@ def lddmm( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + tol: float | None = None, + patience: int = 25, ) -> dict[str, Any]: + """Fit an LDDMM registration of ``I`` onto ``J`` by gradient descent. + + The whole descent runs as a single ``lax.while_loop``, so XLA compiles the loop body + once and fuses across it instead of paying per-iteration dispatch from Python. + + Parameters + ---------- + tol + Stop once the objective's relative improvement over the last ``patience`` + iterations falls below this. ``None`` (default) always runs the full ``niter``. + patience + Window for the ``tol`` test. Compared against ``patience`` iterations ago rather + than the previous step because the mixture weights only move every 5th iteration, + so the objective plateaus and then jumps -- a one-step test would stop on a + plateau. + + Returns + ------- + A dict with the fitted ``A``/``v``/``xv``, the mixture weights, the final energy + ``E``, the per-iteration ``energies`` trace, and ``n_iter`` actually run. + """ x_source = (jnp.asarray(xI[0]), jnp.asarray(xI[1])) x_target = (jnp.asarray(xJ[0]), jnp.asarray(xJ[1])) source_image = jnp.asarray(I, dtype=jax_dtype()) @@ -326,61 +535,54 @@ def lddmm( estimate_muA = True estimate_muB = True - loss_and_grad = jax.jit(jax.value_and_grad(_lddmm_loss, argnums=(0, 1, 2), has_aux=True)) - - # Bound up front so `niter=0` -- evaluate the initial state and stop -- returns a - # well-formed result instead of reading unbound locals off the end of the loop. - energy = jnp.asarray(jnp.nan, dtype=jax_dtype()) - transformed_points = source_landmarks - - for iteration in range(niter): - (energy, aux), (grad_linear, grad_translation, grad_velocity) = loss_and_grad( - linear, - translation, - velocity, - x_source=x_source, - source_image=source_image, - x_target=x_target, - target_image=target_image, - xv=xv, - match_weights=match_weights, - ll=ll, - dv_prod=dv_prod, - points_source=source_landmarks, - points_target=target_landmarks, - sigmaM=sigmaM, - sigmaR=sigmaR, - sigmaP=sigmaP, - ) - contrast_source, transformed_points, _, _, _ = aux - - affine_scale = 1.0 + 9.0 * float(iteration >= diffeo_start) - linear = linear - (epL / affine_scale) * grad_linear - translation = translation - (epT / affine_scale) * grad_translation - - grad_velocity = jnp.fft.ifftn( - jnp.fft.fftn(grad_velocity, axes=(1, 2)) * kernel[None, ..., None], - axes=(1, 2), - ).real - if iteration >= diffeo_start: - velocity = velocity - epV * grad_velocity - - if iteration % 5 == 0: - match_weights, artifact_weights, background_weights, muA, muB = _update_mixture_weights( - contrast_source, - target_image, - match_weights, - artifact_weights, - background_weights, - sigmaM=sigmaM, - sigmaA=sigmaA, - sigmaB=sigmaB, - estimate_muA=estimate_muA, - estimate_muB=estimate_muB, - muA=muA, - muB=muB, - iteration=iteration, - ) + final = _lddmm_run( + linear, + translation, + velocity, + match_weights, + artifact_weights, + background_weights, + muA, + muB, + x_source=x_source, + source_image=source_image, + x_target=x_target, + target_image=target_image, + xv=xv, + kernel=kernel, + ll=ll, + dv_prod=dv_prod, + source_landmarks=source_landmarks, + target_landmarks=target_landmarks, + niter=niter, + diffeo_start=diffeo_start, + epL=epL, + epT=epT, + epV=epV, + sigmaM=sigmaM, + sigmaA=sigmaA, + sigmaB=sigmaB, + sigmaR=sigmaR, + sigmaP=sigmaP, + tol=tol, + patience=patience, + estimate_muA=estimate_muA, + estimate_muB=estimate_muB, + ) + ( + completed, + linear, + translation, + velocity, + match_weights, + artifact_weights, + background_weights, + muA, + muB, + energy, + transformed_points, + energies, + ) = final affine = _to_affine(linear, translation) return { @@ -392,4 +594,8 @@ def lddmm( "WA": artifact_weights, "E": energy, "points": transformed_points, + # Per-iteration objective, so a caller can tell a converged run from a diverged + # one without running it again. Trailing entries stay NaN if `tol` stopped early. + "energies": energies[:niter], + "n_iter": completed, } diff --git a/tests/experimental/methods/STALIGN_DIVERGENCES.md b/tests/experimental/methods/STALIGN_DIVERGENCES.md index 444d56629..126dbd4f2 100644 --- a/tests/experimental/methods/STALIGN_DIVERGENCES.md +++ b/tests/experimental/methods/STALIGN_DIVERGENCES.md @@ -116,16 +116,43 @@ skimage's choice is the safer one, so this is not a request to revert — it is *say so* in the docstring. If exact least-squares is wanted, `np.linalg.lstsq` on the padded design matrix gives upstream's answer without upstream's conditioning problem. -### R5. `niter=5000` runs blind — open - -No energy trace in the return value and no convergence criterion; `lddmm` returns only -the final scalar `E`. Users cannot distinguish a converged run from a diverged one -without re-running. Returning the per-iteration energies (upstream accumulates them in a -local `Esave` and throws them away too) would cost nothing and make `niter` tunable. - -### R6. The Python loop forgoes most of JAX's advantage — open - -`lddmm` calls a jitted `value_and_grad` from a Python `for` loop, paying dispatch per -iteration and blocking XLA from fusing across steps. The mixture-weight update every 5th -iteration is the only real obstacle and is expressible with `lax.cond`. Worth measuring -before committing to, but at 5000 iterations the dispatch overhead is not negligible. +### R5. `niter=5000` ran blind — fixed + +`lddmm` now returns the per-iteration `energies` trace and the `n_iter` actually run, so +a converged run is distinguishable from a diverged one without running it again. +(Upstream accumulates the same values in a local `Esave` and throws them away.) + +It also takes an optional `tol` / `patience` early stop: it halts once the objective's +relative improvement over the last `patience` iterations falls below `tol`. Off by +default, so the shipped behaviour is unchanged. + +**The window must clear iteration 50.** The mixture-weight E step switches on at +`MIXTURE_E_STEP_START` (`STalign.py:1233`), which changes what the objective *is* — and +its value jumps **upward** there, on the reference fixture from `3.259` to `5.489`. Any +rule comparing across that boundary reads the jump as "no longer improving" and quits +immediately: with a naive guard every tolerance from `1e-3` to `1e-5` stopped at +iteration 76 regardless. The comparison window now has to sit entirely after the jump. +`test_early_stopping_never_fires_before_the_weights_switch_on` pins this. + +### R6. The Python loop forgave most of JAX's advantage — fixed + +The descent was a Python `for` loop around a jitted `value_and_grad`, paying dispatch +per iteration and blocking XLA from fusing across steps. It is now a single +`lax.while_loop` inside one `jax.jit`, with `lax.cond` for the every-5th-iteration +weight update and `jnp.where` for the `diffeo_start` gates. + +Measured on the reference fixture, warm: + +| | ms/iteration | `niter=5000` | +| --- | --- | --- | +| Python loop (was) | 2.20 | ~11.0 s | +| `lax.while_loop`, not jitted | 0.91 | ~4.6 s | +| **`lax.while_loop` inside `jit`** | **0.46** | **~2.4 s** | + +The middle row is worth keeping in mind: `lax.while_loop` outside a `jit` re-traces its +body on *every call*, and tracing `value_and_grad` through the interpolation and FFTs +costs roughly a second — about as much as a thousand iterations of running it. The loop +has to be inside the `jit` to get the win. + +Reference parity is unchanged at 1 / 5 / 50 / 500 iterations, which is what makes this +rewrite safe to make at all. diff --git a/tests/experimental/methods/test_stalign.py b/tests/experimental/methods/test_stalign.py index 2aca5fb9b..aaa091dc4 100644 --- a/tests/experimental/methods/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -156,6 +156,77 @@ def _blobs(seed: int = 0) -> np.ndarray: ) +def _solve_grids(): + from squidpy.experimental.methods.align_samples._stalign_impl._helpers import rasterize_cloud + + ref = _blobs() + query = ref @ np.array([[np.cos(0.12), -np.sin(0.12)], [np.sin(0.12), np.cos(0.12)]]).T + np.array([6.0, -4.0]) + source = rasterize_cloud(query[:, ::-1], dx=_SOLVE["dx"], blur=_SOLVE["blur"], expand=1.1) + target = rasterize_cloud(ref[:, ::-1], dx=_SOLVE["dx"], blur=_SOLVE["blur"], expand=1.1) + solve = {k: v for k, v in _SOLVE.items() if k not in {"dx", "blur"}} + return source, target, {"L": np.eye(2), "T": np.zeros(2), **solve} + + +def test_lddmm_returns_an_energy_trace() -> None: + """Without this a caller cannot tell a converged run from a diverged one.""" + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + + source, target, common = _solve_grids() + result = lddmm(*source, *target, niter=30, **common) + + assert result["energies"].shape == (30,) + assert int(result["n_iter"]) == 30 + assert np.all(np.isfinite(np.asarray(result["energies"]))) + # The reported final energy is the last one in the trace. + np.testing.assert_allclose(float(result["E"]), float(result["energies"][-1]), rtol=1e-12) + + +def test_early_stopping_is_off_by_default() -> None: + """``tol=None`` must run every iteration, so the default is bit-for-bit unchanged.""" + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + + source, target, common = _solve_grids() + plain = lddmm(*source, *target, niter=40, **common) + explicit = lddmm(*source, *target, niter=40, tol=None, **common) + + assert int(plain["n_iter"]) == 40 + np.testing.assert_array_equal(np.asarray(plain["v"]), np.asarray(explicit["v"])) + + +def test_early_stopping_never_fires_before_the_weights_switch_on() -> None: + """The objective changes definition at iteration 50 and its value jumps upward. + + A stopping rule that compares across that boundary sees the jump as "no longer + improving" and quits immediately, which is what a naive implementation does. + """ + from squidpy.experimental.methods.align_samples._stalign_impl._core import MIXTURE_E_STEP_START, lddmm + + source, target, common = _solve_grids() + # A tolerance so loose it would stop at the first opportunity. + result = lddmm(*source, *target, niter=400, tol=1e9, patience=25, **common) + + assert int(result["n_iter"]) >= MIXTURE_E_STEP_START + 2 + 25 + trace = np.asarray(result["energies"]) + assert trace[MIXTURE_E_STEP_START + 1] > trace[MIXTURE_E_STEP_START], ( + "expected the objective to jump when the mixture-weight E step engages" + ) + + +def test_early_stopping_shortens_the_run() -> None: + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + + source, target, common = _solve_grids() + # Loose enough to fire on this fixture but not at the first opportunity: it stops + # around iteration 157 of 400, whereas tol=1e-2 keeps improving through all 400. + stopped = lddmm(*source, *target, niter=400, tol=1e-1, patience=25, **common) + + ran = int(stopped["n_iter"]) + assert 77 < ran < 400 + # Iterations that never ran stay NaN rather than reporting a bogus energy. + trace = np.asarray(stopped["energies"]) + assert np.all(np.isfinite(trace[:ran])) and np.all(np.isnan(trace[ran:])) + + def test_lddmm_energy_decreases() -> None: """More iterations must buy a lower objective. Catches a broken gradient or step.""" from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm From d9fe88a4ff8e94d54d6e01ea8e8c7079cabbc997 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 19:55:24 +0200 Subject: [PATCH 125/131] unify align registry and modalities --- docs/api.md | 3 +- docs/release/notes-dev.md | 20 +- src/squidpy/experimental/methods/__init__.py | 35 ++- .../methods/align_landmarks/__init__.py | 2 +- .../methods/align_landmarks/_landmark.py | 6 +- .../methods/align_samples/__init__.py | 10 +- .../methods/align_samples/_stalign.py | 14 +- .../experimental/methods/registry/__init__.py | 240 ++++++++------- src/squidpy/experimental/tl/__init__.py | 5 +- .../experimental/tl/_align/__init__.py | 4 +- src/squidpy/experimental/tl/_align/_api.py | 289 ++++++++++-------- src/squidpy/experimental/tl/_align/_paths.py | 76 ++++- .../methods/STALIGN_DIVERGENCES.md | 2 +- tests/experimental/methods/test_core.py | 68 ++++- tests/experimental/methods/test_stalign.py | 35 ++- .../methods/test_stalign_reference.py | 4 +- tests/experimental/tl/test_align.py | 38 ++- .../tl/test_align_by_landmarks.py | 177 ----------- tests/experimental/tl/test_align_landmarks.py | 236 ++++++++++++++ tests/experimental/tl/test_align_paths.py | 20 +- 20 files changed, 784 insertions(+), 500 deletions(-) delete mode 100644 tests/experimental/tl/test_align_by_landmarks.py create mode 100644 tests/experimental/tl/test_align_landmarks.py diff --git a/docs/api.md b/docs/api.md index 93b5e1ee5..f6c84d2b8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -141,7 +141,7 @@ See the {doc}`extensibility guide ` for how to implement a custo ``` The ``method=`` argument of {func}`~squidpy.experimental.tl.align` and -{func}`~squidpy.experimental.tl.align_by_landmarks` dispatches to a registered +{func}`~squidpy.experimental.tl.align` dispatches to a registered fitting function. The method-specific arguments are documented on each function below; the fitted maps are returned (with ``output_mode="object"``) as the result types listed alongside. @@ -170,7 +170,6 @@ result types listed alongside. experimental.tl.calculate_tiling_qc experimental.tl.TilingQCParams experimental.tl.align - experimental.tl.align_by_landmarks experimental.tl.AlignResult experimental.tl.assign_stitch_groups experimental.tl.StitchParams diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 22aa5da14..2b76e7635 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -6,16 +6,24 @@ - Add a numerical reference suite for the experimental STalign port, comparing it against the original PyTorch implementation. Deselected by default; runs on the scheduled job. Reference values are generated out of band by - [scverse/squidpy-ports](https://github.com/scverse/squidpy-ports), so `torch` is not a squidpy + [theislab/squidpy-ports](https://github.com/theislab/squidpy-ports), so `torch` is not a squidpy dependency. [#1243](https://github.com/scverse/squidpy/issues/1243) - **Breaking (experimental):** {func}`squidpy.experimental.tl.align` now locates data with `in_` and `out` paths instead of a stack of key arguments. `ref_key`, `query_key`, `spatial_key`, - `key_added`, `output_mode` and `on` are replaced by `in_` (e.g. `"obsm/spatial"`, - `"tables/slice1/obsm/spatial"`, `"images/he"`), `out`, and `copy`. `on` is gone because the path - already says which modality is meant. `out=None` (the default) returns the fitted alignment and - writes nothing. This follows the shape proposed for scanpy in - [scanpy#4007](https://github.com/scverse/scanpy/issues/4007). + `key_added` and `output_mode` are replaced by `in_` (e.g. `"obsm/spatial"`, + `"tables/slice1/obsm/spatial"`, `"images/he"`, `"shapes/landmarks"`), `out`, and `copy`. + `out=None` (the default) returns the fitted alignment and writes nothing. This follows the shape + proposed for scanpy in [scanpy#4007](https://github.com/scverse/scanpy/issues/4007). +- **Breaking (experimental):** `squidpy.experimental.tl.align_by_landmarks` is folded into + {func}`squidpy.experimental.tl.align` as `by="landmarks"`. `on` becomes `by`, gaining a + `"landmarks"` value alongside `"obs"` and `"images"`; `in_` then names the correspondences and + `apply_to` names what moves. Writing to `out="cs/"` registers the fitted affine on a whole + SpatialData coordinate system instead of materialising anything. +- **Breaking (experimental):** the `align_samples`, `align_images` and `align_landmarks` registries + collapse into a single `ALIGN` registry of `AlignMethod` records, each declaring which modalities + it implements. Asking for one a method does not support now fails immediately and says what it + does support. `fit_stalign` is renamed `fit_stalign_obs` for symmetry with `fit_stalign_image`. - {func}`squidpy.experimental.tl.align` can now align on images. The fitted diffeomorphism cannot be expressed as a SpatialData transformation, so writing to an `images/...` path materialises the warped image rather than registering it lazily. Adds an `align_images` method family and diff --git a/src/squidpy/experimental/methods/__init__.py b/src/squidpy/experimental/methods/__init__.py index 2da430e5e..c72dccb7e 100644 --- a/src/squidpy/experimental/methods/__init__.py +++ b/src/squidpy/experimental/methods/__init__.py @@ -1,36 +1,35 @@ """In-memory model-fitting core for experimental methods. -The :mod:`.registry` subpackage holds the registry machinery and the family -registries; each family subpackage (e.g. :mod:`.align_samples`, -:mod:`.align_landmarks`) holds the estimator implementations. Importing this -package imports those subpackages so the estimators register themselves into the -(public) family registries. Each subpackage stays cheap to import -- heavy or -optional dependencies (e.g. JAX) are pulled in lazily, only when an estimator -actually runs. +The :mod:`.registry` subpackage holds the registry machinery and the :data:`ALIGN` +registry; each family subpackage (e.g. :mod:`.align_samples`, :mod:`.align_landmarks`) +holds the estimator implementations. Importing this package imports those subpackages so +the estimators register themselves. Each subpackage stays cheap to import -- heavy or +optional dependencies (e.g. JAX) are pulled in lazily, only when an estimator actually +runs. """ from __future__ import annotations -# Import for side effects: populates ALIGN_SAMPLES / ALIGN_IMAGES / ALIGN_LANDMARKS. +# Import for side effects: populates ALIGN. from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 from squidpy.experimental.methods.registry import ( - ALIGN_IMAGES, - ALIGN_LANDMARKS, - ALIGN_SAMPLES, + ALIGN, AlignImagesFn, AlignLandmarksFn, + AlignMethod, + AlignObsFn, AlignResult, - AlignSamplesFn, + Modality, Registry, ) __all__ = [ - "Registry", - "AlignResult", - "AlignSamplesFn", + "ALIGN", "AlignImagesFn", "AlignLandmarksFn", - "ALIGN_SAMPLES", - "ALIGN_IMAGES", - "ALIGN_LANDMARKS", + "AlignMethod", + "AlignObsFn", + "AlignResult", + "Modality", + "Registry", ] diff --git a/src/squidpy/experimental/methods/align_landmarks/__init__.py b/src/squidpy/experimental/methods/align_landmarks/__init__.py index 6aee5d9a4..0a905e026 100644 --- a/src/squidpy/experimental/methods/align_landmarks/__init__.py +++ b/src/squidpy/experimental/methods/align_landmarks/__init__.py @@ -1,7 +1,7 @@ """``align_landmarks`` family: closed-form alignment from paired landmarks. Importing this package registers the family's estimators into -:data:`~squidpy.experimental.methods.registry.ALIGN_LANDMARKS`. Only the +:data:`~squidpy.experimental.methods.registry.ALIGN`. Only the implementations are re-exported here; the registry itself lives in (and is public from) :mod:`squidpy.experimental.methods`. """ diff --git a/src/squidpy/experimental/methods/align_landmarks/_landmark.py b/src/squidpy/experimental/methods/align_landmarks/_landmark.py index e53369273..1f0a5dcf1 100644 --- a/src/squidpy/experimental/methods/align_landmarks/_landmark.py +++ b/src/squidpy/experimental/methods/align_landmarks/_landmark.py @@ -10,7 +10,7 @@ import numpy.typing as npt from squidpy._utils import NDArrayA -from squidpy.experimental.methods.registry import ALIGN_LANDMARKS +from squidpy.experimental.methods.registry import ALIGN @dataclass @@ -59,7 +59,7 @@ def _fit_landmark_relation( ) -@ALIGN_LANDMARKS.register("similarity") +@ALIGN.register("similarity", "landmarks") def fit_similarity( ref: np.ndarray, query: np.ndarray, @@ -87,7 +87,7 @@ def fit_similarity( ) -@ALIGN_LANDMARKS.register("affine") +@ALIGN.register("affine", "landmarks") def fit_affine( ref: np.ndarray, query: np.ndarray, diff --git a/src/squidpy/experimental/methods/align_samples/__init__.py b/src/squidpy/experimental/methods/align_samples/__init__.py index d53ff9fdf..280fc8450 100644 --- a/src/squidpy/experimental/methods/align_samples/__init__.py +++ b/src/squidpy/experimental/methods/align_samples/__init__.py @@ -1,7 +1,7 @@ """``align_samples`` family: align two samples' point clouds (STalign). Importing this package registers the family's estimators into -:data:`~squidpy.experimental.methods.registry.ALIGN_SAMPLES`. It stays +:data:`~squidpy.experimental.methods.registry.ALIGN`. It stays cheap -- JAX is pulled in lazily, only when an estimator's ``fit`` runs. Only the implementations are re-exported here; the registry itself lives in (and is public from) :mod:`squidpy.experimental.methods`. @@ -9,6 +9,10 @@ from __future__ import annotations -from squidpy.experimental.methods.align_samples._stalign import StalignResult, fit_stalign, fit_stalign_image +from squidpy.experimental.methods.align_samples._stalign import ( + StalignResult, + fit_stalign_image, + fit_stalign_obs, +) -__all__ = ["fit_stalign", "fit_stalign_image", "StalignResult"] +__all__ = ["fit_stalign_obs", "fit_stalign_image", "StalignResult"] diff --git a/src/squidpy/experimental/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py index fd8bf35dd..625462987 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -1,6 +1,6 @@ """STalign estimator: JAX LDDMM point-cloud registration. -Holds both the estimator adapter :func:`fit_stalign` and its result type +Holds both the estimator adapter :func:`fit_stalign_obs` and its result type :class:`StalignResult`; the pure numerics live under :mod:`._stalign_impl`. """ @@ -12,7 +12,7 @@ import numpy.typing as npt -from squidpy.experimental.methods.registry import ALIGN_IMAGES, ALIGN_SAMPLES +from squidpy.experimental.methods.registry import ALIGN if TYPE_CHECKING: import jax @@ -85,8 +85,8 @@ def transform( return transformed_rc[:, ::-1] -@ALIGN_SAMPLES.register("stalign", requires=("jax",)) -def fit_stalign( +@ALIGN.register("stalign", "obs", requires=("jax",)) +def fit_stalign_obs( ref: npt.ArrayLike, query: npt.ArrayLike, *, @@ -226,7 +226,7 @@ def fit_stalign( ) -@ALIGN_IMAGES.register("stalign", requires=("jax",)) +@ALIGN.register("stalign", "images", requires=("jax",)) def fit_stalign_image( ref: npt.ArrayLike, query: npt.ArrayLike, @@ -263,9 +263,9 @@ def fit_stalign_image( element's scale when the two images have different resolutions, otherwise the fit is done in mismatched coordinates. a, p, expand, nt, niter, diffeo_start - LDDMM controls, as in :func:`fit_stalign`. Note ``a`` is a length in the *same* + LDDMM controls, as in :func:`fit_stalign_obs`. Note ``a`` is a length in the *same* units as ``ref_scale`` -- the default of 20 suits pixel units, where - :func:`fit_stalign`'s 500 would exceed most images. ``diffeo_start`` defaults to + :func:`fit_stalign_obs`'s 500 would exceed most images. ``diffeo_start`` defaults to half of ``niter`` so the affine settles before the deformable part switches on; starting both at once lets the velocity field absorb what is really a translation, and it fits it worse than the affine would have. diff --git a/src/squidpy/experimental/methods/registry/__init__.py b/src/squidpy/experimental/methods/registry/__init__.py index 12192bd79..d67f452ab 100644 --- a/src/squidpy/experimental/methods/registry/__init__.py +++ b/src/squidpy/experimental/methods/registry/__init__.py @@ -1,16 +1,22 @@ -"""The registry machinery and the family registries it powers. +"""The registry machinery and the align family it powers. This module holds three things that belong together: -* :class:`Registry` -- a flat ``name -> function`` map, one per method *family*. -* The structural :class:`~typing.Protocol` contracts each family advertises, so - the public API and the registries are typed against a contract rather than a - concrete estimator result (e.g. ``StalignResult``). A new estimator only has to - satisfy :class:`AlignResult` -- a ``transform`` that maps points into the - reference frame -- to plug into :func:`squidpy.experimental.tl.align`. -* The family registry instances (:data:`ALIGN_SAMPLES`, :data:`ALIGN_IMAGES`, - :data:`ALIGN_LANDMARKS`) - the estimator implementations register into. +* :class:`Registry` -- a ``name -> AlignMethod`` map for one method *family*. +* :class:`AlignMethod` -- what one named method can align. A method fills the slots it + implements (``obs``, ``images``, ``landmarks``) and leaves the rest ``None``, so + "``stalign`` cannot align landmarks" is data rather than a special case buried in a + dispatch site. +* The structural :class:`~typing.Protocol` contracts each slot advertises, so the public + API is typed against a contract rather than a concrete result (e.g. ``StalignResult``). + A new estimator only has to satisfy :class:`AlignResult` -- a ``transform`` that maps + points into the reference frame -- to plug into :func:`squidpy.experimental.tl.align`. + +The three slots do **not** all take the same kind of input. ``obs`` and ``images`` take +the data being aligned; ``landmarks`` takes pre-paired correspondences that annotate it. +Each public entry point only ever reads its own slot, so no caller sees a mixed +convention, but the distinction is why these are named slots rather than one callable +with a mode flag. """ from __future__ import annotations @@ -18,7 +24,8 @@ import functools import importlib.util from collections.abc import Callable -from typing import TYPE_CHECKING, Any, Generic, Protocol, TypeVar, runtime_checkable +from dataclasses import dataclass, replace +from typing import TYPE_CHECKING, Any, Literal, Protocol, get_args, runtime_checkable import numpy.typing as npt @@ -28,89 +35,28 @@ from squidpy.experimental.methods.align_landmarks._landmark import AffineFitResult __all__ = [ - "Registry", - "AlignResult", - "AlignSamplesFn", + "ALIGN", "AlignImagesFn", "AlignLandmarksFn", - "ALIGN_SAMPLES", - "ALIGN_IMAGES", - "ALIGN_LANDMARKS", + "AlignMethod", + "AlignObsFn", + "AlignResult", + "Modality", + "Registry", ] -#: The calling convention a family's registry advertises (returned by :meth:`Registry.get`). -F = TypeVar("F", bound=Callable[..., Any]) -#: The concrete function being registered. Kept separate from ``F`` so an estimator may -#: declare specific keyword parameters (e.g. ``config=``) without having to structurally -#: match the family's open-ended ``**kwargs`` calling convention. -RegisteredT = TypeVar("RegisteredT", bound=Callable[..., Any]) - - -class Registry(Generic[F]): - """A flat ``name -> function`` registry for one *family* of methods. - - One :class:`Registry` is created per family (e.g. ``align``, ``impute``), - so keys are plain method names -- there is no ``(method, mode)`` compound - key, because the family already pins the rest. - - The type parameter ``F`` is the family's calling convention (a callable - :class:`~typing.Protocol`); :meth:`get` returns it, so dispatch sites are - typed against the family contract rather than ``Callable[..., Any]``. - """ - - def __init__(self, name: str) -> None: - self.name = name - self._registry: dict[str, F] = {} - - def register(self, key: str, *, requires: tuple[str, ...] = ()) -> Callable[[RegisteredT], RegisteredT]: - """Return a decorator registering a method/function under ``key``.""" - - def decorator(func: RegisteredT) -> RegisteredT: - if key in self._registry: - raise ValueError(f"Method {key!r} is already registered in the {self.name!r} registry.") - - if requires: - - @functools.wraps(func) - def wrapped(*args: Any, **kwargs: Any) -> Any: - missing = [pkg for pkg in requires if importlib.util.find_spec(pkg) is None] - if missing: - verb = "is" if len(missing) == 1 else "are" - names = ", ".join(repr(p) for p in missing) - extras = ",".join(missing) - raise ImportError( - f"Method {key!r} requires {names}, which {verb} not installed. " - f'Install with `pip install "squidpy[{extras}]"`.' - ) - return func(*args, **kwargs) - - self._registry[key] = wrapped # type: ignore[assignment] - return wrapped # type: ignore[return-value] - else: - self._registry[key] = func # type: ignore[assignment] - return func - - return decorator - - def get(self, key: str) -> F: - """Return the function registered under ``key``.""" - try: - return self._registry[key] - except KeyError: - raise ValueError(f"Unknown {self.name} method {key!r}. Available: {sorted(self._registry)}.") from None - - def keys(self) -> tuple[str, ...]: - """Return the registered method names.""" - return tuple(self._registry) +#: What an alignment can be driven by. Also the slot names on :class:`AlignMethod`, and +#: the modalities :mod:`squidpy.experimental.tl._align._paths` resolves a path to. +Modality = Literal["obs", "images", "landmarks"] +MODALITIES: tuple[Modality, ...] = get_args(Modality) @runtime_checkable class AlignResult(Protocol): """A fitted alignment that maps ``(N, 2)`` ``(x, y)`` points into the reference frame. - This is the only thing the public ``align*`` functions require of an - estimator's result, so ``output_mode="object"`` is agnostic to the method - that produced it. + This is the only thing the public ``align`` function requires of an estimator's + result, so ``out=None`` is agnostic to the method that produced it. """ def transform(self, points: npt.ArrayLike, /) -> NDArrayA: @@ -118,30 +64,28 @@ def transform(self, points: npt.ArrayLike, /) -> NDArrayA: ... -class AlignSamplesFn(Protocol): - """Calling convention for ``align_samples`` estimators. +class AlignObsFn(Protocol): + """Calling convention for the ``obs`` slot. - Two point clouds in (passed by keyword as ``ref`` / ``query`` so the - direction can never be silently swapped), one :class:`AlignResult` out. - Solver-specific options arrive through ``**kwargs``. + Two point clouds in (by keyword as ``ref`` / ``query``, so the direction can never be + silently swapped), one :class:`AlignResult` out. """ def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... class AlignImagesFn(Protocol): - """Calling convention for ``align_images`` estimators. - - Two channels-first ``(c, y, x)`` images in, one :class:`AlignResult` out. Separate - from :class:`AlignSamplesFn` because the inputs are rasters, not point clouds -- a - single family would have to guess which an ``(N, 2)`` array was meant to be. - """ + """Calling convention for the ``images`` slot: two ``(c, y, x)`` rasters in.""" def __call__(self, ref: npt.ArrayLike, query: npt.ArrayLike, **kwargs: Any) -> AlignResult: ... class AlignLandmarksFn(Protocol): - """Calling convention for ``align_landmarks`` estimators: paired landmarks in, affine out.""" + """Calling convention for the ``landmarks`` slot: paired correspondences in, affine out. + + Unlike the other two slots, ``ref`` / ``query`` here are *not* the data being aligned + -- they are ``(N, 2)`` landmark arrays annotating it, matched by row order. + """ def __call__( self, @@ -153,14 +97,100 @@ def __call__( ) -> AffineFitResult: ... -#: Sample-to-sample alignment estimators -- ref/query point clouds in, transform out. -#: Consumed by ``squidpy.experimental.tl.align`` for ``obsm`` paths. -ALIGN_SAMPLES: Registry[AlignSamplesFn] = Registry("align_samples") +@dataclass(frozen=True, slots=True) +class AlignMethod: + """One named alignment method and the modalities it implements.""" + + name: str + obs: AlignObsFn | None = None + images: AlignImagesFn | None = None + landmarks: AlignLandmarksFn | None = None + + def supports(self) -> tuple[Modality, ...]: + """Modalities this method implements.""" + return tuple(m for m in MODALITIES if getattr(self, m) is not None) + + def implementation(self, modality: Modality) -> Callable[..., Any]: + """Return the estimator for ``modality``, or explain what this method does instead.""" + fn = getattr(self, modality) + if fn is None: + supported = ", ".join(self.supports()) + raise ValueError(f"Method {self.name!r} does not support {modality} alignment. It supports: {supported}.") + return fn + + +class Registry: + """A ``name -> AlignMethod`` registry for one *family* of methods. + + Registration is per slot, so a method that gains a modality later does not have to be + declared in one place:: + + @ALIGN.register("stalign", "obs", requires=("jax",)) + def fit_stalign_obs(...): ... + + @ALIGN.register("stalign", "images", requires=("jax",)) + def fit_stalign_image(...): ... + """ + + def __init__(self, name: str) -> None: + self.name = name + self._registry: dict[str, AlignMethod] = {} + + def register( + self, + key: str, + modality: Modality, + *, + requires: tuple[str, ...] = (), + ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + """Return a decorator registering a function as ``key``'s ``modality`` estimator.""" + if modality not in MODALITIES: + raise ValueError(f"Unknown modality {modality!r}. Expected one of {', '.join(MODALITIES)}.") + + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + existing = self._registry.get(key, AlignMethod(name=key)) + if getattr(existing, modality) is not None: + raise ValueError(f"Method {key!r} already has a {modality} estimator in the {self.name!r} registry.") + + registered = func + if requires: + + @functools.wraps(func) + def wrapped(*args: Any, **kwargs: Any) -> Any: + missing = [pkg for pkg in requires if importlib.util.find_spec(pkg) is None] + if missing: + verb = "is" if len(missing) == 1 else "are" + names = ", ".join(repr(p) for p in missing) + extras = ",".join(missing) + raise ImportError( + f"Method {key!r} requires {names}, which {verb} not installed. " + f'Install with `pip install "squidpy[{extras}]"`.' + ) + return func(*args, **kwargs) + + registered = wrapped + + self._registry[key] = replace(existing, **{modality: registered}) + return registered + + return decorator + + def get(self, key: str) -> AlignMethod: + """Return the method registered under ``key``.""" + try: + return self._registry[key] + except KeyError: + raise ValueError(f"Unknown {self.name} method {key!r}. Available: {sorted(self._registry)}.") from None + + def keys(self) -> tuple[str, ...]: + """Return every registered method name.""" + return tuple(self._registry) + + def supporting(self, modality: Modality) -> tuple[str, ...]: + """Return the method names implementing ``modality``, for docs and error messages.""" + return tuple(sorted(key for key, m in self._registry.items() if getattr(m, modality) is not None)) -#: Image alignment estimators -- ref/query rasters in, transform out. -#: Consumed by ``squidpy.experimental.tl.align`` for ``images`` paths. -ALIGN_IMAGES: Registry[AlignImagesFn] = Registry("align_images") -#: Closed-form landmark alignment estimators -- paired landmarks in, affine out. -#: Consumed by ``squidpy.experimental.tl.align_by_landmarks``. -ALIGN_LANDMARKS: Registry[AlignLandmarksFn] = Registry("align_landmarks") +#: Alignment estimators, keyed by method name. Each declares which of ``obs`` / ``images`` +#: / ``landmarks`` it can align. Consumed by ``squidpy.experimental.tl.align``. +ALIGN: Registry = Registry("align") diff --git a/src/squidpy/experimental/tl/__init__.py b/src/squidpy/experimental/tl/__init__.py index 968c1b565..bb7b1d0f4 100644 --- a/src/squidpy/experimental/tl/__init__.py +++ b/src/squidpy/experimental/tl/__init__.py @@ -2,18 +2,17 @@ # `AlignResult` is the only result type on the public surface: it is the estimator # contract (a `transform` mapping points into the reference frame) and the declared -# return of `align` / `align_by_landmarks`. The concrete results (`StalignResult`, +# return of `align`. The concrete results (`StalignResult`, # `AffineFitResult`) stay in their home modules under `squidpy.experimental.methods` # for callers that need raw fields -- the public API stays method-agnostic. from squidpy.experimental.methods import AlignResult -from ._align import align, align_by_landmarks +from ._align import align from ._tiling_qc import TilingQCParams, calculate_tiling_qc from ._tiling_stitch import StitchParams, assign_stitch_groups __all__ = [ "align", - "align_by_landmarks", "calculate_tiling_qc", "TilingQCParams", "AlignResult", diff --git a/src/squidpy/experimental/tl/_align/__init__.py b/src/squidpy/experimental/tl/_align/__init__.py index d386fa112..e01742a4f 100644 --- a/src/squidpy/experimental/tl/_align/__init__.py +++ b/src/squidpy/experimental/tl/_align/__init__.py @@ -2,6 +2,6 @@ from __future__ import annotations -from squidpy.experimental.tl._align._api import align, align_by_landmarks +from squidpy.experimental.tl._align._api import align -__all__ = ["align", "align_by_landmarks"] +__all__ = ["align"] diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index b28e12777..70edc5f6a 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -1,30 +1,36 @@ -"""Public alignment functions built on the :mod:`squidpy.experimental.methods` core. +"""The public alignment function, built on the :mod:`squidpy.experimental.methods` core. -These are thin orchestrators: resolve inputs to in-memory arrays, dispatch to a -fit-core estimator, write the result back. All container I/O and write-back live -in :mod:`._io`; the estimators themselves never see a container. +A thin orchestrator: resolve ``in_`` to in-memory arrays, dispatch to a fit-core +estimator, write the result back at ``out``. Path resolution lives in :mod:`._paths` and +transformation write-back in :mod:`._io`; the estimators themselves never see a container. """ from __future__ import annotations -from collections.abc import Callable, Sequence -from typing import TYPE_CHECKING, Any, Literal, TypeVar +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, TypeVar import numpy as np from anndata import AnnData from spatialdata import SpatialData -from squidpy.experimental.methods import ALIGN_IMAGES, ALIGN_LANDMARKS, ALIGN_SAMPLES +from squidpy.experimental.methods import ALIGN +from squidpy.experimental.methods.registry import MODALITIES from squidpy.experimental.tl._align._io import shallow_copy_sdata, writeback_affine_sdata from squidpy.experimental.tl._align._paths import DataPath, parse_path, read_path, write_path if TYPE_CHECKING: - from squidpy.experimental.methods import AlignResult, Registry + from squidpy.experimental.methods import AlignResult, Modality, Registry -__all__ = ["align", "align_by_landmarks"] +__all__ = ["align"] F = TypeVar("F", bound="Callable[..., Any]") +#: Method used when ``method`` is left unset, per modality. Point clouds and rasters both +#: go to the diffeomorphic solver; landmarks default to the more constrained of the two +#: closed-form fits, since a similarity cannot shear a sample that should not be sheared. +DEFAULT_METHOD: dict[str, str] = {"obs": "stalign", "images": "stalign", "landmarks": "similarity"} + def _resolve_in(in_: str | tuple[str, str]) -> tuple[DataPath, DataPath]: """Normalise ``in_`` to a ``(ref_path, query_path)`` pair.""" @@ -36,6 +42,23 @@ def _resolve_in(in_: str | tuple[str, str]) -> tuple[DataPath, DataPath]: return parse_path(in_[0], name="in_[0]"), parse_path(in_[1], name="in_[1]") +def _check_path_suits(by: Modality, path: DataPath, *, name: str) -> None: + """Reject a path whose contents the ``by`` slot could not consume. + + ``by`` says what drives the fit; the path only says where to read it from. Those are + free to differ -- ``by="landmarks"`` with ``in_="obsm/lm"`` reads correspondences out + of an ``obsm`` key, so no SpatialData is needed to hold four points. What cannot + differ is the *shape*: raster slots need rasters and coordinate slots need ``(N, 2)`` + arrays, and catching that here beats a shape error from deep inside a solver. + """ + wants_raster = by == "images" + is_raster = path.modality == "images" + if wants_raster and not is_raster: + raise ValueError(f"`by='images'` needs an image path, but `{name}={path.raw!r}` reads coordinates.") + if not wants_raster and is_raster: + raise ValueError(f"`by={by!r}` needs an (N, 2) coordinate path, but `{name}={path.raw!r}` reads an image.") + + def _copy_for_write(container: AnnData | SpatialData, path: DataPath) -> AnnData | SpatialData: """Duplicate just enough of ``container`` that writing at ``path`` leaves it untouched.""" if isinstance(container, AnnData): @@ -44,43 +67,47 @@ def _copy_for_write(container: AnnData | SpatialData, path: DataPath) -> AnnData out = shallow_copy_sdata(container) # `shallow_copy_sdata` shares element objects with the original, so the one element # we are about to write through still has to be duplicated. - if path.modality == "points" and path.element in out.tables: + if path.modality == "obs" and path.element in out.tables: out.tables[path.element] = out.tables[path.element].copy() return out -def _methods_rst(registry: Registry, indent: str = " " * 8) -> str: - """Render a registry's methods as a reST list linking to each implementation.""" - items = [f"- ``{key}`` -- :func:`~{(fn := registry.get(key)).__module__}.{fn.__name__}`" for key in registry.keys()] +def _methods_rst(registry: Registry, modality: Modality, indent: str = " " * 8) -> str: + """Render the methods supporting ``modality`` as a reST list.""" + items = [ + f"- ``{key}`` -- :func:`~{(fn := registry.get(key).implementation(modality)).__module__}.{fn.__name__}`" + for key in registry.supporting(modality) + ] return ("\n" + indent).join(items) -def _document_methods(**registries: Registry) -> Callable[[F], F]: - """Fill ``{}`` docstring placeholders with each registry's method list. +def _document_methods(registry: Registry, **tokens: Modality) -> Callable[[F], F]: + """Fill ``{}`` docstring placeholders with each modality's method list. - First-party and deterministic -- the registries are fully populated by import - time, so this only templates known content (nothing from optional packages). - ``str.replace`` (not ``str.format``) leaves other ``{...}`` in the docstring - untouched. + First-party and deterministic -- the registry is fully populated by import time, so + this only templates known content (nothing from optional packages). ``str.replace`` + (not ``str.format``) leaves other ``{...}`` in the docstring untouched. """ def decorator(fn: F) -> F: if fn.__doc__: - for token, registry in registries.items(): - fn.__doc__ = fn.__doc__.replace("{" + token + "}", _methods_rst(registry)) + for token, modality in tokens.items(): + fn.__doc__ = fn.__doc__.replace("{" + token + "}", _methods_rst(registry, modality)) return fn return decorator -@_document_methods(align_samples_methods=ALIGN_SAMPLES, align_images_methods=ALIGN_IMAGES) +@_document_methods(ALIGN, obs_methods="obs", image_methods="images", landmark_methods="landmarks") def align( data_ref: AnnData | SpatialData, data_query: AnnData | SpatialData | None = None, *, in_: str | tuple[str, str], out: str | None = None, - method: str = "stalign", + by: Modality = "obs", + apply_to: str | None = None, + method: str | None = None, copy: bool = False, **method_kwargs: Any, ) -> AlignResult | AnnData | SpatialData | None: @@ -99,25 +126,52 @@ def align( - ``"obsm/spatial"`` -- an AnnData ``obsm`` key - ``"tables/slice1/obsm/spatial"`` -- an ``obsm`` key of a SpatialData table - ``"images/he"`` -- a SpatialData image + - ``"shapes/landmarks"`` -- a shapes element, as napari-spatialdata writes landmarks - The path also selects what is aligned: an ``obsm`` path fits on point clouds, - an ``images`` path fits on image intensities. + ``in_`` says only *where* to read. What the arrays mean is ``by``'s job, so + ``by="landmarks"`` can read correspondences straight out of an ``obsm`` key and + needs no SpatialData just to hold a handful of points. out - Where to write the aligned query, as a path into ``data_query``. ``None`` - (default) writes nothing and returns the fitted alignment instead -- fitting is - expensive and usually worth inspecting before it overwrites anything. - - For an ``obsm`` path the transformed coordinates are written. For an ``images`` - path the warped image is materialised: a diffeomorphism cannot be expressed as a - SpatialData transformation, so it cannot be registered lazily. + Where to write, as a path into ``data_query``. ``None`` (default) writes nothing + and returns the fitted alignment instead -- fitting is expensive and usually worth + inspecting before it overwrites anything. + + - an ``obsm`` path writes the transformed coordinates + - an ``images`` path materialises the warped image + - ``"cs/aligned"`` registers the fit as a transformation into that coordinate + system, leaving the data untouched. Only available for methods whose fit is an + affine; a diffeomorphism has no SpatialData transformation to be expressed as. + by + What drives the alignment: + + - ``"obs"`` (default) -- the point clouds themselves + - ``"images"`` -- raster intensities + - ``"landmarks"`` -- paired correspondences, matched by row order + + This also selects which of ``method``'s slots is used, so asking for a modality a + method does not implement fails immediately and says what it does implement. + apply_to + Which array the fitted transform is applied to before writing. Defaults to ``in_`` + -- with ``by="obs"`` or ``"images"`` the thing you aligned is the thing you want + moved. ``by="landmarks"`` is the exception: ``in_`` holds correspondences rather + than data, so this must be given (or use ``out="cs/..."`` to move a whole + coordinate system at once). method - Fitting method. For ``obsm`` paths, one of the ``align_samples`` family: + Which method to fit with. Defaults to ``"stalign"`` for ``by="obs"`` and + ``by="images"``, and ``"similarity"`` for ``by="landmarks"``. Available per + modality: + + ``by="obs"``: + + {obs_methods} - {align_samples_methods} + ``by="images"``: - For ``images`` paths, one of the ``align_images`` family: + {image_methods} - {align_images_methods} + ``by="landmarks"``: + + {landmark_methods} copy Write into a copy of the query container and return it, instead of mutating in place. Ignored when ``out`` is ``None``. @@ -129,123 +183,116 @@ def align( The fitted :class:`~squidpy.experimental.tl.AlignResult` when ``out`` is ``None``; the modified copy when ``copy=True``; otherwise ``None``. """ + if by not in MODALITIES: + raise ValueError(f"Unknown `by={by!r}`. Expected one of {', '.join(MODALITIES)}.") + ref_path, query_path = _resolve_in(in_) if ref_path.modality != query_path.modality: raise ValueError( f"`in_` mixes modalities: {ref_path.raw!r} is {ref_path.modality}, " f"{query_path.raw!r} is {query_path.modality}. Both must address the same kind of data." ) + _check_path_suits(by, ref_path, name="in_") + align_method = ALIGN.get(method if method is not None else DEFAULT_METHOD[by]) - ref_container = data_ref query_container = data_ref if data_query is None else data_query if data_query is None and not isinstance(data_ref, SpatialData): raise ValueError("`data_query` is required unless `data_ref` is a SpatialData holding both elements.") - ref_array = read_path(ref_container, ref_path, name="in_") + ref_array = read_path(data_ref, ref_path, name="in_") query_array = read_path(query_container, query_path, name="in_") - registry = ALIGN_IMAGES if ref_path.modality == "image" else ALIGN_SAMPLES - result = registry.get(method)(ref=ref_array, query=query_array, **method_kwargs) + result = align_method.implementation(by)(ref=ref_array, query=query_array, **method_kwargs) if out is None: return result out_path = parse_path(out, name="out") - if out_path.modality != query_path.modality: + if out_path.coordinate_system: + return _register_transformation(result, query_container, query_path, out_path, copy=copy) + + source_path = _resolve_apply_to(apply_to, query_path, by, out_path) + if out_path.modality != source_path.modality: raise ValueError( - f"`out={out!r}` is {out_path.modality} but `in_` is {query_path.modality}; " - f"alignment does not convert between the two." + f"`out={out!r}` is {out_path.modality} but the data being transformed " + f"({source_path.raw!r}) is {source_path.modality}; alignment does not convert between the two." ) target = _copy_for_write(query_container, out_path) if copy else query_container - if out_path.modality == "points": - value = np.asarray(result.transform(query_array)) + if out_path.modality == "images": + value = np.asarray(result.warp_image(read_path(target, source_path, name="apply_to"))) else: - value = np.asarray(result.warp_image(query_array)) + value = np.asarray(result.transform(read_path(target, source_path, name="apply_to"))) write_path(target, out_path, value) return target if copy else None -@_document_methods(align_landmarks_methods=ALIGN_LANDMARKS) -def align_by_landmarks( - ref: np.ndarray | Sequence[tuple[float, float]], - query: np.ndarray | Sequence[tuple[float, float]], - *, - method: Literal["similarity", "affine"] = "similarity", - data: AnnData | SpatialData | None = None, - in_: str | None = None, - out: str | None = None, - copy: bool = False, - cs_ref: str | None = None, - cs_query: str | None = None, -) -> AlignResult | AnnData | SpatialData | None: - """Align by a closed-form fit on pre-paired landmarks. - - Kept separate from :func:`align` because the two write fundamentally different - things: a similarity or affine fit *is* representable as a SpatialData - transformation, so it can be registered lazily on a whole coordinate system, whereas - :func:`align`'s diffeomorphism has to be materialised. - - Parameters - ---------- - ref, query - Equal-length ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``), paired by - row order. No automatic correspondence matching is performed. - method - Fitting method in the ``align_landmarks`` family. See each implementation - for its method-specific arguments: +def _resolve_apply_to( + apply_to: str | None, + query_path: DataPath, + by: Modality, + out_path: DataPath, +) -> DataPath: + """Which array the fitted transform is applied to. - {align_landmarks_methods} - data - Container to write the alignment into. Required unless ``out`` is ``None``. - in_ - For an AnnData ``data``, the path of the coordinates to transform, e.g. - ``"obsm/spatial"``. Unused when registering a transformation on a SpatialData. - out - Where to write. ``None`` (default) writes nothing and returns the fitted affine. - For an AnnData ``data``, a path such as ``"obsm/aligned"``. For a SpatialData, - the affine is registered rather than materialised, so ``out`` is ignored and - ``cs_ref`` / ``cs_query`` select the coordinate systems instead. - copy - Write into a copy of ``data`` and return it, instead of mutating in place. - cs_ref, cs_query - Coordinate-system names. For a SpatialData ``data`` the fitted affine is - registered on every element living in ``cs_query``, mapping it into ``cs_ref``. - - Returns - ------- - The fitted affine when ``out`` is ``None`` and ``data`` is ``None``; the modified - copy when ``copy=True``; otherwise ``None``. + For an ``obs`` or ``images`` fit this is just ``in_`` -- the thing you aligned is the + thing you want moved. A landmark fit is different: ``in_`` holds correspondences, not + data, so the target has to be named. There is no default for that; guessing + ``obsm/spatial`` would silently transform the wrong array in any dataset that happens + to key its coordinates differently. """ - result = ALIGN_LANDMARKS.get(method)(ref=ref, query=query, source_cs=cs_query, target_cs=cs_ref) + if apply_to is not None: + return parse_path(apply_to, name="apply_to") + if by == "landmarks": + raise ValueError( + f"`out={out_path.raw!r}` needs `apply_to` when aligning by landmarks: `in_` holds the " + f"landmark correspondences, so it does not say which array to transform. Pass e.g. " + f'`apply_to="obsm/spatial"`, or use `out="cs/"` on a SpatialData to move every ' + f"element in the coordinate system at once." + ) + return query_path - if data is None: - if out is not None: - raise ValueError("`data` is required when `out` is given.") - return result - if isinstance(data, SpatialData): - # A similarity/affine transform is representable, so register it across the - # coordinate system rather than resampling anything. - return writeback_affine_sdata( - result, - data, - output_mode="copy" if copy else "inplace", - moving_cs=cs_query, - target_cs=cs_ref, +def _register_transformation( + result: AlignResult, + container: AnnData | SpatialData, + query_path: DataPath, + out_path: DataPath, + *, + copy: bool, +) -> SpatialData | None: + """Register an affine fit into a coordinate system instead of materialising it.""" + if not isinstance(container, SpatialData): + raise TypeError(f"`out={out_path.raw!r}` names a coordinate system, which only a SpatialData has.") + if not hasattr(result, "matrix"): + raise ValueError( + f"`out={out_path.raw!r}` registers the fit as a transformation, but this method fits a " + f"deformation that SpatialData has no transformation type for -- its transformations are " + f"affine at most. Write to an `images/` or `obsm/` path to materialise it instead." ) + return writeback_affine_sdata( + result, + container, + output_mode="copy" if copy else "inplace", + moving_cs=_coordinate_system_of(container, query_path), + target_cs=out_path.element, + ) - if not isinstance(data, AnnData): - raise TypeError(f"`data` must be AnnData or SpatialData, got {type(data).__name__}.") - if out is None: - return result - in_path = parse_path(in_ or "obsm/spatial", name="in_") - out_path = parse_path(out, name="out") - if in_path.modality != "points" or out_path.modality != "points": - raise ValueError("`align_by_landmarks` reads and writes point coordinates; use an `obsm/...` path.") +def _coordinate_system_of(sdata: SpatialData, path: DataPath) -> str: + """The coordinate system the query landmarks were annotated in. - target = data.copy() if copy else data - coords = read_path(target, in_path, name="in_") - write_path(target, out_path, np.asarray(result.transform(coords))) - return target if copy else None + Everything registered to it moves with the fit, so it has to be unambiguous. Reading + it off the landmark element rather than taking it as an argument keeps the call site + to `in_`/`out`, and it is the same element the user picked the landmarks on. + """ + from spatialdata.transformations import get_transformation + + element = getattr(sdata, path.modality if path.modality != "landmarks" else "shapes")[path.element] + systems = sorted(get_transformation(element, get_all=True)) + if len(systems) != 1: + raise ValueError( + f"`in_={path.raw!r}` is registered to {len(systems)} coordinate systems ({', '.join(systems)}), " + f"so which one the alignment should move is ambiguous. Register the landmarks to exactly one." + ) + return systems[0] diff --git a/src/squidpy/experimental/tl/_align/_paths.py b/src/squidpy/experimental/tl/_align/_paths.py index c911c95ea..d91a7f83a 100644 --- a/src/squidpy/experimental/tl/_align/_paths.py +++ b/src/squidpy/experimental/tl/_align/_paths.py @@ -1,22 +1,28 @@ """Location paths for the ``in_`` / ``out`` arguments of the align functions. -A path names one array inside an :class:`~anndata.AnnData` or +A path names one location inside an :class:`~anndata.AnnData` or :class:`~spatialdata.SpatialData`: - ``obsm/spatial`` -- an AnnData ``obsm`` key - ``tables/slice1/obsm/spatial`` -- an ``obsm`` key of a SpatialData table - ``images/he`` -- a SpatialData image +- ``shapes/landmarks`` -- a SpatialData shapes element holding landmark correspondences +- ``cs/aligned`` -- a coordinate system to register a transformation into (``out`` only) Naming a location this way rather than through a stack of ``*_key`` arguments means one argument covers *which element* and *which array inside it*, and the modality follows from the path instead of a separate ``on=`` switch. This is the shape proposed for scanpy in https://github.com/scverse/scanpy/issues/4007. + +The path also decides what drives the alignment: ``obsm``/``tables`` paths align point +clouds, ``images`` paths align raster intensities, and ``shapes`` paths align by paired +landmarks. ``cs`` is the one write-only form -- it registers a transformation rather than +materialising an array, which only an affine-representable fit can do. """ from __future__ import annotations from dataclasses import dataclass -from typing import Literal import numpy as np from anndata import AnnData @@ -24,11 +30,14 @@ __all__ = ["DataPath", "parse_path", "read_path", "write_path"] -Modality = Literal["points", "image"] +from squidpy.experimental.methods.registry import Modality #: Element collections a path may address on a SpatialData. _SDATA_ATTRS = ("tables", "images", "labels", "points", "shapes") -_EXAMPLES = '"obsm/spatial", "tables/
/obsm/spatial", or "images/"' +#: Accepted spellings for the coordinate-system namespace, which is not an element +#: collection: writing there registers a transformation instead of storing an array. +_COORDINATE_SYSTEM_ATTRS = ("cs", "coordinate_systems") +_EXAMPLES = '"obsm/spatial", "tables/
/obsm/spatial", "images/", "shapes/", or "cs/"' @dataclass(frozen=True, slots=True) @@ -38,10 +47,13 @@ class DataPath: modality: Modality #: SpatialData element name, or ``None`` for a bare AnnData path. element: str | None - #: ``obsm`` key for point paths; ``None`` for images. + #: ``obsm`` key for ``obs`` paths; ``None`` for every other modality. key: str | None #: The original string, for error messages. raw: str + #: True for ``cs/``: a coordinate system to register a transformation into, + #: rather than a location holding an array. + coordinate_system: bool = False def __str__(self) -> str: return self.raw @@ -60,17 +72,27 @@ def parse_path(path: str, *, name: str) -> DataPath: if head == "obsm": if len(parts) != 2: raise ValueError(f"`{name}={path!r}` is not a valid obsm path. Expected `obsm/`.") - return DataPath(modality="points", element=None, key=parts[1], raw=path) + return DataPath(modality="obs", element=None, key=parts[1], raw=path) if head == "tables": if len(parts) != 4 or parts[2] != "obsm": raise ValueError(f"`{name}={path!r}` is not a valid table path. Expected `tables/
/obsm/`.") - return DataPath(modality="points", element=parts[1], key=parts[3], raw=path) + return DataPath(modality="obs", element=parts[1], key=parts[3], raw=path) if head == "images": if len(parts) != 2: raise ValueError(f"`{name}={path!r}` is not a valid image path. Expected `images/`.") - return DataPath(modality="image", element=parts[1], key=None, raw=path) + return DataPath(modality="images", element=parts[1], key=None, raw=path) + + if head == "shapes": + if len(parts) != 2: + raise ValueError(f"`{name}={path!r}` is not a valid landmark path. Expected `shapes/`.") + return DataPath(modality="landmarks", element=parts[1], key=None, raw=path) + + if head in _COORDINATE_SYSTEM_ATTRS: + if len(parts) != 2: + raise ValueError(f"`{name}={path!r}` is not a valid coordinate system. Expected `cs/`.") + return DataPath(modality="landmarks", element=parts[1], key=None, raw=path, coordinate_system=True) if head in _SDATA_ATTRS: raise ValueError( @@ -105,10 +127,16 @@ def _table(container: AnnData | SpatialData, path: DataPath, *, name: str) -> An def read_path(container: AnnData | SpatialData, path: DataPath, *, name: str) -> np.ndarray: """Read the array a path points at. - Returns an ``(N, 2)`` ``(x, y)`` array for point paths, or a channels-first - ``(C, H, W)`` array for image paths. + Returns an ``(N, 2)`` ``(x, y)`` array for ``obs`` and ``landmarks`` paths, or a + channels-first ``(C, H, W)`` array for ``images`` paths. """ - if path.modality == "points": + if path.coordinate_system: + raise ValueError( + f"`{name}={path.raw!r}` names a coordinate system, which holds transformations " + f"rather than data to align. It is only valid as an `out`." + ) + + if path.modality == "obs": adata = _table(container, path, name=name) if path.key not in adata.obsm: raise KeyError(f"`{name}={path.raw!r}`: no `obsm[{path.key!r}]`. Available: {sorted(adata.obsm)}.") @@ -117,9 +145,33 @@ def read_path(container: AnnData | SpatialData, path: DataPath, *, name: str) -> raise ValueError(f"`{name}={path.raw!r}` must be an (N, 2) array, found shape {coords.shape}.") return coords + if path.modality == "landmarks": + return _read_landmarks(container, path, name=name) + return _read_image(container, path, name=name) +def _read_landmarks(container: AnnData | SpatialData, path: DataPath, *, name: str) -> np.ndarray: + """Read ``(N, 2)`` ``(x, y)`` landmark coordinates from a shapes element. + + This is the layout napari-spatialdata writes when landmarks are picked interactively, + so annotations made in the viewer are usable here without conversion. + """ + if not isinstance(container, SpatialData): + raise TypeError(f"`{name}={path.raw!r}` names a shapes element, which only a SpatialData holds.") + if path.element not in container.shapes: + raise KeyError(f"`{name}={path.raw!r}`: no shapes {path.element!r}. Available: {sorted(container.shapes)}.") + + shapes = container.shapes[path.element] + geometry = shapes.geometry + coords = np.column_stack([geometry.x.to_numpy(), geometry.y.to_numpy()]) + if coords.ndim != 2 or coords.shape[1] != 2: + raise ValueError( + f"`{name}={path.raw!r}` must hold point or circle geometries, found {geometry.geom_type[0]!r}." + ) + return coords + + def _read_image(container: AnnData | SpatialData, path: DataPath, *, name: str) -> np.ndarray: if not isinstance(container, SpatialData): raise TypeError(f"`{name}={path.raw!r}` names an image, which only a SpatialData holds.") @@ -151,7 +203,7 @@ def write_path( Copy semantics belong to the caller: it decides what to duplicate before calling. """ - if path.modality == "points": + if path.modality == "obs": _table(container, path, name=name).obsm[path.key] = np.asarray(value) return diff --git a/tests/experimental/methods/STALIGN_DIVERGENCES.md b/tests/experimental/methods/STALIGN_DIVERGENCES.md index 126dbd4f2..1a608ae70 100644 --- a/tests/experimental/methods/STALIGN_DIVERGENCES.md +++ b/tests/experimental/methods/STALIGN_DIVERGENCES.md @@ -7,7 +7,7 @@ PyTorch original it was ported from, why, and what was done about it. Every number below is **measured**, not estimated, by `tests/experimental/methods/test_stalign_reference.py` against the reference bundle in `tests/_data/stalign_reference/` (generated by -[scverse/squidpy-ports](https://github.com/scverse/squidpy-ports) from upstream commit +[theislab/squidpy-ports](https://github.com/theislab/squidpy-ports) from upstream commit `b2068edc98974efa54537eca194736e177bbe11d`). Upstream line references are into that commit's `STalign/STalign.py`. diff --git a/tests/experimental/methods/test_core.py b/tests/experimental/methods/test_core.py index 901b6cde1..ab782fab6 100644 --- a/tests/experimental/methods/test_core.py +++ b/tests/experimental/methods/test_core.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from squidpy.experimental.methods.registry import Registry +from squidpy.experimental.methods.registry import AlignMethod, Registry @dataclass @@ -40,43 +40,85 @@ def test_fit_then_transform_round_trip() -> None: def test_registry_register_get_keys() -> None: reg = Registry("demo") - @reg.register("mean_shift") + @reg.register("mean_shift", "obs") def _registered(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: return fit_mean_shift(ref, query) assert reg.keys() == ("mean_shift",) - assert reg.get("mean_shift") is _registered - assert isinstance(reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))), _MeanShiftResult) + assert reg.get("mean_shift").implementation("obs") is _registered + assert isinstance(reg.get("mean_shift").obs(np.ones((2, 2)), np.zeros((2, 2))), _MeanShiftResult) + + +def test_one_method_accumulates_several_modalities() -> None: + """A method is one record with a slot per modality, not one entry per modality.""" + reg = Registry("demo") + reg.register("multi", "obs")(fit_mean_shift) + reg.register("multi", "images")(fit_mean_shift) + + method = reg.get("multi") + assert reg.keys() == ("multi",) + assert method.supports() == ("obs", "images") + assert method.landmarks is None + + +def test_unsupported_modality_says_what_is_supported() -> None: + reg = Registry("demo") + reg.register("obs_only", "obs")(fit_mean_shift) + + with pytest.raises(ValueError, match="'obs_only' does not support landmarks alignment. It supports: obs"): + reg.get("obs_only").implementation("landmarks") + + +def test_supporting_filters_by_modality() -> None: + reg = Registry("demo") + reg.register("a", "obs")(fit_mean_shift) + reg.register("b", "landmarks")(fit_mean_shift) + reg.register("c", "obs")(fit_mean_shift) + + assert reg.supporting("obs") == ("a", "c") + assert reg.supporting("landmarks") == ("b",) + assert reg.supporting("images") == () def test_registry_unknown_key_lists_available() -> None: reg = Registry("demo") - reg.register("a")(fit_mean_shift) + reg.register("a", "obs")(fit_mean_shift) with pytest.raises(ValueError, match=r"Unknown demo method 'b'. Available: \['a'\]"): reg.get("b") -def test_registry_rejects_duplicate_key() -> None: +def test_registry_rejects_duplicate_slot() -> None: + reg = Registry("demo") + reg.register("dup", "obs")(fit_mean_shift) + + with pytest.raises(ValueError, match="already has a obs estimator"): + reg.register("dup", "obs")(fit_mean_shift) + + +def test_registry_rejects_unknown_modality() -> None: reg = Registry("demo") - reg.register("dup")(fit_mean_shift) + with pytest.raises(ValueError, match="Unknown modality 'obsm'"): + reg.register("x", "obsm") # type: ignore[arg-type] + - with pytest.raises(ValueError, match="already registered"): - reg.register("dup")(fit_mean_shift) +def test_align_method_supports_reports_filled_slots() -> None: + assert AlignMethod(name="empty").supports() == () + assert AlignMethod(name="x", obs=fit_mean_shift, landmarks=fit_mean_shift).supports() == ("obs", "landmarks") def test_check_requirements_passes_when_none() -> None: reg = Registry("demo") # By default, registering without requires parameter does not wrap/check. - reg.register("mean_shift")(fit_mean_shift) - result = reg.get("mean_shift")(np.ones((2, 2)), np.zeros((2, 2))) + reg.register("mean_shift", "obs")(fit_mean_shift) + result = reg.get("mean_shift").implementation("obs")(np.ones((2, 2)), np.zeros((2, 2))) assert isinstance(result, _MeanShiftResult) def test_check_requirements_raises_for_missing_dependency() -> None: reg = Registry("demo") - @reg.register("needs_ghost", requires=("squidpy_nonexistent_pkg_xyz",)) + @reg.register("needs_ghost", "obs", requires=("squidpy_nonexistent_pkg_xyz",)) def _needs_ghost(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: return fit_mean_shift(ref, query) @@ -84,4 +126,4 @@ def _needs_ghost(ref: np.ndarray, query: np.ndarray) -> _MeanShiftResult: ImportError, match=r"Method 'needs_ghost' requires 'squidpy_nonexistent_pkg_xyz'.*squidpy\[squidpy_nonexistent_pkg_xyz\]", ): - reg.get("needs_ghost")(np.ones((2, 2)), np.zeros((2, 2))) + reg.get("needs_ghost").implementation("obs")(np.ones((2, 2)), np.zeros((2, 2))) diff --git a/tests/experimental/methods/test_stalign.py b/tests/experimental/methods/test_stalign.py index aaa091dc4..0dd933e5c 100644 --- a/tests/experimental/methods/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -16,8 +16,8 @@ pytest.importorskip("jax") -from squidpy.experimental.methods import ALIGN_SAMPLES -from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign +from squidpy.experimental.methods import ALIGN +from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign_obs # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. _TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} @@ -35,15 +35,22 @@ def _points_xy() -> np.ndarray: ) -def test_stalign_registered_in_align_family() -> None: - assert "stalign" in ALIGN_SAMPLES.keys() - assert ALIGN_SAMPLES.get("stalign") is fit_stalign +def test_stalign_registered_for_obs_and_images() -> None: + method = ALIGN.get("stalign") + assert method.supports() == ("obs", "images") + assert method.implementation("obs") is fit_stalign_obs + + +def test_stalign_declines_landmarks() -> None: + """A diffeomorphic solver has nothing to do with paired correspondences.""" + with pytest.raises(ValueError, match="does not support landmarks"): + ALIGN.get("stalign").implementation("landmarks") def test_stalign_fit_returns_diffeomorphism() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, **_TINY) + result = fit_stalign_obs(ref, query, **_TINY) assert isinstance(result, StalignResult) assert result.aligned_points.shape == query.shape @@ -55,14 +62,14 @@ def test_stalign_fit_returns_diffeomorphism() -> None: def test_stalign_transform_matches_aligned_points() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, **_TINY) + result = fit_stalign_obs(ref, query, **_TINY) np.testing.assert_allclose(np.asarray(result.transform(query)), np.asarray(result.aligned_points), atol=1e-5) def test_stalign_transform_accepts_arbitrary_points() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, **_TINY) + result = fit_stalign_obs(ref, query, **_TINY) out = result.transform(np.zeros((1, 2))) assert np.asarray(out).shape == (1, 2) @@ -70,7 +77,7 @@ def test_stalign_transform_accepts_arbitrary_points() -> None: def test_stalign_transform_backward_inverts_forward() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, **_TINY) + result = fit_stalign_obs(ref, query, **_TINY) forward = result.transform(query, direction="forward") roundtrip = result.transform(forward, direction="backward") @@ -79,7 +86,7 @@ def test_stalign_transform_backward_inverts_forward() -> None: def test_stalign_transform_rejects_non_2d() -> None: ref, query = _points_xy(), _points_xy() - result = fit_stalign(ref, query, **_TINY) + result = fit_stalign_obs(ref, query, **_TINY) with pytest.raises(ValueError, match=r"Expected an \(N, 2\)"): result.transform(np.zeros((5, 3))) @@ -89,20 +96,20 @@ def test_stalign_fit_with_landmarks() -> None: ref, query = _points_xy(), _points_xy() landmarks = ref[:3] - result = fit_stalign(ref, query, landmarks_source=landmarks, landmarks_target=landmarks, **_TINY) + result = fit_stalign_obs(ref, query, landmarks_source=landmarks, landmarks_target=landmarks, **_TINY) assert result.aligned_points.shape == query.shape def test_stalign_fit_rejects_non_2d_input() -> None: with pytest.raises(ValueError, match=r"Expected `query` to have shape `\(n, 2\)`"): - fit_stalign(_points_xy(), np.zeros((5, 3)), **_TINY) + fit_stalign_obs(_points_xy(), np.zeros((5, 3)), **_TINY) def test_stalign_rejects_unknown_kwarg() -> None: ref, query = _points_xy(), _points_xy() with pytest.raises(TypeError, match="unexpected keyword argument"): - fit_stalign(ref, query, not_a_real_param=1.0, **_TINY) + fit_stalign_obs(ref, query, not_a_real_param=1.0, **_TINY) def test_lddmm_accepts_zero_iterations() -> None: @@ -258,7 +265,7 @@ def test_lddmm_recovers_a_known_rigid_transform() -> None: rotation = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) query = ref @ rotation.T + np.array([6.0, -4.0]) - result = fit_stalign(ref, query, niter=250, **_SOLVE) + result = fit_stalign_obs(ref, query, niter=250, **_SOLVE) residual = np.linalg.norm(np.asarray(result.aligned_points) - ref, axis=1) before = np.median(np.linalg.norm(query - ref, axis=1)) diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py index 190054b70..1b63e39b7 100644 --- a/tests/experimental/methods/test_stalign_reference.py +++ b/tests/experimental/methods/test_stalign_reference.py @@ -1,7 +1,7 @@ """Numerical comparison of the STalign port against the original implementation. Implements scverse/squidpy#1243. The reference values in ``tests/_data/stalign_reference`` -are produced out of band by https://github.com/scverse/squidpy-ports, which vendors the +are produced out of band by https://github.com/theislab/squidpy-ports, which vendors the PyTorch STalign at a pinned commit -- so torch never becomes a squidpy dependency. These are **excluded from normal runs**. See ``STALIGN_DIVERGENCES.md`` next to this file @@ -61,7 +61,7 @@ def _skip_without_x64() -> None: def _load(name: str) -> np.lib.npyio.NpzFile: path = DATA / f"{name}.npz" if not path.is_file(): - pytest.skip(f"reference bundle missing at {path}; regenerate it with scverse/squidpy-ports") + pytest.skip(f"reference bundle missing at {path}; regenerate it with theislab/squidpy-ports") return np.load(path) diff --git a/tests/experimental/tl/test_align.py b/tests/experimental/tl/test_align.py index 08fdb1af4..7885a6f98 100644 --- a/tests/experimental/tl/test_align.py +++ b/tests/experimental/tl/test_align.py @@ -157,7 +157,7 @@ def test_two_sdata_objects_share_one_path() -> None: def test_images_fit_returns_result() -> None: sdata = _sdata_images() - result = align(sdata, in_=("images/ref", "images/query"), **_TINY_IMAGE) + result = align(sdata, in_=("images/ref", "images/query"), by="images", **_TINY_IMAGE) assert isinstance(result, StalignResult) assert sorted(sdata.images) == ["query", "ref"] @@ -167,7 +167,7 @@ def test_images_out_materialises_a_warped_image() -> None: sdata = _sdata_images() expected = np.asarray(sdata.images["query"].data).shape - align(sdata, in_=("images/ref", "images/query"), out="images/query_aligned", **_TINY_IMAGE) + align(sdata, in_=("images/ref", "images/query"), by="images", out="images/query_aligned", **_TINY_IMAGE) assert "query_aligned" in sdata.images assert np.asarray(sdata.images["query_aligned"].data).shape == expected @@ -179,7 +179,7 @@ def test_image_alignment_recovers_a_known_shift() -> None: ref = np.asarray(sdata.images["ref"].data) query = np.asarray(sdata.images["query"].data) - align(sdata, in_=("images/ref", "images/query"), out="images/query_aligned", a=4.0, nt=2) + align(sdata, in_=("images/ref", "images/query"), by="images", out="images/query_aligned", a=4.0, nt=2) aligned = np.asarray(sdata.images["query_aligned"].data) before = float(np.sum((query - ref) ** 2)) @@ -187,6 +187,34 @@ def test_image_alignment_recovers_a_known_shift() -> None: assert after < before / 2.0, f"overlap barely improved: {before:.1f} -> {after:.1f}" +# --- `by` ------------------------------------------------------------------------------ + + +def test_by_defaults_to_obs() -> None: + ref, query = _adata(), _adata() + explicit = align(ref, query, in_="obsm/spatial", by="obs", **_TINY) + assert isinstance(explicit, StalignResult) + + +def test_by_images_needs_an_image_path() -> None: + """Caught here rather than as a shape error from inside the solver.""" + ref, query = _adata(), _adata() + with pytest.raises(ValueError, match="`by='images'` needs an image path"): + align(ref, query, in_="obsm/spatial", by="images", **_TINY) + + +def test_by_obs_rejects_an_image_path() -> None: + sdata = _sdata_images() + with pytest.raises(ValueError, match="needs an \\(N, 2\\) coordinate path"): + align(sdata, in_=("images/ref", "images/query"), by="obs", **_TINY) + + +def test_unknown_by_lists_the_modalities() -> None: + ref, query = _adata(), _adata() + with pytest.raises(ValueError, match="Unknown `by='nope'`.*obs, images, landmarks"): + align(ref, query, in_="obsm/spatial", by="nope", **_TINY) + + def test_warp_image_rejects_a_point_cloud_fit() -> None: result = align(_adata(), _adata(), in_="obsm/spatial", **_TINY) with pytest.raises(ValueError, match="fitted on point clouds"): @@ -237,13 +265,13 @@ def test_bare_obsm_path_on_sdata_is_ambiguous() -> None: def test_mixed_modalities_in_in_are_rejected() -> None: sdata = _sdata_images() with pytest.raises(ValueError, match="mixes modalities"): - align(sdata, in_=("images/ref", "tables/t/obsm/spatial"), **_TINY) + align(sdata, in_=("images/ref", "tables/t/obsm/spatial"), by="images", **_TINY) def test_out_modality_must_match_in() -> None: sdata = _sdata_images() with pytest.raises(ValueError, match="does not convert between the two"): - align(sdata, in_=("images/ref", "images/query"), out="tables/t/obsm/x", **_TINY_IMAGE) + align(sdata, in_=("images/ref", "images/query"), by="images", out="tables/t/obsm/x", **_TINY_IMAGE) def test_query_required_for_anndata() -> None: diff --git a/tests/experimental/tl/test_align_by_landmarks.py b/tests/experimental/tl/test_align_by_landmarks.py deleted file mode 100644 index 248fa6fe5..000000000 --- a/tests/experimental/tl/test_align_by_landmarks.py +++ /dev/null @@ -1,177 +0,0 @@ -from __future__ import annotations - -import numpy as np -import pytest -from anndata import AnnData - -from squidpy.experimental.methods.align_landmarks import AffineFitResult -from squidpy.experimental.tl import align_by_landmarks - -# square corners; query = ref shifted by (5, 7) -> a pure translation both models recover -_REF = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) -_SHIFT = np.array([5.0, 7.0]) -_QUERY = _REF + _SHIFT - - -def _adata(coords: np.ndarray = _QUERY, *, key: str = "spatial") -> AnnData: - adata = AnnData(np.zeros((coords.shape[0], 1))) - adata.obsm[key] = coords.copy() - return adata - - -@pytest.mark.parametrize("method", ["similarity", "affine"]) -def test_object_mode_returns_affine_result(method: str) -> None: - result = align_by_landmarks(_REF, _QUERY, method=method) - assert isinstance(result, AffineFitResult) - assert result.matrix.shape == (3, 3) - # affine maps query -> ref - np.testing.assert_allclose(result.transform(_QUERY), _REF, atol=1e-6) - assert result.metadata["method"] == method - - -def test_object_is_default() -> None: - assert isinstance(align_by_landmarks(_REF, _QUERY), AffineFitResult) - - -def test_anndata_inplace_writes_default_key() -> None: - adata = _adata() - out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, out="obsm/aligned_spatial") - assert out is None - assert "aligned_spatial" in adata.obsm - np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) - # source coords untouched - np.testing.assert_allclose(adata.obsm["spatial"], _QUERY) - - -def test_anndata_copy_leaves_original_untouched() -> None: - adata = _adata() - out = align_by_landmarks(_REF, _QUERY, method="affine", data=adata, copy=True, out="obsm/xy_aligned") - assert isinstance(out, AnnData) and out is not adata - assert "xy_aligned" in out.obsm - assert "xy_aligned" not in adata.obsm - - -def test_in_selects_which_coordinates_are_transformed() -> None: - adata = _adata(key="loc") - align_by_landmarks(_REF, _QUERY, method="affine", data=adata, in_="obsm/loc", out="obsm/aligned") - np.testing.assert_allclose(adata.obsm["aligned"], _REF, atol=1e-6) - np.testing.assert_allclose(adata.obsm["loc"], _QUERY) - - -def test_out_overwrites_without_ceremony() -> None: - """There is no auto-derived destination any more, so there is nothing to guard. - - The old API derived ``f"aligned_{spatial_key}"`` when ``key_added`` was omitted and - had to refuse to overwrite it, since the user had not chosen it. ``out`` is always - named explicitly, so writing where the caller pointed is the whole contract. - """ - adata = _adata() - adata.obsm["aligned_spatial"] = np.zeros_like(_QUERY) - align_by_landmarks(_REF, _QUERY, method="affine", data=adata, out="obsm/aligned_spatial") - np.testing.assert_allclose(adata.obsm["aligned_spatial"], _REF, atol=1e-6) - - -def test_write_mode_requires_data() -> None: - with pytest.raises(ValueError, match="`data` is required"): - align_by_landmarks(_REF, _QUERY, method="affine", out="obsm/aligned_spatial") - - -def test_too_few_landmarks() -> None: - with pytest.raises(ValueError, match="at least 3 landmark pairs"): - align_by_landmarks(_REF[:2], _QUERY[:2], method="affine") - - -def test_length_mismatch() -> None: - with pytest.raises(ValueError, match="same shape"): - align_by_landmarks(_REF, _QUERY[:3], method="affine") - - -def test_unknown_method_lists_available() -> None: - with pytest.raises(ValueError, match=r"Unknown align_landmarks method 'nope'"): - align_by_landmarks(_REF, _QUERY, method="nope") - - -def test_non_finite_landmarks_rejected() -> None: - bad = _QUERY.copy() - bad[0, 0] = np.nan - with pytest.raises(ValueError, match="must contain only finite values"): - align_by_landmarks(_REF, bad, method="affine") - - -def test_bad_data_type_raises() -> None: - with pytest.raises(TypeError, match="must be AnnData or SpatialData"): - align_by_landmarks(_REF, _QUERY, method="affine", data=object(), out="obsm/aligned_spatial") # type: ignore[arg-type] - - -def test_spatialdata_copy_leaves_original_untouched() -> None: - sd = pytest.importorskip("spatialdata") - from spatialdata.models import PointsModel - from spatialdata.transformations import Identity, get_transformation - - pts = PointsModel.parse(_QUERY, transformations={"query_cs": Identity()}) - sdata = sd.SpatialData(points={"pts": pts}) - - out = align_by_landmarks( - _REF, - _QUERY, - method="affine", - data=sdata, - cs_query="query_cs", - cs_ref="ref_cs", - copy=True, - ) - assert out is not sdata - assert "ref_cs" in get_transformation(out.points["pts"], get_all=True) - assert "ref_cs" not in get_transformation(sdata.points["pts"], get_all=True) - - -def test_spatialdata_registers_transformation() -> None: - sd = pytest.importorskip("spatialdata") - from spatialdata.models import PointsModel - from spatialdata.transformations import Identity, get_transformation - - pts = PointsModel.parse(_QUERY, transformations={"query_cs": Identity()}) - sdata = sd.SpatialData(points={"pts": pts}) - - out = align_by_landmarks( - _REF, - _QUERY, - method="affine", - data=sdata, - cs_query="query_cs", - cs_ref="ref_cs", - out="obsm/aligned_spatial", - ) - assert out is None - assert "ref_cs" in get_transformation(sdata.points["pts"], get_all=True) - - -def test_spatialdata_composes_with_existing_transform() -> None: - # The fitted affine maps `query_cs` coords -> `ref_cs`. When the element already has a - # non-identity transform into `query_cs`, the registered `ref_cs` transform must compose - # the two so the element's *intrinsic* coords still land on the reference. - sd = pytest.importorskip("spatialdata") - from spatialdata.models import PointsModel - from spatialdata.transformations import Translation, get_transformation - - offset = np.array([100.0, 200.0]) - # `pts` lives in `query_cs` via a non-identity (translation) transform. - pts = PointsModel.parse(_QUERY, transformations={"query_cs": Translation(offset, axes=("x", "y"))}) - sdata = sd.SpatialData(points={"pts": pts}) - - # Landmarks are expressed in `query_cs`: the intrinsic coords shifted by `offset`. - align_by_landmarks( - _REF, - _QUERY + offset, - method="affine", - data=sdata, - cs_query="query_cs", - cs_ref="ref_cs", - out="obsm/aligned_spatial", - ) - - matrix = get_transformation(sdata.points["pts"], to_coordinate_system="ref_cs").to_affine_matrix( - input_axes=("x", "y"), output_axes=("x", "y") - ) - mapped = _QUERY @ matrix[:2, :2].T + matrix[:2, 2] - np.testing.assert_allclose(mapped, _REF, atol=1e-6) diff --git a/tests/experimental/tl/test_align_landmarks.py b/tests/experimental/tl/test_align_landmarks.py new file mode 100644 index 000000000..2a1cda8bd --- /dev/null +++ b/tests/experimental/tl/test_align_landmarks.py @@ -0,0 +1,236 @@ +"""Integration tests for the landmark path of the public ``align``. + +Closed-form and JAX-free, so these run everywhere. They cover both write-backs a landmark +fit can have: transforming coordinates into an ``obsm`` key, and registering the affine on +a whole SpatialData coordinate system. +""" + +from __future__ import annotations + +import numpy as np +import pytest +from anndata import AnnData + +from squidpy.experimental.methods.align_landmarks import AffineFitResult +from squidpy.experimental.tl import align + +# square corners; query = ref shifted by (5, 7) -> a pure translation both models recover +_REF = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]) +_SHIFT = np.array([5.0, 7.0]) +_QUERY = _REF + _SHIFT + + +def _adata(landmarks: np.ndarray, coords: np.ndarray, *, key: str = "spatial") -> AnnData: + adata = AnnData(np.zeros((coords.shape[0], 1))) + adata.obsm["landmarks"] = landmarks.copy() + adata.obsm[key] = coords.copy() + return adata + + +def _shapes(points: np.ndarray, cs: str = "global"): + pytest.importorskip("spatialdata") + import geopandas + import shapely + from spatialdata.models import ShapesModel + from spatialdata.transformations import Identity + + frame = geopandas.GeoDataFrame(geometry=[shapely.Point(*p) for p in points]) + frame["radius"] = 1.0 + return ShapesModel.parse(frame, transformations={cs: Identity()}) + + +# --- fitting --------------------------------------------------------------------------- + + +@pytest.mark.parametrize("method", ["similarity", "affine"]) +def test_returns_an_affine_result(method: str) -> None: + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + result = align(ref, query, in_="obsm/landmarks", by="landmarks", method=method) + + assert isinstance(result, AffineFitResult) + assert result.matrix.shape == (3, 3) + np.testing.assert_allclose(result.transform(_QUERY), _REF, atol=1e-6) + assert result.metadata["method"] == method + + +def test_landmarks_default_to_similarity() -> None: + sdata = pytest.importorskip("spatialdata").SpatialData( + shapes={"lm_ref": _shapes(_REF), "lm_query": _shapes(_QUERY)} + ) + result = align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks") + assert result.metadata["method"] == "similarity" + + +def test_landmarks_need_not_live_in_a_shapes_element() -> None: + """``in_`` says where to read; ``by`` says what the arrays mean. + + Requiring a SpatialData just to hold four points would tax AnnData users for nothing. + """ + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + result = align(ref, query, in_="obsm/landmarks", by="landmarks", method="affine") + np.testing.assert_allclose(result.transform(_QUERY), _REF, atol=1e-6) + + +def test_stalign_rejects_a_landmark_path() -> None: + sdata = pytest.importorskip("spatialdata").SpatialData( + shapes={"lm_ref": _shapes(_REF), "lm_query": _shapes(_QUERY)} + ) + with pytest.raises(ValueError, match="does not support landmarks alignment.*obs, images"): + align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="stalign") + + +# --- writing coordinates ---------------------------------------------------------------- + + +def test_apply_to_selects_what_moves() -> None: + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + out = align( + ref, query, in_="obsm/landmarks", by="landmarks", method="affine", apply_to="obsm/spatial", out="obsm/aligned" + ) + + assert out is None + np.testing.assert_allclose(query.obsm["aligned"], _REF, atol=1e-6) + np.testing.assert_allclose(query.obsm["spatial"], _QUERY) + + +def test_apply_to_is_required_for_landmarks() -> None: + """``in_`` holds correspondences, so it cannot also say which array to transform.""" + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + with pytest.raises(ValueError, match="needs `apply_to` when aligning by landmarks"): + align(ref, query, in_="obsm/landmarks", by="landmarks", method="affine", out="obsm/aligned") + + +def test_copy_leaves_original_untouched() -> None: + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + out = align( + ref, + query, + in_="obsm/landmarks", + by="landmarks", + method="affine", + apply_to="obsm/spatial", + out="obsm/aligned", + copy=True, + ) + assert isinstance(out, AnnData) and out is not query + assert "aligned" in out.obsm + assert "aligned" not in query.obsm + + +# --- registering a transformation -------------------------------------------------------- + + +def test_registers_a_transformation_on_the_coordinate_system() -> None: + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Identity, get_transformation + + sdata = sd.SpatialData( + shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, + points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Identity()})}, + ) + out = align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="affine", out="cs/ref_cs") + + assert out is None + assert "ref_cs" in get_transformation(sdata.points["pts"], get_all=True) + + +def test_registering_copy_leaves_original_untouched() -> None: + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Identity, get_transformation + + sdata = sd.SpatialData( + shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, + points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Identity()})}, + ) + out = align( + sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="affine", out="cs/ref_cs", copy=True + ) + + assert out is not sdata + assert "ref_cs" in get_transformation(out.points["pts"], get_all=True) + assert "ref_cs" not in get_transformation(sdata.points["pts"], get_all=True) + + +def test_registration_composes_with_an_existing_transform() -> None: + """The fit maps ``query_cs`` coords into the target, not the element's intrinsic frame. + + An element placed into ``query_cs`` by a non-identity transform must keep that + placement, so the registered transform has to compose the two. + """ + sd = pytest.importorskip("spatialdata") + from spatialdata.models import PointsModel + from spatialdata.transformations import Translation, get_transformation + + offset = np.array([100.0, 200.0]) + sdata = sd.SpatialData( + # Landmarks are expressed in `query_cs`: intrinsic coords shifted by `offset`. + shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY + offset, "query_cs")}, + points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Translation(offset, axes=("x", "y"))})}, + ) + align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="affine", out="cs/ref_cs") + + matrix = get_transformation(sdata.points["pts"], to_coordinate_system="ref_cs").to_affine_matrix( + input_axes=("x", "y"), output_axes=("x", "y") + ) + mapped = _QUERY @ matrix[:2, :2].T + matrix[:2, 2] + np.testing.assert_allclose(mapped, _REF, atol=1e-6) + + +def test_diffeomorphism_cannot_be_registered() -> None: + """SpatialData's transformations are affine at most, so stalign has to materialise.""" + sd = pytest.importorskip("spatialdata") + pytest.importorskip("jax") + from spatialdata.models import TableModel + + def table(points: np.ndarray) -> AnnData: + adata = AnnData(np.zeros((points.shape[0], 1))) + adata.obsm["spatial"] = points + return TableModel.parse(adata) + + pts = np.array([[10.0, 1.0], [12.0, 1.0], [11.0, 2.0], [10.0, 3.0], [12.0, 3.0]]) + sdata = sd.SpatialData(tables={"ref": table(pts), "query": table(pts)}) + + with pytest.raises(ValueError, match="no transformation type for"): + align( + sdata, + in_=("tables/ref/obsm/spatial", "tables/query/obsm/spatial"), + out="cs/aligned", + dx=0.5, + blur=1.0, + a=1.0, + expand=1.0, + nt=1, + niter=1, + epV=1.0, + ) + + +# --- input validation -------------------------------------------------------------------- + + +def test_too_few_landmarks() -> None: + ref, query = _adata(_REF[:2], _REF[:2]), _adata(_QUERY[:2], _QUERY[:2]) + with pytest.raises(ValueError, match="at least 3 landmark pairs"): + align(ref, query, in_="obsm/landmarks", by="landmarks", method="affine") + + +def test_length_mismatch() -> None: + ref, query = _adata(_REF, _REF), _adata(_QUERY[:3], _QUERY[:3]) + with pytest.raises(ValueError, match="same shape"): + align(ref, query, in_="obsm/landmarks", by="landmarks", method="affine") + + +def test_unknown_method_lists_available() -> None: + ref, query = _adata(_REF, _REF), _adata(_QUERY, _QUERY) + with pytest.raises(ValueError, match="Unknown align method 'nope'.*affine.*similarity.*stalign"): + align(ref, query, in_="obsm/landmarks", method="nope") + + +def test_non_finite_landmarks_rejected() -> None: + bad = _QUERY.copy() + bad[0, 0] = np.nan + ref, query = _adata(_REF, _REF), _adata(bad, bad) + with pytest.raises(ValueError, match="finite"): + align(ref, query, in_="obsm/landmarks", by="landmarks", method="affine") diff --git a/tests/experimental/tl/test_align_paths.py b/tests/experimental/tl/test_align_paths.py index 44678da36..9e5c74824 100644 --- a/tests/experimental/tl/test_align_paths.py +++ b/tests/experimental/tl/test_align_paths.py @@ -42,10 +42,12 @@ def _sdata_image(name: str = "he", shape: tuple[int, int, int] = (2, 8, 6)): @pytest.mark.parametrize( ("path", "modality", "element", "key"), [ - ("obsm/spatial", "points", None, "spatial"), - ("/obsm/spatial/", "points", None, "spatial"), - ("tables/s1/obsm/xy", "points", "s1", "xy"), - ("images/he", "image", "he", None), + ("obsm/spatial", "obs", None, "spatial"), + ("/obsm/spatial/", "obs", None, "spatial"), + ("tables/s1/obsm/xy", "obs", "s1", "xy"), + ("images/he", "images", "he", None), + ("shapes/lm", "landmarks", "lm", None), + ("cs/aligned", "landmarks", "aligned", None), ], ) def test_parse_accepts_the_documented_forms(path, modality, element, key) -> None: @@ -64,7 +66,9 @@ def test_parse_accepts_the_documented_forms(path, modality, element, key) -> Non ("tables/s1/obs/x", "Expected `tables/
/obsm/`"), ("tables/s1", "Expected `tables/
/obsm/`"), ("images", "Expected `images/`"), - ("shapes/x", "does not read or write yet"), + ("shapes/a/b", "Expected `shapes/`"), + ("cs/a/b", "Expected `cs/`"), + ("labels/x", "does not read or write yet"), ("varm/x", "does not start with a known collection"), ], ) @@ -136,6 +140,12 @@ def test_image_path_against_anndata_is_rejected() -> None: read_path(_adata(), parse_path("images/he", name="in_"), name="in_") +def test_coordinate_system_is_write_only() -> None: + """A coordinate system holds transformations, not data to align.""" + with pytest.raises(ValueError, match="only valid as an `out`"): + read_path(_adata(), parse_path("cs/aligned", name="in_"), name="in_") + + # --- writing --------------------------------------------------------------------------- From 68763c9d33165355ae4eaa9dee3c58a842bfcf1e Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 20:10:38 +0200 Subject: [PATCH 126/131] guard shared coordinate system --- docs/release/notes-dev.md | 3 +- src/squidpy/experimental/tl/_align/_api.py | 47 ++++++++++--- tests/experimental/tl/test_align_landmarks.py | 70 ++++++++++++++++++- 3 files changed, 106 insertions(+), 14 deletions(-) diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 2b76e7635..5433ae54a 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -19,7 +19,8 @@ {func}`squidpy.experimental.tl.align` as `by="landmarks"`. `on` becomes `by`, gaining a `"landmarks"` value alongside `"obs"` and `"images"`; `in_` then names the correspondences and `apply_to` names what moves. Writing to `out="cs/"` registers the fitted affine on a whole - SpatialData coordinate system instead of materialising anything. + SpatialData coordinate system instead of materialising anything; because that moves every element + registered there, it refuses when the reference shares the query's coordinate system. - **Breaking (experimental):** the `align_samples`, `align_images` and `align_landmarks` registries collapse into a single `ALIGN` registry of `AlignMethod` records, each declaring which modalities it implements. Asking for one a method does not support now fails immediately and says what it diff --git a/src/squidpy/experimental/tl/_align/_api.py b/src/squidpy/experimental/tl/_align/_api.py index 70edc5f6a..4ee944fdf 100644 --- a/src/squidpy/experimental/tl/_align/_api.py +++ b/src/squidpy/experimental/tl/_align/_api.py @@ -209,7 +209,7 @@ def align( out_path = parse_path(out, name="out") if out_path.coordinate_system: - return _register_transformation(result, query_container, query_path, out_path, copy=copy) + return _register_transformation(result, data_ref, query_container, ref_path, query_path, out_path, copy=copy) source_path = _resolve_apply_to(apply_to, query_path, by, out_path) if out_path.modality != source_path.modality: @@ -255,7 +255,9 @@ def _resolve_apply_to( def _register_transformation( result: AlignResult, + data_ref: AnnData | SpatialData, container: AnnData | SpatialData, + ref_path: DataPath, query_path: DataPath, out_path: DataPath, *, @@ -270,29 +272,54 @@ def _register_transformation( f"deformation that SpatialData has no transformation type for -- its transformations are " f"affine at most. Write to an `images/` or `obsm/` path to materialise it instead." ) + + moving_cs = _coordinate_system_of(container, query_path, name="in_") + # Registering moves *everything* in `moving_cs`. If the reference sits in that same + # coordinate system of the same object, it would be dragged along with the query -- + # silently producing a wrong answer rather than failing. + if data_ref is container and _coordinate_system_of(data_ref, ref_path, name="in_") == moving_cs: + raise ValueError( + f"The reference and query are both in coordinate system {moving_cs!r}, so registering " + f"the fit there would move the reference too. Put each sample in its own coordinate " + f"system (what napari-spatialdata does when landmarks are picked per sample), or write " + f"to a data path with `apply_to` to move only the query." + ) + return writeback_affine_sdata( result, container, output_mode="copy" if copy else "inplace", - moving_cs=_coordinate_system_of(container, query_path), + moving_cs=moving_cs, target_cs=out_path.element, ) -def _coordinate_system_of(sdata: SpatialData, path: DataPath) -> str: - """The coordinate system the query landmarks were annotated in. +#: Element collections whose members carry transformations. Tables do not: they annotate +#: elements rather than sitting in space themselves. +_SPATIAL_COLLECTIONS = ("shapes", "points", "images", "labels") + + +def _coordinate_system_of(sdata: SpatialData, path: DataPath, *, name: str) -> str: + """The coordinate system the element at ``path`` is annotated in. Everything registered to it moves with the fit, so it has to be unambiguous. Reading - it off the landmark element rather than taking it as an argument keeps the call site - to `in_`/`out`, and it is the same element the user picked the landmarks on. + it off the element rather than taking it as an argument keeps the call site to + ``in_``/``out``, and it is the same element the user picked the landmarks on. """ from spatialdata.transformations import get_transformation - element = getattr(sdata, path.modality if path.modality != "landmarks" else "shapes")[path.element] - systems = sorted(get_transformation(element, get_all=True)) + collection = path.raw.strip("/").split("/")[0] + if collection not in _SPATIAL_COLLECTIONS: + raise ValueError( + f'`out="cs/..."` needs `{name}={path.raw!r}` to name a spatial element, but a table has no ' + f"coordinate system of its own. Store the landmarks as a shapes element, or write to a " + f"data path with `apply_to` to move only the query's coordinates." + ) + + systems = sorted(get_transformation(getattr(sdata, collection)[path.element], get_all=True)) if len(systems) != 1: raise ValueError( - f"`in_={path.raw!r}` is registered to {len(systems)} coordinate systems ({', '.join(systems)}), " - f"so which one the alignment should move is ambiguous. Register the landmarks to exactly one." + f"`{name}={path.raw!r}` is registered to {len(systems)} coordinate systems ({', '.join(systems)}), " + f"so which one the alignment should move is ambiguous. Register it to exactly one." ) return systems[0] diff --git a/tests/experimental/tl/test_align_landmarks.py b/tests/experimental/tl/test_align_landmarks.py index 2a1cda8bd..d8ef944b4 100644 --- a/tests/experimental/tl/test_align_landmarks.py +++ b/tests/experimental/tl/test_align_landmarks.py @@ -126,7 +126,7 @@ def test_registers_a_transformation_on_the_coordinate_system() -> None: from spatialdata.transformations import Identity, get_transformation sdata = sd.SpatialData( - shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, + shapes={"lm_ref": _shapes(_REF, "ref_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Identity()})}, ) out = align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="affine", out="cs/ref_cs") @@ -141,7 +141,7 @@ def test_registering_copy_leaves_original_untouched() -> None: from spatialdata.transformations import Identity, get_transformation sdata = sd.SpatialData( - shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, + shapes={"lm_ref": _shapes(_REF, "ref_cs"), "lm_query": _shapes(_QUERY, "query_cs")}, points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Identity()})}, ) out = align( @@ -166,7 +166,7 @@ def test_registration_composes_with_an_existing_transform() -> None: offset = np.array([100.0, 200.0]) sdata = sd.SpatialData( # Landmarks are expressed in `query_cs`: intrinsic coords shifted by `offset`. - shapes={"lm_ref": _shapes(_REF, "query_cs"), "lm_query": _shapes(_QUERY + offset, "query_cs")}, + shapes={"lm_ref": _shapes(_REF, "ref_cs"), "lm_query": _shapes(_QUERY + offset, "query_cs")}, points={"pts": PointsModel.parse(_QUERY, transformations={"query_cs": Translation(offset, axes=("x", "y"))})}, ) align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", method="affine", out="cs/ref_cs") @@ -178,6 +178,70 @@ def test_registration_composes_with_an_existing_transform() -> None: np.testing.assert_allclose(mapped, _REF, atol=1e-6) +def test_shared_coordinate_system_is_refused() -> None: + """Registering moves everything in the coordinate system -- including the reference. + + With both samples in one coordinate system the write-back would drag the reference + along with the query and silently produce a wrong answer, so it has to refuse. + """ + sdata = pytest.importorskip("spatialdata").SpatialData( + shapes={"lm_ref": _shapes(_REF, "global"), "lm_query": _shapes(_QUERY, "global")} + ) + with pytest.raises(ValueError, match="both in coordinate system 'global'.*move the reference too"): + align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", out="cs/aligned") + + +def test_reference_in_another_object_is_fine() -> None: + """Only a *shared* coordinate system is a problem; two objects cannot collide.""" + sd = pytest.importorskip("spatialdata") + from spatialdata.transformations import get_transformation + + ref_sdata = sd.SpatialData(shapes={"lm": _shapes(_REF, "global")}) + query_sdata = sd.SpatialData(shapes={"lm": _shapes(_QUERY, "global")}) + + align(ref_sdata, query_sdata, in_="shapes/lm", by="landmarks", out="cs/aligned") + + assert "aligned" in get_transformation(query_sdata["lm"], get_all=True) + assert "aligned" not in get_transformation(ref_sdata["lm"], get_all=True) + + +def test_ambiguous_coordinate_system_is_refused() -> None: + """Which system moves has to be unambiguous, so exactly one is required.""" + sd = pytest.importorskip("spatialdata") + import geopandas + import shapely + from spatialdata.models import ShapesModel + from spatialdata.transformations import Identity + + frame = geopandas.GeoDataFrame(geometry=[shapely.Point(*p) for p in _QUERY]) + frame["radius"] = 1.0 + both = ShapesModel.parse(frame, transformations={"a": Identity(), "b": Identity()}) + sdata = sd.SpatialData(shapes={"lm_ref": _shapes(_REF, "ref_cs"), "lm_query": both}) + + with pytest.raises(ValueError, match="registered to 2 coordinate systems"): + align(sdata, in_=("shapes/lm_ref", "shapes/lm_query"), by="landmarks", out="cs/aligned") + + +def test_table_landmarks_cannot_target_a_coordinate_system() -> None: + """A table annotates elements; it has no coordinate system of its own to move.""" + sd = pytest.importorskip("spatialdata") + from spatialdata.models import TableModel + + def table(points: np.ndarray) -> AnnData: + adata = AnnData(np.zeros((points.shape[0], 1))) + adata.obsm["landmarks"] = points + return TableModel.parse(adata) + + sdata = sd.SpatialData(tables={"r": table(_REF), "q": table(_QUERY)}) + with pytest.raises(ValueError, match="a table has no coordinate system of its own"): + align( + sdata, + in_=("tables/r/obsm/landmarks", "tables/q/obsm/landmarks"), + by="landmarks", + out="cs/aligned", + ) + + def test_diffeomorphism_cannot_be_registered() -> None: """SpatialData's transformations are affine at most, so stalign has to materialise.""" sd = pytest.importorskip("spatialdata") From bffde5c7da7568b2b0389aac4f26a110f211b965 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 20:30:48 +0200 Subject: [PATCH 127/131] regenerate bundle on linux --- .../stalign_reference/converged_n500.npz | Bin 62604 -> 62629 bytes tests/_data/stalign_reference/energy.npz | Bin 32094 -> 32122 bytes tests/_data/stalign_reference/gradients.npz | Bin 53062 -> 53086 bytes tests/_data/stalign_reference/primitives.npz | Bin 172493 -> 172521 bytes .../_data/stalign_reference/trajectory_n1.npz | Bin 10243 -> 10274 bytes .../_data/stalign_reference/trajectory_n5.npz | Bin 24184 -> 24203 bytes .../stalign_reference/trajectory_n50.npz | Bin 24519 -> 24540 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/_data/stalign_reference/converged_n500.npz b/tests/_data/stalign_reference/converged_n500.npz index a3a73c21c9c6a9b7e5656c48cada3b60a3f34776..9bd3d77d0779b637d149f898ee0c6e25e71b8fc3 100644 GIT binary patch delta 54489 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 S9uSE9?ryl#sp-xO0{ssOJtlMj diff --git a/tests/_data/stalign_reference/energy.npz b/tests/_data/stalign_reference/energy.npz index b06eb05764ba70b1bd117faccd5def552536f326..828f349d96d0d4fcddd39b0b5ee8866b8b5e7ccb 100644 GIT binary patch delta 1224 zcmccji}BYlMxg+2W)@ur1_llwR%DRKZTm1$NU2@{#03I>CJ4QB>iN7|4g$xvU$ z)f%QMlGDulYO7^PX6VYZ3)NGSrB?}c$+Z zzTP~aeQ*0c?z;5v=XM%9^6wFw(loy*+EMuix0a*g3RdoeK^xqb{f>X}<&A0Gvw7!3 z3`EbcaGI%Z5_~OGziNI%jKHFBdBNw#Z<6<2EjweS=+~liysSy*P+5!3@jTBp0{;cg zWcq#$hC9}aWqAAw_C9(uZR??BA8T~#A8b|pX1JZPkh#L@xpkWSp~L0Z zSbj5{S?QnKK4)go{=)VD?rrSbu(38gH~RXw!@UpIDSub|s9QGO{_Dn%KLX>*3(8hK z*Sov0U$;N{$A{NFH~TO2+Uof(`}l9y^5TNN{Ves|{l-`SDsFqW(`}nl{nYe3ORuiC zIX`dZ-K^00|770PulRqfyYh_di(B{RB#Q4Wd!{r0($>B64*EOHW&AWJ%K6)~%V`&@ zGTY9!Y;EyAo!oi-}g)2J{;&9^Vv!=I+MwdcDH8f8AcZ z)qCqZcS81?kF$cB_a5DteSh+yt)ImwyFWYm_HLc$U9KsFar4uCQ_M)jlG5i)A z1A}XPUWsA+@hIDWd12Z_2C7N{W<5~{Mr2Z z{F`Uzr@!uAe?Pu;{=>`};%8UM>9HN=zaD5L*~fOcD0+M8uE@OCdjif^?#koZXdYS> zf1>{T?CpyWety8ZfAMmGLlYAi;<&vIu6?k5fv62fmZM?@i}yjVfW*t+<$sjCm9^nM zK9eiyU`&CVPiO9#V%GV;(iOygK5Dxk`PK8ICBN79sim@ii>`WEo32t>kF9#1>pIOp zf`&66Z`pgkQ&+agf_uVl(^UsE+OL_fx%**FXQu4&dZF0pqVHShPAG5kJ6@x{$N%EP zuID{JI;_OjTrX8$Be~n|%)_eo+BZqZ*t2f8+6es=F!;$QtpGI@2of3AP}<7t0a{5|w^&4$>2%hgspTK@TYYts93 zPhVV@*vxKiZ7B7te&&gn^Y>2wQsTE}E@SWJpLVV1pDC36iG1-)<85KY+hrdY?vh`* za?!Q3eZT(r{Q6$I$M?m%C1Q8K6@EW|*Y`zQiFod&RXP7NtJ>__^RAh? zy`Q9=lUYmN3c~&TSi_jbM#JRXF%1yN}0uD@*B27#{S9kImKB@uL{{uu4FG~oId#@yBNeU zZVs4XM>yLGCeP%klzyA%m{gtJDvQl9T~3%`Mm&d}P2R;>#;Mo2KbJLFuiI*~CD-J9 zE>L~|<$(w6ldn~pv4DLbP$kA%4ixj598fJX*}h7ORg9T|Aq31Ts*++{G`Y9Rlw&yy z14Bhwydg+f8st+iU{2})Vj)P50+S7lb0+_+Qk4dI9VRbsvRa!3?Dc{gF~)t9 z>#LPnL092>?6hw~_z= diff --git a/tests/_data/stalign_reference/gradients.npz b/tests/_data/stalign_reference/gradients.npz index 984850d726b1a8b6f07eb7a32eade902b09ead90..ee10348ec56ee823618ccbf7bd8f44818159445a 100644 GIT binary patch delta 29784 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 delta 29755 zcmce-Q*)*b6D|BqY}-yIwryJzPHdakOzcc}$F^Qui;AqNn;1CS|{r4Zt|D5zcL>Lf#92yyL0sX)9f5rp$e_~;hf&;n;LP)3$ zATdBmG8a$vJ}a(w`a>l`+-C>l(wu8G!dJarH>d24?xlZa&ZIIt-qEr^i_Iq1Fjk=H z{&@UyarwHsZ~A6|Y0TWMb1?Z~tVMoWLa_DfDU;5BBD;FKBTg?knFok7qw4*x z7v?n0>@R-9!3bdSJ`Cf|Hp#xLY&x!B@h3BdY+|W{Stq>%Yv<`2x{+Erh?&0<>~-H& z?)eqampMy-@O0okx{7EMt#Hn54jeQNKHJ+%y%5F~t97Y8ncO+{#kh!Bu<@gOaf52orSLpe(SL-@ARUa*7EN1A#cf}!(Cp`0s zf_S=u2rkEzG8Jw1W8O3>sGMT+qF0(Q5}?t<=7l`FbFs{nebBsjYn}C+Ex|irjJwdrepIkw@&VW2JF5?$_1RXRvKfZ92{cR)TS@4&qO*Z;QB!q24R8qs`F&DM0myRP41`oAIe39qt|Pz>ht zW3!$dybugR6&(U6VFJ>gqpHMSm+RP90eWcF*xpFQ0Lj_ctStiR2hx0_`$ty_qvwDB z{okGN^P;E|9H4B`86jWwpZkmKYP)H_9<0s})SmvUzk<^K0pYUMGn*g_LkFl5{2v@& zuKwcIxFqJfv0H1hTUocoZ}!SM&V5vnNX(tc6AjLz36EDTg33iJq@Yw&IbbT3mKBf? z_mZVd++{I^!M*oJ-tK?)tv=U*J{N4O_W8WpcJyQ9ZRNASGbbk`8!Wlnz^vW>QwFKF zNIN615m}0dOMt&ku_ycSL}KBgg%HR;o*6JVB+$Y81zTk9k<=N$rGC7Q=oxYq?9rkFqI47=whXY z?l8-!Uz1dsEgSi!dbek7RzJiHKd6>s!}6Uu6W&Nl%$t8LScU%Bgk)4fM5`%8Z1fMa zFTW7@Wkl=1 zfeC4nWwkdvhnoEl;q>an6GJkWpW>%xu+-AHlP=U_Sm8kZ{x|Kyt@FNcSNr%mVq{Qz zvEfCbI?=$FvyNY>Ph1Wm<5)M_WaKvuwWvqiCuPQzwM$SO#0~kJA1NdSOmKrRXF$l@ zp})(#EvF~L`jx*zKDkjyqML9WxWnbAkag@Fv$Ib9aMxdSjrOCD%QgsQDj|-7rlhF& z)4TiGfDfR?x;0tgI~l*zh?37xurOf0OhmK_1omit2gIFWRJ11xbT|niGcW1hl7zx1 zYMN9gZ1LB?guhQOhJthX9EIP>VT_zL6~CF)hyvX#9S3wj z&D$Lbj1SNrf(7=T6ToDC*ehAaku0|y>+j=p>H!WLnfO1aLKy0*wo)n3tTkQQT`wti z%c^pna(fN)CPG82Dti9{oLatGbvk5iHj4=JucKjB?~Z>UUHQF8$R8IJ?3O}|m7nfg1B{AanGFDOOR^qGn zdw|$?ht=jnzPdnm1-paK(a5mftYl6tB)#u)jb_1cXhb|au=Y=GmgL7H(Fs&mNlsIA z$X>mfXUr7;F6XOgR@#KMDN~9v{b{%NHhItGG%2?C(Gop6gRyoX%ki6U&1hsQnxRX7 z%_H6-EZ;z*b-vWJ0>1LjZk1Pp{qv=61jrl=o`q?xiCNtavfX(~6L#FQg@2~gjkMY; zy26rmL*yG{K(0p1d=ZPHT;xU4t|#^3OziLW_{&W`r>eXm-bcm}Rs1x-THCMkK720T58>w`-=ARquzh5& zr$6${JQNA=mShif~AB9{j0PH4J-GG0II7$ zrme|A1vCjV#?ciP5qA*yMS@eWEMgYns@rQ*6A11d-MX`AOOgS9V-_&ktS$+4S?9!Jm1ug38bi}Cf!J+9-E1U@IPfq7J!c(9FWJL`zOPsEP z?-~#Ofz;M+Tm_#SeEV|cib3uFYf8V#0ln}3J;F!JVMo;H?g&I189-7jT{Vq!BpU4| z&D~BwNPv_Ja*+7vBoZ?XFdl5m`r}D2u1OzNJ=#?|+em zp;_R@IAC0q|I&u3BOtE>(-8axy4$fohO}wjU>abGtm%h@=eIm$pF}^YBKM!vUOO_} z73q{O*lp`997uPvE>PfwtbA&p&by$;5-$-a>DnIuQ5g)@fgPjYb=GA(Td`)$*od2H=!yaltd3~U=X5b1k!v=a35n3h zCJL9Aburf6h&AkhKAGXjK_#wsB%_-obJwq94w$wPh#x%`?Y!H7;zDJ4AfjEWq;N9n z**@^tWpnC)iTSAq5X$#b5C2S;b%D*OzChXZh=!irEM^K>i8KlIuX7gx!-O=n?`Y$r z2oUfc_HOTi=UTOxQr8lR(u{j&g1r%8`8Y_C5<>Q;5kxvkI1c3iIR%LF!QRe3^e=nB z315x^cA*JWFspY;P1Q}a|GGWycbjc^!buDQie_BQ z9@jyQ`*&GvZ|)x?h%2TX6xoxe3Xldmh08xJHPN!f)u!1ir$ z&oMnVS8GE6kMW_vlHhL0(ZK0p3li*s4CZ^=>8-F8XF(aSES|B<&*|#2yS=Y>>gFNH z!yBol6!q>b;!StLels$8(+jrIZgWMkvtg3v7eZ%ct+$R#O=iMf!9w5q;5i(GSyQM; zc(QdKxGHFfCY-%jU}83*J7B(q0SWdgiO;(a+|%d;iPS zhJ`Rmf6^C3-_=ZkhoBejCd1Qia@Qzz`Y|p$h_HN ziUb_$j#8{sO#MQ|y>R*}sh=ja`y>$L%gp{Mc$===LlRxSV~&HE1?3MGe>No!KXc?GpBNBVMbIQQt580QA4mYN5~!$8R&vt8g}I6Z0XnAa++tmtC0 zF4K;Ry^V+G92Ohn0FE{Jq^C3kPgA-qnk#!Cy?k=v-$cBVZ)5adtN=kv%@qi95b_N z-s$H5-bjje4MSh8_c#omopgP0`y5Bss&ByXpaSnuo$SNy`1oC-zz)+m-aGUmp{|4} zG>4bv8?#;<_WZ%m^OSALT{d3s*q{$UHcU%=7GXoiHFa2~Y8A!E{(z;-wdpHKb4T?1 zmlHmyZQ(HAoaf2QN@m7i5^g)|=EgR^R}u#_lTs;x4_m#($&|&Xpmok;X;;Hl2Bhyt zzZV*zHLfN-H<5jnGYj(Sl11vAYseONjR}i;Z}Z53Qz>q7o%^qm1T;#0yT$;NtSYGw zJBFFEv7g2_p0QZ%N8V_CoHI zJ$aWz!O|b5KGAWyq+uQ#$4w){^P-+7rwe$H9+m`u-mFpuQFGQGJ4D?5edz$6{cT8b z27hhZxuS*^jU%OJvba{1QV|0xJ8{8>qsF@+K1-V9wGQ#`!vEqfE%emRt!8LBERb-@ z_e|e^UWeiG(iUELpC@oQ&89WK$hM;dK)roFUCco~`sj6!NU9{d!pWG+ob3BL->-{s zz&_Y-F!mZ&AlEj)E178RMVK3bJT}CMWq#tXV`vHkym;!X6F36q+7$yvXl_gW zUXtLa#ZNzPI<$nn^TKCA`C}{TjCtr|RY55yz?dlLpyp9Ts5>Vc4T><*gKG-|oK2K< zuvrDwc;0G#+kI|^Fxlzt77NlfYklS`RHS!?L}_?fK)xdNcBRw5uxeh0TAGOHdFFlm+RGuqG5%DWkL(M3+oz3%_<2$K z?pjbIk8)m0-JMcNe@dLyj!hsX6(#IlW)jX?h0Afxz9~1nwNjd!{tNF71de#WV*H6HltRG0v862KZ)CB9!-=+8 zl(2~VwHqO;G-#h;A&y1TO&?I3pz`vaH0VPA7(xNMp*|2_s$ig@3q`*_-(K!|651sU zA-&z3r81!#cEs>}fb&AyD=d2d^|Vgdzd+_MVXMxC&N72NaB;K`YIkSZ-D6HTd<==HU6nJ5 zQ7X(iJY90)(S$Ks#RVOG4$fZbedUIehU6%ZA^RAr_XW|CDHP3Yo$R^&#Ex_m)B@&0{DpNuN^Wm0V#2}PiI0ov9BLz z^zLj%wyCsM3{5Nnfd$_ps~pBScRLEP6I>$U4C|j40`WK#^XA!_%woOzgB({L2RRMB zYgu#?2pMhb1?^NAH8?VJbnu+PmG=kw_%@i*rd72*6*f^!yIVy=A!jJuB=>nLv;U25 z$I4>R0#*S`9EaxXlyd$VBWi`KPLWT`zdanENWuc-mP;hICzuU_3 zAeLy~Dd$Z!ueMj*<}=4|GGW)<>BPWy1!*587cW}iy92wpGl4+8{>|<5YO1lgOI5=|x zek^Kzzs?H6i9wKap#{XYDk@@HcU>>zcjh=@yuUTGbHlq5i?-1?hi3L#61{7F;X7=J zR63~>mwqBkba0;knHXYuThzf%%0HH)*;}iL-0NEVR}hgkn5l(CX+^U855_GthinPo z6bTNm)c7MJT!M35cFVW>(O4y%%-6sjh~vbTBOkwcDU`&fN&#E#FFz_o8B4}4*`N$p z(o$pF*Sx#d8lqun)JBNqjCO}q#rStC$GjORh11;5rhd85 ztBI>y<{ulEh9oKjBW~sfu|D={Mi90I)SlJR zB*XqFXF_0OkE>H3+Lkq5RlWPePEXRq;jWM$3d@j>=Y7m6=NWRr`1@ERM67fj%qoe} zQ~{f7*t^`M%4@~Bw4Y!fdQ2@1vavSDQEtvC^k-rf$M`GpT<}b&RC&Nf6bX96e$h=G zBU+Q+S8S*pv0c@XwKF8$@v1K+P!l5u|Bgi9w(+WuUTSEKJ4;Yz3AZ^&eVhZ8fNaIE z#uQVogdvZ*a8rSpZR9zZBt=euBc7xl3tQzI$O0oF2re|7bA} z;rDA%!-qI@F;7KDygOVC6z$QCf855ZNkkJf2BYM<u*-ersp+$ z+$Y6E#^inneb|q05@xRk4n<+JUbTwTKRfbil zb=8O&;1{xu*f(Gnptc!-JBM-(bM6P`fz6w8#)Yc3c3T-P$;_?&FFkSr{#B3YZ$JhL zWj$+_T4HvPx;)C3Pe#AMplx8iq&RrTV3vK3+AZVuc>Kjh7IAKT5D6PD$w_hN)?ko~ zQrXw=!%MmNk23Um+Z!bnRd$v>Eth^Nw9j;d{IPWJQPj9JsD>Y?C~_aKvc?FG(Q=qe zHN2a-^0;I>#0;>J+Jm}tkVx#_FaC1Q$?Y3udCKabtyB8NS$H8`*R=i#q7+5Gb~ zXSQ5+OiBv*m?#`y2ysq)xt>pG>BKtk_nUsiD3?Zu3Uw(E?Wfrb{qzK>21`j0nX2bb zqbgn*lv8&lE982!PUr=)xSKf<v2DdU%bh*LuR^gT|M&Z0o* z^n$oevpBGwa)J!%G;+b>n~q%)`_2N$%A!W8#)dZ5J7`jI3D%uu5cvs~{FwS)NG>oQ z$qPl`l{A6;G10J?NYEC>6xPY^L~I11j#D4aciGpidG-$kjaLmSRjoHr#A(?D5jitN z8izOKL5B98z^r?45O4&9uYmDwPeK)^niZ&@elR zUAsle%nZcEY2`lHp$^U*Z6~Tv9}IH$l+25+RX$*4^Eav?^M}FciR3iA)hFjx`}DDRVs+M!SkZpyd?+u?a8Yrs97FR5|l@q$_P0?+|+g8Cy5$qh6UbB z_fCG=Ua{$}P4q1Q@2O!l-s2tp5R-Q<-{Ys$@%<0w>lI(9bs({y=&R#6f!gxlm-Tv? z>I5*5V_{fgiNQqmXn4%Nr6uhDdjfl(i+@B950s>+9%4!&Ro;t9YrcgAA&{=xzlpNH z>-GUg<72nae=iPC_&1s}j#gBfN%M`yBtKMK_PKYi-MHoUbMY-5c#OMWYMb!RW;cMs z`B3tYBn?=Wi0g|T6%%&x?lG8$$t_mEdH{4Z_iFu5Cnpso9qZf>yS2+ABHGyk7x2Vd zQrD`FxvOK0T|m1vG~ftH!S_@!mcMIbE3`GMLdVLk)K>S-9P=y2UM_Q@xu<*e&NAQ# z=A0pxttuC8Iq#d+&t#QnK+HvwwEL3Nw*xX(UK2LnLjF$bKg7zbXxn_``u*OrvH(8} z^zmtUvE|b6Ko`doSLNYej zI6E)8o}zhk?8%tRVpU62kZ{})AT820-tZ5VQ^2A z*b0~iiBjG+%&)Uh5-IN?lo!RLc`iMpDmiK4w|kzL)J{-y_KFH>o(39n%*)~f&F^rFils>b6)zgQ3H z7KICt=U+F7n&$58ewP1|gg?qYQvWEqqmdU%|EXO7JLR?^;!ugP102?ID2-`=L$3>c zZ~3bH*ecGFZ&)w;i^|<{$=42c#=`)vZa}*a%vEVHJg3q;X@zUWsSNNkWgEuYnCA_y(8?CzT^&?U>_>5}t<^^*@my{`LEDPb@M;p5HAsRj zBD1+TdzNrp*L8FE0P+ymX@1^nfg?4%DxlmcD!A%*t)pcb=YWgLypj)51eJc7-QFVt z?hlSZrESm;!LM*~RbrkSZ?{N6N;SMpzU`%uDMuq;-Ch9zmI}?4Sg*2mB2ZnT)6mQ0 z&NC5p^7b_CzbpUZq2;QH`bfmuXbaz9lBZn%$PL-u}ogZ3qL5X6wl7+8?EYk!>7Mg4fZHo$@K%aMo* z--3!Rpo_UIzexqE6I79!EOB=tWS&EFND3JpuBPrs%`*_(`SNre4-r3?CDFx43+eE; zVvr6skK5Q1h_`@;C72pN?7XCbDBTW^i8Pd`wI)+N^ez66ei{3Xi4a?4e4i<1Pihq9 zR@KegTYh*rHdAW}(r1-3o5VoRtqF8(-oM_m@n`-~bf+rc{RO|?Xv1#vsFQ>2 z2a?vq;7}v`B$IFPgb_I(2 zX1L4Y(slFqRK@CDMP-m;g!_HAD<5!6N~M@qg;;HcA^skXx`+zvm15nbOM4Red!JNL zXHe4X#@0E7YGUA@BY#H{NR7z;j0a2Ncwp8qh5ECA0Ub)02xnq4+$1&?IpK^xvf)^( zuI{-K@a?YNh`CpF%=B&pg-RhXwZwP|bus*iH=jR6H${bL=#KMx(8s_nL)`p03@BOK z6*I`BkNOd#)3|UBhLbJ8?RL>g0zr<6CWxozB>y|f{zLB z{1Z)GY~>%1xd`Xfu~?3-=a&L08r-B*_VE`M@F)CJqRT1TY_FlVal^@81g7xvZaGDV z%6?-0{XqzRSK`p}dtSp<+W#NPd!7n}L8e}|gg%d>YL{(*kRbwyI5C=UJRbFt`BUy* z2X?@TU4kz7zNcVqo1Hu4?^4ECDAUSIZRGK%X-XM|xsp6Fzlq4O#pC%o-2;V_o7`45 z;751wHCa|(BzKCiu;s<)Tcr#$YmjKGfeaf}@wH^xOdmACmx+eyWt>8c!_ciy5tNJn zcuM+8HiGEk<9qy`YKW&CcJn++VS?N#wsw zM2A(p2(KEa$$hoyg_jxPONVD8I^2Z^;3ZE{!=UCP{t!gpZTd<%&h*QF^iD3>u$W{qjEKo({ORpK=)t_LVq^_Z zfhhPEGEoQCKv6!E6;xCf^v0~GKJE9^ppibvTIfy;tZz}Euj%?_+lw!I=Z5g&e)orxWp^O!LD$az+|-C#(KZ>Q}g+yE9?n@?3GiA#2^yNxER zJqnBW!9^)ZM_!p%ap^sF(vD>-vtU6&#n;e~ykd*6qd0UxFEPFEW%dm35|>4Zrp>ON zw3$VDc6XJ5yF=Wy*T=XXKCF@<#^hQVFZkvZTBFA%8 zz~cTV30-ArRy^k5)!ex=h|bgKIp^9pF#(2t>3D33TP3$Wz@Ct7T-dk$_pb&Lp_p|? z7l-j;lL}?vZ|xx$W~4+vwMzhmQE&J0O^lNuf#o!Vc1K^VeVJz{zh@jR^WB7aG;-(e z1E#48Tuk(x6~q0J$cW_8de1S1`jfq9lQ%Tvde-(68o~~QFYB_cd%>S(@28={KVEzN zg)XWN1Tr(cyNxq_2Eg*=DMn-T#)(i@`Ov6I+6^awjaCG@6RvvJoIi*Lf}B1yHJiXO zMW|o)j}1S`HyXx$$YiZhl4K&a=~Jjiu3tT4ebFJLM8dVVn|_Iss_aBYVi8tOgf}9V z-iu#+hWcn*obHI;HYwmIhn3-1oMgVApnQ0ke45!Qc9=NJ`<|lo<>h|BRkAWhZO+NC z#i0NLr(0!<`{t$ymCPsnA0fmN2FG z*gkE^VC8<9sXkh9aX*J`#&6^B;WyR~Kc1a;tA!Jee2H+&hWAt#CwSue=6${*h+qTC zsnfO&9RI8mHo8szP=tW#(6wJ5{Q<(-J!%I+lVR@L!rqnWct}x+JUm~{33i)p1+GbH zsjmuj)g_;&P^u@yHuSpwIexqN9!KTt z#NV^;KHjE9oO25?va-Dq$gmB>F&WTF)j|A5Pb5Dd&7f?U0AII5hBxNTerpe`yeh=-+(37Z6R1BFv80)hSN%pV4-BgZ`d7Q8wuXo8^g zvqBaN@oy9zyluQC9xh zA9uzhbyL{YMP?)d^*K<4U(7j;Cdq(M*-6j=v$OGDlFcdC!E~?i5WNFETq+85V@?0c zBk2j{=vGCU$h*KpA0j~Aygj`0VkMNh)PrKJcNqw;-nh1{IHiGc?^qRNS1M6By%E?Z z4N3^-ZHz_TBios1>xu%qXP;;tdG8TMEO-QyS6^oKC z;VVZ@&4aREmCNbk5fFBepO)!tNJjH2fhXI@Ht?0qA1f$`gdqiup$EtwiKlI!YjhJ} zhl_jP&s|AjJa83e$gVUAi^G1sOtXWCyYBOrEi$pgKA!LiBxqePn4%2g3A9N%JR_On zu!2eNA0qFvdnNXA9G`;uXvts*M?G7E1Hm1ZO6h_Q^^f*t6Q=-t_qsa}on(t+ zQ+tl{`X^+7|IKJs=bI@G?A&rB#|No*rr7*=XN!^X{=;uNCG1>}|FLRnDVJ(e;^wBI z=V1YP1*M(F7#a8zN9FfbRFZz6*#q#FUhdNum-vSy%du+hHNZr6DBnTXp$qSXiuYKd z56i$Mcadg`DsCTn_qwapw2_>;&rp!muY*@C%>BI0-To+`CZ%Akhl3R>JKUE|yBGEf zK$~`(c-?jUe7}oYCUATr!h}!BG3nn#a|L#LNd2a}amn(yg>(p-4@Se;$UC5FSNBeQ z3BNzGsIbA{f!6j`ci8~BFV&gKG-`q)#EOW>+|5QQJ zEqWhXG8w07I=cHIos_(g+q_3B>Tk){;KbHiG3%8oipU z;$pf_4<0d5ZF+jmEga{m(-M#A%0mmJZ=+NWy4D#|uTvidw#k9*z)i=N}_r=H?cN6cshx!9+lQ6lNq-~ZL8ZKf_GP)qTagl zSl$q^phxE$I|g+at4qN-$y7S=ov>>Qel}|Z*py)Pn?`)BFA#B9f>YfleAGL= z3fX%F)bIz2kD~Tt!4;XS4_xW;lf7{Lf6t$BA z;Jb}6ki}^OuU-;e_I@_sb5@}lOcY^csl3w#O@t{iYOT2NJ$>G~3-GW|d^XSYPaKCt zFZjLfLeH&q?~UK}@!HGB!C{%DMx-EewsWj2ii{hApc9W}wKdo4-_OMi3Zh)wZadX` zGl$U9^yx5(P=&Dkn`)V93B7lN>;9VpvV1>R%0nQFAO4x3W{LhT!8=R7Qt#!IWj=LB zC-itH-?l3;GERHg(avN6KWD3G-$OSgt&gh}u0re0pG?TrF_hQ^M!yE_ELeiXpT7+X zCy(MV&m_(X27AkDxiH%5#QAf5=ewFT2m}QyCe7dgm}10`#+cy&WtYdc5yMA7Fi*g- zKS#D1hD_F*h<6AP#rj(jDcu7mkkYKsu)#LK(f_vLTcZPoA|h50XT*@=D)M5wu5?EV z(Pnt4!2k{}qMK8*Z7yfJGhe|(83L|*O5=mSC$y>E{H@SLm3t#k*1_yp@_MLy7P^@x z;MpN<=7ot!`Eb%_Uowdl9UULQq3nk)?!2rr=3789ewjN-rWRV-UQB${u7)j}Rt)r2 z63IxSVM%UWf`S-|;eMDANp`a5|5C><{cxy)!(t;LXV%w1t%Ah+6dwG7QwcMyYPy%x z4DBnfc@K|AYhiBRczPnmEU1crix;8}E;amSAdXNM&d6<(m}S6i(Z~i~eFkI*Hjkkv zRpHEPMMRIXSAYHJ5JFetk4hXoGF=>td!$Hge>8ANNgQh86U--azYmgAnO}>ojbBkV zvBUFnNS?W&@AkY6$H~yImbKeE7LF53RK1geYN;Z42$R{Dw2?oLU}ocGmFt3v6hP^z zL$$tp!z0xm0?-MX{tW;|#Y?~UfB92<61ChIYZ4(cWocpi9)hdvR%#O$MTwMEUG`83 zB4po2B7T%Wg{Ce0%m}tRi?LIbX8-dhDT+PDhY*6SED{s)Ff!#G z`fq!%quPU-?WW?6GeMa%Xg)nb&QO1#rjPyWYl;k>e&}n2gYg5_71#n*5uA$ltGxJB7YH2}XhWE@axtgh>w`f#cGh-R z?lSz_spuA&Jp=Jwzm->Hj9TvRCG21{#AjAtrFH|=7s5=Jr!$kp*aUW?HQ|v1tT7$D z1wo?p`QjmiQ{O<#kwPr-7gm3B!SyQfHVladkF4vs22@e#dvP=#4o$I+Jas6X$>ViD z@)sXn813hDJjY524zG3Mo1YBUs>twBnwjkbN*Wn-4^WF2t&PeySM=_gBw*lO;g5M#ve z<`(;eH)`~t?GJw3PI<6J3)&?@z&@Y|6H{thi48wbBbJXyU z)S8Ou`StlHX`=0+l~=1N6!tW$RE%4{r(1LMg9o8iGIBDX9@UcnY+IN=JfK5a^AkZ4v8s!!wDmjp zqL4ZQEr97KwOJxf1J70K6!KcyhI=cCTS0J!x*cGB5U64z6y)0#OJw}~FKMme1kOZy^{1&oR9?T%U2#ZfEp7w%xvGa^SdU%$W9$^yQAwQ| z4w4s1I4S09dE^|Hw4YJ!{(j60{r-twb>m-cFje)B@BKj@FQeq<&q z{g&5d30Zj`L!BF&|9C&;bjy&CG%ntaqOJu+^!ugHx;Rtaql{G~opQne_5B;hw;U_? z(V>0QKMGi++4o+4~iJ~c&v>vsW(Qs|OU=#Yjp z{Yl2_>L*hm@m<`RpHH?m$;m=-6`YUY0k^5+R^x(Ll&k-~8y4|XX4c9;wnJFyVKcP< zTdj}u<-_8FH#)OwVFy>q?5$##>%k?aONTE#ukHX^uv^#hRFF&ZPMbx$PmUy9921aV zF1k_4?C;UpW)(!{yRIaeRo*brA+Mj;41yy&>Pc^y!C;RZ1h=Yb%5-Pq$UPVGC z)3;j$os?*CF_B$NJ2hCTt=vk)>%F`g7v!3|TlgY8D}&AiRO$69F} z{^$JG%0PzQ?VVwrs^SmGNvuePoNR{Qx9)z1kaC0RcI25O*W^GbE&9O(3&UQEL$d8o zO!nonYycK9Oi_VdGUFIhKOoC>R0cU0)9><;x8tNiuj-y_9p$(gDD~{f2H>n?@#`Zx zfiVR>zODM#--jD}bvgVlL2rH!E-O?K@oJ;gCt}GrO+s~Cx#Da0<0X z*i)tE{OMwKFpTERg5c_A1}52h}o#^!Ps*e;+NL?ltXP& zpO;|3H3qt9n`-7Ydf9E}15F@hVfKn!pv~D(A1|hAksEZW z%5x=uude+c^~yTUX{C|fos{s<9(N?+E=O8g%|D1*!`m%;T{WRoN zwt~F6U~q}hE3=H}2e!%tt;0il37ssuY;qLxm>$SMkgSHA-p+eDoHVVsX+|+cHN0M8 zBr7V=*6m#am0awi>-%rx+pu&or4O=DRmBFu+rENt`2pv&B%`#lX?u> zFM+LZD;apC@ce9O*`LaYn2XbqbHk=@d~}RVg6e{idh%?{qHD?Ulj+Iux#HF5OOoye z148$h;Wwy*z>`h*@Q3IBuH*v@EU~0k|I7qDwWxG=Hddtm+N`HKj&{=L8<~V}cqlCf zDb@cwjcb%?+XhQ~nUq4txBjg@mr~}jM9{2KnWS71e2=8nTB)yqx@MjJ+fNo>AOC#Z zvlJyM8M95ei^#c@q6AAw+Xp4HfagR5+L_abo5>97o|~~xpj@hy9(p6 zDS)Esb-Yq;1y7eM4IF+XGd+x(qxSGB??%&$m9v<#k=Yx(dyyTKrK%#BAVkz1>M7#r z-mr*e-PM(AuQMzDF9u0Z5L-zTYI)Y#C!gD3KLhrO29lw5Do*y;wS31R^w z$Q>j77?4b&f80wJCs;qOY!B{3q_Fm;6{^EacyR)lcVw5L9m?3<( zEe#04#0bq4th@Oz0+y7;Ve759>MOg#^ZOk7E9dd#KQR3Ko~O8YBU+={m{c)p4bM|T zfOBvE6;7QbK<)Q+@o8O^JS#F$7L88BqS}zJ`DGvngXu!0yaIL5Dvu_|_V4kl@^3#R zKe~jHzbQfQ5*?%bEpg0#wsI{G!9tQ{q)uPPZ29fuYuT0XjP&jp9p}HJQZ?~;ESxw@eKTMR9Pvm$deg53DXl%-K8#p4NB+~XQS`4%(HLCO7;E!6 zYSX4*h~=l56h$3xPV^B0zlwbiZZu9pVI}g(lS}w)Z_9ff(}zoGF?>4>V^0@)0ZPyv zybDoy-`f0ndne|C%WD~MDLqP|AF?9W(!1V)R9X%t z=i$F2k-B$>#diwSC8aiHmqB%c&Eu>Sp-7ad{?yyf3yN$~C@f}`E@1_24&DC=_D%k} zGQxwzW)SNvqFrQhyM7Cu(o+BD0ay}Qf8+;e1o#`6^AXAr<+9z{ZYduL;y=tuA;g$9 zKdl#?l8^=w8QI;b~1I%C`ScVBUm0R+EPG6z6;d6&f7l z<905?K^1D%A;cmO7(TW2g2bWW3Ul*Ac%icJU=As^CyTKt?7l3}|mQD&%|j zVkZ{#^3zmA9H-T(GiS%hCDn9{t2e|gg!T#!s2<BB)A`n} z8wDBBq{JcJkKVz3rrfdwe8gb#+(7BbFN z510&;Ij|T@LkA*FanV=4&`7p3OK$I~uJ;nMJm`8k8*q)?ML)yyF+VSU5Kl>8dU+J* z&>oCUK7y>c^AKmvF#|($P47`2M3^k_ z+}kFC-oF9u&T)ZUz>J^yL8n8k95dKbOLneImm(^#=rD~UWT{=lh82Ok_DClC(%JzQ zCxd3AjBf1jmm2-BYaKh7KlV?(&56HwxoHcTRkrpZ^@vaMfnXUcqVN;+2pZqi$C~~$ z{AL61?(%yz(M*>Bj@!S;?cG!duq9*!{o`&?XnWS?^%X?6*LbJX7H7qMzDjZ+v*ya6-?E1GEw_@K;>h9 zI`0-M&2h&%19@+kfAdl-$y1w}JUQWpYofCM>#+^ukDnFAt1DSrzg_Bz%@c1Son8`$ zc-1&Z_A&;mf&VI9Q7I&{M~UAdt0fOXk#tGF6FP?BtZqQ?-6Rl&r1!#U5BHQh=C`Jd>*an@6MIqnG}f6XSnB7r&n zX=8EXsiWbk*5&lk2VS7(k1B6}TN)3oi92)Q)saItFIr^orzwr)7ufzikq=T_CB+k& zkWIwpg1-i|x3%<+ah8HMtTkM!|7GrhMdKbH0bvVzSUB}-hdhCqT)#W6je|J|XbGw^z!DZEex9v8>oC!ymUm@|Nhk9*pBf0i)7L*mzw-(%FJaK0h{@zeAK zCfxP6ZzxWqz7KOc)U@|T!k-g$-EOdo;ZS!RuYRRQ_!_WCSyWqz#&}d4e5w3EX(U3I z0~Lo8nk`HtzPUDBw7blk8HcnKZ#XI(6frMcHGY=DrBXA3_zP&8SWr;aa>_P=PVt7M zj&Xx|k^n6gaOB?U;EMf>EVOlrTd&B__dYDv0nkvFH!=F)t;#ugcKuK0nMsT?}CW?X~yrZMGwPCwPLV@^SUd7YD?lLX4g(yth8rX(q_O zqWqE*Q~xjMGBf|-f)GQzN(pG^|A#&rK+GCI@H545fxFrKnBS@E;jnRGkwUzq@p1H( zOKe6}D-+xGcqlY`IKaU!jYC}aEASl}+#n}@mD7bwP#e&8$0C?P?PtuZhXliH(=Y|MkWy%Ge$jBDzH=VbvDjiw{*nS7`Vvj@juJ-x(~_K6d1fd7EN3U>crJ4)T;qvzWku5;m1Cn)N-tyws7 z(~;jgp$3RvL%}(eLYp7%NZj0oifI|7A`BiM^<|U%acQaO^=+V_bP%+ZhHVinwxqc_ zv940(wm|$9y#kt=B5_rjW899x>xiMCb$pm{6mi$wWH@l@F4C(XTNGwMn%U@HOW+wG zXF|rvCf=hF8Ww9G8T6NDciy-`_~dpO`^uOeC7l1={Q`c!r*KFM^Uc6zpdg^px0VuD z_iY{+_nt!YsclHfEJ{cBHWv&C$mRwQauU7$|Q zGX{sbfgSLhnw+YH#`8(|ogMqN91uBtW`dqM;Iz@m*+H}ENnQ$ z_Z(1}Hb(Tv4}o+s{<-+w-M(wa!GO*oh^ja}blW=3pEqYO`vgebeL)yX*` zJBf?VtO|hvjp+)9!@MkVd$GR1r?!YP&`lcHt^mR6t)~=ym2%t`2-)-lP51?Ngst0i zxNLXZoi4UuyYk0i@>!UT!PS}e=E$hw{hAzyVtN^f0L(2IH<2e@$g2mnqWQ-|CZJ7f zK%-c&ANg__JKko}ur+3v2(P4!cMcu08>5IKNn3ApZmr=Ex)Yg4I2haxI&6LBdWyd^ z2iixmO1cjxo27f!Z1M+Ae>SrDyjxp4$Y#%Xp%IlIH19GDGi(1cED)TLr?=Xoc_=rV zdgc*Q2k=giSOiZNKd-Dt#0esI@xjqlf# zQwjx!>Mp%fA9WAX!;#N*xmsP3uP^!^blE9G4AWypuxwm z!9u7Wi*MC4=c8XF0(h$&@tZkL^Y~Gk-wi$&&cY=gaqn}U&(Kvy87(pqeq7<}W_L(l zTZvG8`;kP=4M?fyXIDS|?#Y0*EiFHEvcC?6D~f5&!fd(aZ==rlw1JI>Xlm!bD`tI{ zfS8xFQwem?pAL3h1D0{0RAhI*e-dqax^U`&*^zDoBFAj53@5&28NDdgzqA`;F>_0- zWNTZmq*^Omov%4&Q*CYfQKM^KyXGe<@0?ur;+C#*@L{)k9M<{=FBWKP9zPoGUM0dh z0Fs#Ahq=R~HcdNsBk-m|NJaUXOxhwc@bTMO2VRjnWXdFY`R!35Cch+vz*pyCzkRJI z#;(K$>*X(IuHzrzqFWQ57tDn5l%pCfNBW@$Vr1GxZYa5$Ru8VJlA*SyBaUu0)f52e z!~Q&Gf^!TDO|+#i4!DX{hUc2D`KZLj`WxgyyHio!Tn9>}-0CRAt;ntwIVJgk{QQkc z6dIxAJKd`62Lmc+4L&*>PcVy(%~lyYr))IayFHd8+F;`##$jGURX^m-hXISv@iB-E zNp8|xg1Ymd@yZTGlqaa!nDi#(N#1TLS_8d%H;%9k@BA-ZyW^(TccmGWjII|-7K!nk zyi42You1V%8#M#yQNwmvsF7RT!W+k*qVPL!Em|L?q(HqBA?>T=2U<uB56iYTRjr&L&i~WMbGZP=Qf+{g=<@bAJixLx zb<9UXslhj-)?($8vd{{!YqY?CkUnGz?z8;{TOO&;=am?uw3IRBae|o%ZuL*0@!KAH zu0A1%AcdE7`{VWWr(|c$4l!o0OZ@tA2Q<(vE!Zq6o@o{;?B7#AGzfcrndh zk%sqBPZ0@O1#uHmwbo^3)BUROGXyuFzLU!3|A~jVp#kG`NHb9Q&QxYIVME*6{e3)D zbAuHeM_(o|WwDX4aeTPGMY705+#O ztGUQIXsZ>7)5ISk5Bm^g(&+B77~_t?QD4XKM_D!R3-&r3y27`_Oc3k%=G2u5cUBm4 z|JX-y!=qX3Y)*!5;7t0sGyqp{GST)lW*q5gC`j|8O*mw72ArrlHP-X+i*3auyfW%( zePJo8$dPO@+BdQ%tWG&8rW4(S`?st8uLtE%d!``Z?m^G8+alZ6)tMYph+nen?JIg{ zeeu?ImzFkj5@elXJd|=V$ZlhLRQ$xIk}lo%HT;zsMA(?MvE}nO>GS(-9=mIChGh{} zb8JcQz`k%nMHg4Yp*&j0Y=IxZa&6QiBo_3=six?-_Kg9_F$#uX@}(Q$+6| z3;kIR%=Z{jvR_8kovJMkT%IoGZS!ODgWQjv^-4m0D4{>#plTeS-wOJr%_)^iXa(Od zH3NcX>DTtn@7ppakM<*sm&<`B#-+pvox0gLnK&HgjWgSAM?<88gTy=; z1eo_r7i^b|U*g?c-kUET;*K&oZfczh7`y)XPkZW=+XGz>(LP9!85|QC0+NxANCmd7 zA@Du2GOl&sD_Juj7crc-w!b`{05u{ar^{_)!u`4F$fKl8Eh&CW0nM{fuei{(59)wr zQMMhIaL;8yMWQs`p(0}Ii6&lTYI`679Z^;}?131!1N~^hDc_7Uj5Rm3OnQVZ!S+{8 z9?Rc3Q+xGs73AMUpyZ*842#DK{ZZH=490Ida^yib+V;nZWW)G>bUKX0EV02q)Hg73 z=@-8bjc%BU9kU?EwEuA8uI#|R-qeNB*PMeIn2h+Ty@e_Z4EAS=_tfpeyE+1=42C?^ zL%rpLMLs#mPYLnDatgj>5HfQms_IC-$3pbw8V;plbD?cWRVFt(pu{JpAYZZHv1R&r zcRNzMJ1M4QAo*!0GovE`PAdD0mMt(@55ZQK;M=!y`p6?gQ?%nFhMpIV>6pI?@B*0n zB~?EfMb`jezV3l$_aU|WsT*iYnJyCyQYAXqBgL$8J!KoB3ee{y=3TgU*%8Wq!%&^i zY_i&NoeQ>=YI{%#CAD&hb4yfja1_4LFOW}UG$`G|pK_b;&*K)1aR@n0J3DDb)8pphpDxljZjlTDAk0uN_^C?VYW$ z*|s{GL2-&!xl%2SEqnY-n~ZH(#OH-!V`q!eoHpRzcdfORhaX};9A{p&Q3o&ex#Sq{ z7BWdDtlX1@V^Dto;|?83g2!(vitl}5N~kBx@^to7Da9#Vn9TT-B=4>!osg_;tth0} zFSVxnuULrf@KQO-hHR@+^o>OVY43u;v6Qa@b^UAOTEAH zmTD51WxJAndDWn7s$YNA#|F9VprK=XBlERWKIQ1U#wp1~0N}Zo_X)B7NpP4xS#8L3 zT3k3LfVSd5h_V^KRCxZ;{~*Lt_Bf^GiJd+*0)$B{HVA)^up$As*O3=aS)wHRkVN5q zyZiMrrWyoZ-~AF`fqJcD$Wc(^Cqd<+UO@eY@GSY#iQ%iMaiOPzCTCbA8>Yy*@fb(E z)+W#0I|DM3Bk+M5Zg$!`y{RDL@CDgdOXfr1gtI%PyEFak))R@^EBBjvCW|xG>=POj z+|FPKVs<$wLuBIHkm}Pf>Pb!+k-S8qpoq7dXx>RMV2giI-ZGQ zO{?(uyWHjuOw}l6xU>X37I4{?Sqbo4?8_~G&7bA%zXBxgXIj02c7?WWW1Fs=1D3Ur z4C@MC*e`u$WK_w=AI)dJRW|Gl?dUL86~(a#lA}h*BJ%4Alw1l{M3^O{dnh9P-aXUG z-E+;S&5wC1| zx>LcJ1{6@+dIl8}OJgi?t(U*nPU3^Kh1`;*5)gfR&T@k6s1rjIT<$BhjG z;q{E!No!WLoX(i;shTV+uRDeO+QJI!YynXf*w>Ls zd`apjsyfcyV607FHp5ht0LDxT2eDQOKC)ti2{Z6QjTZw)|WuU)5u+lQVClw%mSIe{`k^Rwy$>B<2cH1?Q|oe!(!_#6p__FCrA7QcS{#VJ>&^avD7k zA}E0O875h5j+u~lcP8n%S{L{ z{7&WU=f&I8D!=3o$%C1i$mhpziQe|zYwLYEvNO2JY=4hp5(=^1iw=4ChEO< zQRC=To4~1;6>Lkfx;daG3rIbK^MU&5-Uf`!Rnei&Qi6>eB>l8Q0BIiAC!)uUpEf?F zI1X($wd>f+`ubj`SQ-k4g#Y{A;5rvHMd@%Pdoe!AAZzpe11n|pw+)44M|ROp3yRFVV5b-LZG(w0o7TwW7_v>VIql&G(W$7B%3`3JTJ# zBW;wl>eGKKSsvL}0%h$VxvZnv+Ox_dtj*_~TOJ7$2b^nW{>3rjbJH{_`>ujB$@5*^Dppvx@H=Bz_hq+4&w+q%P{7NX#E z6(dG_wbm8SL@=pXvHi&22Whq?i~IlXz3OSD_;ynJ21cMs0&krlc(d`gF+q%fUA946 zr%tKfcIl|+lJ=IMiLF2F-VcbMVGUAWF2fUGVCDxry5dZYo+v+5#k?4%hnjv*g9mOZ z+zPoD4DlJ=+~@KxW^)C8Fe5t5g>LK!U70IZKVQFWa2Y~2u(Y-{JB^`AD5*Db_!ire z-uJzs#ga-q0XU%%b%*l_Dy!AIJH`^ty!5V==PDs~<7FlGkNYBB9%6@@Ijm4?sdV)% zBip2wV0@Wx=z+L?5H;8o@pIqW#9o^S_&L~E|I&$XyV>1E?CDw~T&9CF+>6B~6muF) zmC<6T9L~Aw4+A_*=CZGKl$&YB-}U{Hpep602$#M^06lyrz4uu}R1|1-tyUj0B+`H4 zMPXfx-f=&)uw?Ka0yS(8IGv3)iY3l<>AuAJVCzwp>fvZuQ$8tBn`|)zWRw@LTgk$X zUf-yS?h=%Wlm;TtrEK}ySF6Q~Z`N2wPn=cgvK8 z;={QcfoDTJn%BxmxW(hat^35~`q7n2Ds{|k^1PI|9O=CkOj#{Wyst*B3qD0H!4O+>!kb@LjA zIg;~v0aPs)7YPp+Y0}F>)bi^R*Y^3wbY}Ap;1Ae*)BS_UpP)D;RX+(kr~~k_;0lSJ zqW!qA;ldtlZ@&PenJRde3N*1PONi3VAE0l^qE7#MZ!`Wkl3->F`(xFf zDirC`H0VJWD;!ZQ=m0Q~B{9Eqd-;a;ML`B0c|}5&eEnp1IF6aoH{6M#v^HK$e{}*8 zVDfHJPA_ldJ52t(DFpvbpk9tS*usAyl)~OOhbaGhX z+;_V4;mwD9za<~kCwrayKhMb~%4_Z?@$tK_XTU&+bgD@r6q|gxJ%Gm@5J9o}0tzZd-Xg z9r8bdr$|~9jUDxWMPo63K8`PRj;Ha6f=81k4|Pa?$Zd}uuxC>4bNOSgxr^gG10Jz{ z@IqX2R%&?84|Zq)_)|s4^T%GF4Qwlj>aNoDE4zZk9CU| zJ4G_cTiXFWC=wSG%6Jtc#2Le`GTHO*X z4`EZ)XT~J#Z$*0s7Snb)#9VnpMyGs>O;$6XWu|R2j&WBBzqx<^7z5(DWJ~ttuyN)Ur9|z7I0ga3|)e6t+;VgUauEYb!H9=bV?d~3EP*Lu`xgRrac)o zHBr%QIZ5V8h4mR-incLxc71b%jXCGhV(%-2Kf#3hA+fh)nB61E(CwDm>qfEi1|C`X z38Ll?`#A82Rxzg6}vH>so>o&ahF%V41&#Pv^2B*)F>S{+d%i93w~}_%7SZe zQn;)RT;$2{-Iu*75Ev5I$^7}~mj6S2T z%0%3BTvHftPzx$mE7B~lZx6(rgRjL|efi3bLcV(Xd`Ci+UulWg z_2{^5RVO>LT|;R?%PmuV#XyxI80ORQms6_x^Df2K3^!`Jy1TXJ6#^=2Rzq1s0Mtk@ zHXwGXh^|}d07yxtC~q5dob^&3ve!)h@ZofzC&p6{!sWZ-mhUe-nu3V~n-+<8Nj=T7 z=Urb!0UC{;a0r+XU|Y0z_ZL7nB2rKZDr;!Wm;zGmRZULWoS5QcrvmTtvR}p1H47F~ zCGiFiFGg6SqWZz~Nz9w3YV1!0Y(5^bkl%nx!wuLYpl67@q)(N|aW}xB{9eJDRa*C> z?wDr3_wA%#zW0K_YRB{M(hEJDKwe|ni`^kyC78+J{FKUAn8v!K{(_w}%}C3)EDb8m z8dhj0i_D+161J2)L1~g0wRU%&BJT>dGdC)K)UsKJWoi7ag2_J?aDsyRa`**Z+}Gj3 zYz>eTZ>vj&sWc9yX8onTK0XydEPC-xf+lOaB8T6%-z&jLzU`y^HZ5?Dy)z7b^Q*Yh zuik-EJ%6nnp4!@3uK=x`Hgiw$kXXJrU<=qjHzYYL_>rf^9^5 z6nB%QVYw3Z`R^;#y3k~7bFMZ!qN9yLLk)qd8{#ucFn({kj09gyn=@|1P!=@(@taf7 zC&jRqi1k6F_FRT%Ee9hZk1CnK)mx{6q>p2ma3UgAv2WE7S2|eCxZ|XF(;12S^4@Ws zPh{vVS&HJ1-_FnSZC8V=aOzQD%&pAF*+e62=AKoT03P1s?Pb0t{%@|--l>9&I7XI( zkQk})9Z!Lju{auI#1@5+WOt;isXdD1oxCpfTJBw2KjtkPFS~ECvJ4I4VbDGFVQQ15-43%Ir2eId= zkG;2h55vbR-l-$*oXvBcm@R{1YNc`8$xJkHq*92AirLe8JhK3;~C#(3^G zwxgI0xa`a@=M`_?z0p9sB_x4~d?iJAM_!<>4H{R@*f1^;j)DM{9>EMpUscv7d~aZ# zJfBN26uR|~8a9+*%ehnJn+JF+m+|(3!*8&OO_29bsOC=_^Mc8vk1-`&QjNjdzk3w_ zj&M6AG1IC^fI<{|TG;z}-lq948Dc3; z5AzW}KI{#sn}dPG@4?z9i5iipTY&o?dOs5Kkw-2{&{wGgCtvVrP$=3gH{ZL>!aE$9 z>vzYdfGJyCu|G~7AS}7r|^WO{e zxlO625SHDU^llopp1XWDIRDCCEv&S|h8zX4gYCDvGokR@f_?`%aZ1S5DxuXZdXzri zp&ES;TW#QX`f6R!noRIe@Aqw2WPEf}ozSZJqV4rE6SsFk~99hJXe zmvz{sC}vF&X{3Psxu0})g47`K&~35=LYqCqjG{w_XJW1l_njR?6=ZgE0zzy6yuKi* z7jp?Fb;pq`9YXezkrYa4U22g>cpPai&BqLrzsSa%PIT-}vkEHv`K#cPuQh2L-uRHK z*Z_1bUXX}8m|2F#gq9gTamB!u8k^1RJ-(lW7b(I7O?NIF-piMNV&IDUk`G zIL#65@8btSDaXIncA3WnWi8}RSGkZgPYW2lbhOWtL9`bd{;iXBnZXrY!PFB`J2L_ozB z%8R>h1V&;yWKG55$JA1R>yb$(a#D~6?v&h5MtKM4Y}KKWKA#sK!>GQ~bC#Yw>2LPF z?j8zY3@N9I`0KxxI@_ygvkG6(NsGsK*fX4K9@_H_XMa-ozqwJLYBKj1rMCGqEhk-H zrSb+cSM_lgu>%}GO4|G; zudpRl?0WQ`{LFuOQ~MJp?St>$%Ngv2Fs9KKag~-SLdbs=7UuHv>@RHuPTKNhwa}u| zHmPEb{h`nNNDv-~E0yTI4VR0c*;ZD4!Q{8!)Nn zm@+O&^puUr&2feQj#t9~)d3v++JUkt0d&wBeVO?`!v?SMR;I#f$@2J_wQ9;&F?!?< zZ4b~7Zl=d7+HYEL@9g0jaBEe1qko@=T&P=A6M}W#YD6%BtPG9Q;53t8CC!@_REP$H zKBFpTF-}GcRXnL8Frh(X{@=^bOab7N`IQ~kydc&PPm|S-Yh3ec51_=EKiOh9vSdWk z!DL43kQQP)VrT!asx2zp@n$DZg6>CXEVOz3GI|-eZUsi;oh&lsVenB$8u;StG7z;u z;(y^DDjc!un2vOaT8AT74apdvv!x|% z+NfTBX@OJ}rqjnh9nkZc>`#u(P_%17Q%bh-qsGXO+2hjnH{3a@G5!0G(4Z?|1;fYb zpO2Q%>hNxOV?fS5ZYk(lD~EPT`0lfNCO>mw@fK&m{?WnfgH)2ClUv}Nv;lVVDtxHi zyfSo%__ep8&5%lM1UG@nRjy;K{HvhMvW&OPGYw^WOJR% z^7Fu`zFCUJ^|?uAVPxkj{8n!*Lbl*J>>x}G@%EuT18xvO=pfH_)5Gt}px2nH#@fAK z>P&DoGgKW6)tJ`q=GjFs)dyO|l1G9l){>DRdb_)wGm0IIK^Y<6}o zkLTIvpR&=ueLPQV4NI6eH+6k6N5Qf_>zyhQEo0FqMpd1wqRn!hMr|g~uh7F^ovg=S zQL}>r2B^!#>FSg?x9F#g>Wt@OSPmfY1fyhu8iM@36qnk^=se!q2Fy#V!tx&dBrCSv zG8jtj)An2h=;AQ&re^{N^Hafj{}_wc`ftMzj~8f|Kge}U2kA+UM{a>f5w<^IW294N z)+w)x+`qG#X9PKir9|PQ4G6bh&l*h3e3JJ7G(Prky(pa0C3QyJOv5QD3HY%U`p>R6 z_fnt0Q5WIF!`6ubrYUdd@46{kz-hHZ1{baX@o9OMRE5*|S4uz4BKUU{H)ud5rCSQ@ zG@*>lYeUR%;Vq-}*|sYNA}{^NcX`;E-6P zKq0588aFu5J5}xN<6|tyQgmZ9W}Dz$jPfRg(OsqG=AF|UXK|EI2(=fYqnCRxEuXXT z&YK^Oc|kl{*HtS8^=o?L8dPgJPA#j zO?MR(u=gYA>3iH)@-GG}Q`icz8Swx(iw}{)y+3xq-@GaCNx|yDy{u%?_J5-a?6{9t z8>7-@so@)AdzkcF1;y{6JrJNH$r_Um2_HvbJ3h+=PPU_cY0LjkaUVoOFvi=srI-R? ztm4nHQw&WZ-sx)>NnS8JvA6ulLpNT(3?F?`{y8cbKfj4-@bcH4@*}=0d+aFCUI(<} zAkhR-buv^tDT=c3`mW=hjVTgZ`Sb;-jBg$@h-zHBZ<5$Ghea);pV2~QjgvO=DvUW5 z5jEm&T|L{_1iEc9Xo7nNWR{+aiDF43-KP-h`FFZ^-IGs>Q3?A?eVt4jpJiosclMd5yjM*d z)P72+DXB4A){O>9m0n|4eNksF>*{r~Y{Yy>K7G4b6%WI8YXDxHZDE&Ee4F@t2f_J> zl{mGcmd-^X@u*)+4Z6r>(dcg$Zw_pZ_*#eGsW_ug?U70bP9jd7USEqs{r3;kb2A+Q z?x%3ZyZKpVbqCl}T)+iFjswYa@AD0hgYTzsHKvh2gi-ycNSHCD9<%g z-=-R)^NgYm7MAUwFqO}kR~|uo>!FO-z+&%hrU;vUK-|&g3UD79C?C+0rnW9uug^w>L8Tctt-SiXHH3mR7% zxp;f@2x9dnwoZ|hPd7CnXIJtjMifasb|Q&f6!x=Kb~0qpVT6_saXr zBvh9i;AT&tsUfBNF&fytR{WG)PQO>kqrcM%eZh z1ia2Njm0Tu!!T3}#8T>c*`jDq?RF@hD*5_x7PBkfUN~qGF=`S=xHydVKP_C94yE5#!1hL{=w&nAd@jtM5uXD!q{s&P<@F z*2qEbTe^3+wjncU*!GSt$HFJavbV$)WEI%Dem38&^rG z(KmjV7>9V&acWYzcdsf4R;fTCWpdXKGQm`>!Jj=-)=hvWUY?bgRm2hgH}O{_@TE6? z@A}U39Qr%`IxAuODO>TE+bW2JEyZ+C;P=xWoq*IA8}z=J&qxT(iXO4*bM^7S{c(f2 zFY*?4VYSB^GrFZqBYPEJNn~N$)XGcB9O=H%e7mm>>V4MY(bnnwO_80;U~8N6vO$Z6 z?gyh#D#Jm_ z;+z5Hm_CebmjN>$aRrtr#O~4WR;shgi0x&1SdqgORl-}@#At{JA&3$j@nzi#s|KP;5!J}gjOS4d+$9FJRo;Q2n zEc=98(L%cspCvJ~>S4>to0Gavndwq=a=Sgb0d0TdQtH~X ztBbrYlD;dB0g?rR^?R0>jXyx>(VI@`uhzQNUsE3M9>|DL zg31utGHAv)%-!bfh=(K8jVmx}JDtbet4#EKx9NGmy@Y63S&_^bope-97dvqN?-x+4w6SJar;mh!7#ub33W zSBPO)24l3pX2$0Ff_h4KsZ@Df>e~--{1ri)@+LoWmN2p7Iqge675r-@KKlcn>B`8^ z@Mpiae#f5gBJ-E~PAoh$AN)@eZ6W106O!tGJpQrO=^B8u0g z@Gl&pp_a7VoS$3bAwpV7QYcyF&va)4;Gj3r$=i zd{!fcIwrdA(Ot6(PI=H#+)wtwhNpS0{Lnz46jsy;Lpeq)P@)PhJzpYJ?mh?S#2&7i zvss$LK2b~;{(?SF8p8%#^{w`^IVqNH2ichJC+HRIDTCQ$@n+M>f^j>*t0M&SCibOh z+sdU{iQ6j{;7ayma=R=-h$a5W>8Z;(c!sA2+4E6H^&$*zu*uZ5GY$}%JDu$({E_4P zdp6AP@GvM9C+z`E{mjptPKw4Xnv9RFTL#r(f2A6xNZ)$^<@9)NEI72rt-i%gjsVfo z!8o9Y89#Rc`9UAPgW2|UJ1vFz>wQ(N%QBl{mI50M@cE{8EjM(S&8G;X_|-hqW{HYKO(qY7yGt+cvL#wKi5~-0Bbg-pptHhZbz)Z<);qvRu_kA)SezDGq*xRHO}w>n)Yaj2 zUSNXlzbOZ!XgM|NWKl(lj9t)l~A8PdAb|2{1?Xm&&JU5A7wN5--7?Yw9Wtd z{)EU+;+-I*_|L3R{a+;h|I16jeEMYRY;0!uf6tGbz=K#!Dw`062u`|~pvM2t!|*@* z!fuUEpV0r;!6Zq{lfn>9Nlud#1pm=Qt^Q?m|1FgNwd=(rsb-Q4|37Hx|GWQxZzxK- loFpgwj}`hKCiK5+aR1i<{9d1u9KG<8q^D3|4#xgV{U41J!|MP5 diff --git a/tests/_data/stalign_reference/primitives.npz b/tests/_data/stalign_reference/primitives.npz index 92b77fc1bebac7146def5ee3524b00d87823e7e4..48b515a2bb3efea2cdefd0e2f9bbfb9c7a7e8bf0 100644 GIT binary patch delta 142672 zcmY&g z)<@UI3VUl{X+NLREqax@hYQ#<{q=%sz-{k(F{$7F$-g9UwQFi-QhWh@GNo`ym*@^` z+zasxY~ujzOAwoOCUQA)X3Gc6cUc_^Z^_50f0lm^m0k`Y0*~7nX`y<9Or4XkuHo>4 zrLPQn?&rK85(~Jb%w?SBH9}3;p-c~+gKk0B69=p#b|1Oz#0atYSNGaJo~0@CTs=)= z`Ti|j+q&*OygS-U{Mbh(x|PEl*;A9vLOk3}FiNE>7g1NmIqUIu3VJ>@xNXxjjPZm= z@f|l$(i^c;0BN?DDfSh=XpLJ1yQ@rf`7UoERQ6;!d_$LWKC#-}Xtdt(i`CiEmp);+ zSGd0EN7xs5+*Z?PCwWh|*H)n8Q^N>#EPP8Yxaad5F>S+6!iJ-DcgLsqlwKMtn%Z{Z z!u!E3^8e2~>kG=!@Batr#1t?rfScuixtE>#EUfwcJyIp+ikc6zN^TZe+V#AZI^KCP zepv5Ds|u5>ipyB6nxOhG@dqt=VZE4?6dj!GmbdqJ@b&BA#?*P}GVk%Lrx0=Md*SHX z*uSOd6si;|*?wWaJ#5B&T84bWGA2@b22>neLi9`gU3A<_;v3+680a=m@~d+BQER8( zyVOwh))!dc|3kIDpSJ&d+A8#f6HTt)O*9GBaC19v&wnGv4$Ms{1GEkpq5Z|jZ|NBR z=QGJPe7HHtyIDd|Zv~Tni3KMBHWov9j1{KKO*fJnIEF@B|N6cdvEEfLfP6LYK$0N% z{h3}r4tfn2kla~A2yD3#{Bmmj3)vuMyT`rPe;a+>z#@*S|KglV`qGmgQ!=((kS=>E|-xR+Q9P)%133&uyImg2M=ddzlyfKQqS{lQ3ebvdxE&BlK__y0qKOJL&NqeVMZXCqe`&D#bR|5*xh8-CFCptXb+Z~#^@S8HO6fqV(B!~`ICYLi2iNh_-z@QB{BOD1^+d}U#|4wNP{HS z^^#uWup)YJm)%lv4CC&;wxp;ri>wpFEnrymEUH$YaTCwV&yd91bg@Akvuv`x(4Mp0 zA9{<-cIuL4+^ywQ`dQDYjqO)NxZ;W-?G<@PKT`(_h<$LHlhm*?x~rSblglh5?R4U{%0ot@lqk5jM(q zPp583s_yOL->1K#7oXi0uSVG6R?+s{S4*_y^&Iks zo%dtH7&$P*ykaQU@)n9|@2CR^&vgF+Wp!T`V4SyC0kb{f_UH1Sr^sH&ebG#rH_T|W zBxe=egWn7IcORO~$T?0Be-ACVdT4%kx<0IQu>PS*DgLx-A4P|u{`E&s7FwJo^D8a+ zOqE7crG%LH9TNgk0gs&Tta@}0i<|T+57epiulAZK3F?fWMD_SGuL=i{Jasay%qwUF}dsyNOS zz~AS)u!LF9ffpnd{6K2(csVt)*?i)e#LR7xO8c_t^q_0bJCKd-WR7i9$)Yq#``q2d zUJ{R&&XF=%d(nAoFeDA3K8_;@v_EixIp>H#NAkkvWKSsRu7>rxQjbvOumI{#@)cfs z1yEo;3q)%A0XG*3x$s?u@SQ8Q@N+RF{k<7ZWBXd`y$i;s8(vGeKMm&~(@WELzF6WP zbAM3X1e|tsz7XQgsJ=1n$vTUEg~1mE*@P?iR2nyYhoj5S!#cF~MD>vaYJW3sb>3Dg z#B4bW-TPV|31YV@x{ZKSnJrct@}c{H;26mjJzj7v_}`5BW_* zoU?1sGg#c-c#KbnesE404*It(pMQi9>Pv5G+T^!eUEIB*LbXNQn10hm+52EU2DS)aYWtvA?J^e}aRNjDNtv0hN7)CGD{ zmXh1w7BXqVILG>NCPKa-*IV)p9pBT?XratO2^k)F<>p_(2?;`P5OoBNTgA~;sNaO9 zX>gJTlb64(q({p(^+6a)UE;~3)0}#m4A;eb_k7i+DedfW z-jo!h=Fx;0-78^3q4)bvtZkeK4J*+S!(Xk=Kx|5L3CJ)3ngZi*;!_ujy!%)s#B1)T zIM$4>@JH`0SeuFnz9n48bJ6-LQwcWXD89Ibi;X{FgoA_R*5(h=cC z18PW-qJ~@GJ`|3$cIJrHaPS&(WD5N?I_lHmPu=(OW ziP}y@%0N9KAC)DpWi(yCzivqE$)aGdWp=nngc3Ah$KnKwBtW&BaMSb8qNR();{En9 zPgzS~vsXup;56bgw#Jo~LR);LCx20#0oopL^|3!172Y(dYbt#nfKf(m8y$P^Lrtq; zDbuc%*r9Cl3uSEasZAOe<27MJ>4wi*%R77m>0Z!GVHu$@v(EGQV4os<@cfvf*M-WP z!xjV1>d2fHj!biS7Or5ZKv}I~*W}}u$F_WqBKfbT&^UYY_u$j3sq8xz@Y-CMC~v$U zp{?qAuAYBE)aqTzzo_P*FQt7xt7y_9lm#;5Y;`vd3)tSXJ+ec`-G^MYb2Py7a7jXP zT_B5eC{f339+-rRdNyUTm$14ESf2UfazX%2pQmF7bkMca<}KOqh`pn`PxzR`xU~ak zt3~5JHZXV17A!XJh2zC_d(7jzG23+6HMzU6LmWzCUTaC{0xm`h?unQndIZM{8wbfE zdN(v(#D(m0xf`2ZEu(02;)Xm5>HK8vy!Axoti25b^u&w&!M(e8kX0Lj6Rnxv7Mwu* z7_kO33&C~IIix%{EjD7fFA83fG<69coPdICN8%P_z~slTnEXFVJk!n-b{mthm4v|` z68CyH$nid0UNI2&1f0UT5gEIshAy^Z+>**_Es>X^$D|V!NV&W<_%P5!+Fe+=F3>(E zY6TnM*f^&>&0gzZM%$75m3kCl%|if<;iv07R`|GZ*Y+=TfAbF3G2VW{zv(quN*D;> z(Rpt*IsI5D%53!p2P7%D*-#UwaW_2@~<(S*)vum_`8;Z-&04F*(5-r{YpOh{vmIp`LdTM4y zL9|{bPqcG>^rlsBIHl#HH$E%OBfS4C)QDdw&c42^{dXf$HNgaQzFv9QjMcCRG;ETD z`pgkx*mfZzr?Ui+Bp|fFED}r&>?dTtHj)oL9o79%X3&(Ndo6{4<9W=CysNwz@?)7 zf&A6bJmsI!RyE=J^Hk!$v@N-#XLlo~h8aiE;kGxt)5qA7+1N2HTfk@`Td3%a)LTeb zh9C@A((0_uWPfr2&nx11KZKVzSAs=E$i!B!A` zJB`fV!Wd$`L?@f|`CDzNlRo(7;MO<=N3AJs$B6q26>NqA!WNXzoMcxI=WVobrG4VL z$AuOrvJAQaYfHK{JaAOw(_y{gQ}-r~@Qh6%$}H}&@?y@S%)Q+rcto+Y2qS(uWi4mM zWj<|I$q6aB=dJOmP4ce?Gbh%|1SysvP`5QEoFw$-oFJV^?S)NYWQFE5*mu&W6vjHD zmyh*!H`E#BtBU)R*(VsEx~6%l=IB<`KY?VM77D6YAZ~n~3IOG8eOx=W6y(@I5pUF*y; zhfRBnab3|cyCP4X@Ni;8ayF@6<(;D`wMx@MjK8?142V}9%B7M<{b62AerWBFt>^0N zv?t4I#vB)91n6NeuQN6*<~NL@U8%@+8inO)%GK9WTKx^JngYg1;j=Ky*Y`mR zIzwH5AF6R!_xgSx+JUSYLNSt+Fzo!HiIz6ELy?f+oJ}Wi>H8~CJkc@q#FM^37&m1bXu_~(&w%t%pqnkiIC&6|#3}0p-FeMlvKtEZ^KXy6$>pZz> z5D*Hz_}I_QT{p4M_&R&wY37F`J{DVDJB#n8=dgUkE_G9d3=-_07Ao$T7}<7ZGeIJ&q&Ar z?Qa03tKm5q3N7W2%X$jo?wzHq;l{ag+-V{$uVpLZX64ZHO4d{G{ubi$CO!hme8t0F zND04ZqQ@r1lJ!k99pkA^2JB5H--b`^Zv@sJ?B)07hhlqzAg8V@QFWK&G^9XKzL(@? z!0>$QE^w{8vzyx0egPeJy4|POyi^2FQ~*XJtw`9mp#?hNtx#G~#UJIA&*qx-H!^oW z7BRkqoN;TvoWJgTbJ~Q*Q*(Mb^H~Yfsld4tcr|}2cQoaI@^0ZT8K-bwCUwfI#hUPb z)&};G78OBO2idW=(*Qvx=*;#YTVzmvfa-bzSqrB2w$(dla~)TxF^4?}({6`cz}M-x zlz5}I&0%3T5T|5Bm+dSH5*&W)q{j*xjD$h-Gi@c1(B0Y)VUcZ%SV4E|fuRU}rlxLp zuAI!&vgmgke*RyOq~PS)W5O7k1{t5YuPhk3vrOAa_>!_QQgym?+?ct_+^33Q;A~%g zJ=050&^4~Vd-A<3f}Mi*&T4UL-QRF1IH|xBoyT+KW&D%4y4H7jtFI;u`wnUCrscTa zc)CpDu#oy`^tSl5Ko$fQze9=8x3vJGpElUSSL(D^W7--Si|Ut=u#xqGFEiRyC%xMo zF<0O3-DYcJE?wncjU~SIEWwK90GJLUwGWCzqc!ojJIs11bd7wK?C!Du^f5)p<<9eK zE<(gyH%jZ~Quzxi>h*6`eWxgKhvP?|zJ6Au6>AQCiyRZ=*tcUZ$DnY_kM}>>1>KZ* zckMb?jZGsfx#Nhb7L2o@*O~Erx!@6sSG=qOjyRo&o1gW*UdBDl#)dD3fR{T7OA-gm z6;jiKT_Zib_3p5ttZ1ZFL7F+?!mF7CG4X4t>@^<&7~cFA004=2a&no_1K^)wW|h=5>KvZ}Qm0}-jP{v0*i{lm z`ucN^pZ-}I%?IoQ(zZGx@>P-(c0$Y?J!>BP&jqx1pDWUO1opj2wlQUZEd)%r@=H)v zA16K!_fD1U`goZP?Wu7**kMFN)zOK%0Z+ zSj4cdljB?D;~K>F)U6hbEI0FeGcAbpP$=QK4@_FfXcb^U*8(kGB#ll1&hLdH&HHf%{eb%#8|2Az&xD44)3d^BO2ZO{9 z0zqpM*gWC28%bxW=pI8tA>DnZzCWC9b=4+yzOTh272W4Y*=NqkAp_)Xt^u$gvR6y2RKb`>H%ndLwg<;t?E@Jd(`}l;8Bu-ve;{Y!Hr-pNj+%7hsfgl3(n8U zI7%*52*uwtL`QTl0-gyrMr|7V#bo_G6xn3Yx>x_~4Ldv()LkLaKwNDT?eBTg6p$7< z{Gry><@hi)`kJNjXww3o6Ehw$f#w)g9aGw(rvT>1wUUcHyMX%b1-07P>20rGQE@-R&cvYQd)@-rVf>jw zLxki&63KjeiwG#fo?!5b>a1D}_`t3l;tl<<*M)4zy!?y5)qSl>jEWCSP}sDfNw4ee z@V=aS%j4s0gdX2adRjPjSG|m}xL$Dl0f%C*$nn}Wa9NdgrB=-2K>y@QeqJJ9_*&ID;qnDw+9uCaIVL9reo0U&6O;!V|peV6U zk?(Z|Mh6^o+YrDcs)}xhs}?S8<|oS>%oUCr`?3$dI*thSV|}Xen6QOdE7X>Da01e~ zlMZKuW9IJr9ReEf+BAgqQgRQ+vSIpj&*Xq8%sq$+;%uFjCKrnjtfj|0e`<5{GX1+! zA~4rtRw5-3jqwa@MlC`;j&vRg1pmqv>gH*oDTum$L+ zJnId08XL2oSgk6elMtOddU`H^R`sKcQ8d4}>VKaR)txHS2u$apbl5<1&>d0B3OWTE zdE9OZC)ee~TzQtFb`Xt>FM|;GqS5m@-fZOQ3s<^IAwWur5fKG7?%OvC zAue8={#YT52{CviBIIYdT_yT!>00QijgL3z`T|E~0tlz{G9o&J2YDfp+sqT&5InmtWcWMODlfR4u9E;ud4 zMW#U8n4+}E=37t6FJ{>jP6rcu)t8<4Vj@%G@d52D2CDox9GD1wl^{4=_z#5%hAuuk zI5tgcEq*)xzzO56o}QqI8$h2y-RJ5_q(b-}qc5$u>g$0BlSwnEDdi6cD!TS6V7R^8BaXy4%x~5lb1w{jL6zFaSd?{JSWNNTMLD zYI=w3Xr@Z0n0COTI>>nzELF3a=U0+XmpIiBq>ozV*Z6u==sac{EStVr?MueX{Fgm~ zrANnf+g&_qy2%cMpp1X=dp9cf-?5zxr=s9<7EI*^Pd5=0h#8BT5~|h`zb9CiV{T6& z-R{YbbkkjAuwc#7Z9w^Br()20d-{#$h5PUE@kBDn_n*KaHHN)B$@Hw(XK@CEY4`=s zuSvgZk7L}L8%rGL{YQq1C_js9mqu#aAit-)kUj(}wiySG?s3FOh;V3C4nSM2?q zjGAn&57C8<$2@Cu0tl#sBs%C62ge_gmvT#9{| zvnZ|=&RU-?aF!qHPj5CUFy;;QEzmp(q|Ie+(UwGr({m$tkCkA?nWGXtR>F`+-N8q` ztR#Iy6`O|rJ&F0{nohxj)G!301%`?;B_hins$9?AfQb86NxCFz1T=LU?rS|PF$^xH zkJ(&UXjGaH7n+`Kmams%ZnUL9r!l@f+KF56`s441CDig8GtXObOC1Io+k|OvQyIU> za@0iZE6}*V8~qE!aWE2_v+LSI+P&bNAGo!ze`y=87l#PjV&qW`QMU)k0DOu@dblV!NGfKz5&u%e>uSXYnHRU3 zkhWJ{0e3xu>Ag!*@xQMo9^L81sW zgr}9dmjKaA;MwYZ;e@J|6KFuX3JZLGz?DRN7^|f_|BN^wgmj|de5dO{m~r6MyDa?? z3>dNkjdyT+B*#w^`VGw#JjNi);;E)a^;17D+k=SHaulAiyE2flg}O9f$q6CCJ^i5* zn!k`AC@W%g2mMqol!KVm=oX;Ux9#|dkf@fH+o&}Xb~kl0Zvz%!jB2{@OlOJU_coaY zN?RmB%EI0%DGv1sXWwqk)@LgDjI0Kdv=?SJb%V#(30w0ZAVME?Leg&hIU7aq++ z`+lwcWk4q2_+7k6uH=8=xtz$&iN0g~#MA$mm3vx8{i( z)#UF4K_w@+s1SqE;1cs6dgvc!OR@2N&0h~UF&p-l+2%&5yw{Kxm3Bm3L-@!c&wvi# zS_#!B#c89Xt3)v9M(xrnD=B98?A_4e2A361R9P-V7U;+INzx!kSIM>wqrVX8FBCEY z`)}W2$2Z_LMd1%c*1PW*cO4BP8s^}kor}~!SKhG7cpS$lx22a3Ar45-k3p^xfq`EA z2dz}+pEMVUdswPU@{ngz`S__)6hP;F5ju}iB2sLP;L-NC5Zu!U-p@8I>r=w_=fOHL zLl*PzuT^?FEVJMzISE;4zd7DED9n7zGCa)F5Bz*zn#`cfIGCAQshs_<;y&$v}h7bb3)rBq~gef7?J42r-d~rY{PvqNBk3lQ<>B zC>Y)#1RO3|F_T2MFgj!h{b$bl`f%SH{D0Ry7hR)l4hU@vL|jLr`;My+q|k&u*E-XPbZ<_0SK6Wn0Q^b*C3xw3N3y&$9vcPI{dQ-N`nG;q$iKMRA?K(?xx znkf2QGnz5|hhRa_dZlNa7gD{Vu?AN@RQ$dz1lsz;SftE%f%`cC&T;)u3kVC^*0?!$ zw9O(UPCe65AbX`?7@JqEm_PiSm&dbik;w69NNIldZEw@^j2t%$m;kti+b61`KQ`hM z(-*V2acfMgUVSmN55Of{l=+`{aMNfvxe5WdVMbdyUR($g8!;bvk%fSAfY2JJi35lE z3UOp3^*iq&F^HHlz-T+{Nztp%%*d>nNMUWx$3WH#Prr&M&Wx$v@huDHtOX-T{c2GB z4Rf}`*Nu!chGBP57!w+Nd#``Iz0mK`VhmQaLYs%w^xZ|D4j|xD$Pq+WQvf$zy&&a2 z!hv~?NA+wYfJa>?lm?Rxx4*a$b#V)|gt^G&W+R9K!&+Z&4`>)uHeA8*aO8qWdor}| zju(Z*!=HS#Q_YVnzR8y;5WquN36YkBfX;UhdZqE1dZrBTLb39!n}&E} zwlYlO>|GND5Y6r^tmbUgxRfWarUF4wf}qs23$f7~`mJ-ZwS9>b%Fp(cpZK8O6#|7!xfk#BkEOi@C>B)j6IN_X>-kxlBC_N^Z-Tb~XEU#`^nZ+UrocjQ%LXCV!L9 zLi}xrY?!8X$;$J$IW5t!Tu9UPx%bLpgNg4y_uc8bfRC1wFsL_n@IUA3q5hZHu=@(`^4huw z;9)1ZW|m~Pz9M1}4c@|q_)0S>HiUJYnn*ggMEp-EW7SV(nhAYGkGWQ1q;^cCrC-Iv ze(&X3k0XB^jHq&+q7-Ps=Ik!lA72SEKd9Ya+&$Ft(hH8O^zfmTCQkMb$|-K_c*QN$ za}lDeU_XP?>uP7s)an%y;f|8Z^YO8E0ecbyQlle@jin+h#533zakYq#q}_WSZH${^ ziuU(`+eBpVpg#$uXZPo@M?sb{^Rn1a<)vauMkSILR>C|jj=|$>gT%ujXo+U+WIG)3 zITaR$0$~+h2T2&DeIJ5&QpKOMEcN@oUQg;gG8~nQw>F2=8QZd)Hd5WnJ8cy*z^wpe z4E5)VRSPvrfW`M^ngU{Ig@!7S9Eaex50iu2qBQ55lY?#eDS?9um9??IycT zPI6x^woQL8!K2Qoe7Q;l;y?x-_1?_Ws5#C!lOzVlcg5|d5UkqUOY33$4Tbw;P;SCl zEEl@SA{u=v=nz+B*hf^}r4dUHX#Ay^ta@X%$kIcX3%zvwv#fz>YV2$noor}Jzyfzt zT|pNbxE{k3oBwFms(wkSF@OsRNjK4{lMPEUWlx>kOb%D1l>XsUi3E9W_t8 z)|itm1I#9)q5E2|EDo2XuTWxZ?=-yp7+T5JL1|<$BoGC3U5)^H8LcoMcx(y8`z1{! zFkkEY%a9Ne@y_$8^hY@gx-r$p(lzqfBU@LEmbY>W>#^+kt%SG0uJHF;H|bxdi<7QL zQdsyQT?nVvKo!TfZ9%G0L`dOxNu!@2SZPPb7;Zdw*7B1+Z$!ulf*4PzPcTv12k&j= zN)qghuh&=>dn)XwA&{G)01AWcB|9J9L2UNXIGGp&C<)KZnSkjbgz0ptnc(zk-lo+U zZF%Nz%d=}6*E(^`A(0m_-95=+eTBs8t3~b!^v5L`X^Qo|)4+DNWD#*yI zIA+<_wLvG67S`i@tukcoTy}=@65GdcMDpAc8#X3EkNCDbE{K3d zq<@$E*2w7y>q0t8%ElM}29lex`_hsg%7hsykvyON9;R%=iY$Ik$e&A>izq7^$=p-78qM7 zvb~Yc1JT`ZBQql#cQ!X_g5>H77jH+n!EA+#@ad}Zw8?MtUF^3#1VF(eyO}GcnZmnv z2MJ_qT2KQLu-GKo%D-EeWJcTIH(+v`UBW!_ksI+v9uYkh%spJ8_kD5rO!gNyV8L+a ztB2rh1Dl-o&K~fhB_R-G|B-zR`DNVkD$;F#QgdcVZ`Tagy(gT;)cGhh@-<1i$ivVg zSo(R`O5AK1-Jvg2FKIG*5kA@qlBBt|7K#!WP00TUG{A+eG_3O-YW*Oi`q-=%e<46T zv?3Nd*31<1N^`*+Nbwy?TE240HgT%WJ;z$hgcDP|$LY5jX zR7JDdpQKh>e%C^cser?dMdqlWrD*w zn@w~9@GzgNqktXn_(@qmF?J)QRd!#W4V7kD>utUMZ=f)Si?bSxm8WC=PU++oqEoC$)9YNyP%`EoW zpm0qyTfDNyfV{6E46bmv4{jrO6vWdm8ryO*AR+%P)0HXi=)aa8FaaBnae+Q_YTHO@ z>?I3*+6Zv`(d3Oy-h`KUmBY>uh=!q2CbJcWrvqT4)hE7^T4GvxfmuvJEa^8K(i z#Uo#DGnwjp0_}a5u zy7v7#g42R2qHF&fQA4?V7OA9F$`pSC3}2@rC$<*UZvVR9u>GV7!~XZP&QWGC^@sEU zu5?)Kl;+K=K?w56O%6BtKYkLsl`V>r6o?|yyzieU(E6)NnX6~~k0KGd&9gr?utjE@ zHzO8NG3;zFEGJ`vd%5Ns-r*&b=$=wzF)Sf&%s-QH5zlHu_p$J0X~B=7Zl2E$5*buJpIYve_l-uhd@W91C4K?Mcq$y zGwim+Xj+Fi*b??#Axds3c604MYTY_^n5x5IIP&d?fIj)bAJ@)*g6jFZfuBP60mCq` zi9*P!^8|T=J8A33Y?77_6IBpIx--ZVJRb_f$l@5-AsM+r`}1RAYKK_5fcIDSSr;!_ zfZ+Kqhq)7LUOJ|6mVIEvCoKRQnA~UirJc{*A%Fotm9o#HkMi$rvgQ0x+Q`vrj)A%AQ zXU!&rn~l=;F9C$zk+0zl&=ROMRXz!P)#rveD*YmC--VbOyj2KUG?I7w5{RoD53G>P z#n>FinBBG@Wq418r*Lz#`uEL;u(6J_Jq*7#a7C@vopJR8WEQHecS#VnY}#4s*A+9Y zs5rZ#T~=6+9qY+~6IH*N!K@yh9nR8M`wq_G335d)zu8=uzH#R&5ET5R$ueO~8R;%b z=URyfRdX;XF6$!YJ8#YL?@y_vPxbAv?~g72LT{Y_SFh-}Q<;~Q0h|nT zWg(yVjrK%yZ&MT`t<&js#0d602d z{VVahq7=j{<`>cHPFlpzo=0Nm8wpVaaXY)N5Fvrdu!OX-2r~m=Z1v0j)LE=*1Qv zxdC4JzU{}!Rd(wumV=hBvwc_LBH47={061|Zph{d{B?ArWA>B}$pC7&W9Q1I_c7Al z_^OdiD(ssN;Nf+iNJYBn{Y~S>M;mR5CFSs6f)7e+Sh6B=%dR-~`QK&4*_jiKi7%Nx z+?|Mv_@8(pr0v@!8Z`;C)RNtfb(vt#2A7>2despr@d}Ms+@QRq){QNTU_XY58F)UE z2OY*y5!>uwTj%LibB-&Ssd}sxue@x+mJNgrOIj(#fG?u3u9nBmal})L&GWq-m!gkr zXKw1s<1_c~V@6m2c&`%f%#3>b@x3)P3zh#_?%?}|Zkr}Mp7BaH0jcCjDC4@&*l#)% zDcGd@=&965=kb^o)pI?(0gr$be9bWR(Zi#9^L7$z)-8qh2Hn}iHii{E2z3@2TH1fR zyOt&mfliFrYS!)pr?QBJbW8rW&%un~GNMhDx7@ z+A~b&+Vi(iLrq1`>TP%i>TEA(wS0vv1qL#v2eX1y zT$~nt!RuzMJgLaH=utc*8O^2#rTi|rO5_Lo`(-;#zLJ^Bsm zh4MoW9x7@TjN2>>f>)9ht$r6jZzttS_eC1M&z-*D&5HILfwB=S@YCa%%2r_u@%!|9 z4rXa_lFy$hA_l{v?z3$+v3u$S@`KAjTVyBl@L~1Dc@5l#U;guKnl~lr8O~%vbCpV; z?&6Vhs+hpP+A21gNYx#q>Lt63ST=X9If*k>Srjasrf!NaawE^b8SMO-a9m$Dc^;~j z$b|~Bu-|m1q!+4ciwzWp1eWIC*D&muOgYvY=;rBB`k1u0PY&TECBDv?9^I^LNDj;J zgll}ck1eB#gIIy3hiG<}VdP?ahYP&3+vP99|9^hLUJYF(@q>*NnEjtL&40*FedSNy zejcX$6&A;e`QylA!aDu_wT{J?M0EnNH$99#d71h)U1SqP}`PM|M(C3`5@-E zf2d?wsc0c)n3POuO9#40X6(6tUMD)zwk1+y_>6eHB;fHm{Ru7dK~PiYr%e+x79Q1Z zNtY+}W@!2&6pQ0~RQL-2B%)CCH!OZg2##bC&DI%A|5IUUWiB%$=x6TJo3{=ak&;aP zU4^4W*Ur48a4SLGZ+BP5^lZP5w}O3JL*%{(QmNJCIvRIrUeRaOL2%p0dtb(h8}ZAa zT)^U|53u{t26x#hPkf^8eR7j@0V_ZY{hjD_4qNmxq4zXkAYzDDzjcTP!~Sq^uKAEp z!y{#!w)hyZ3ml{lVEwI?Gx3rF2T1g8*ju%Z@gN#(sZ=$D2aL#4-ut$BOpVy8eRV!m z2udbCco~U20-L9bAESJ@aHN?@Vtzx>Z0LE-0HaXX)CG~A2)fONEXJ3%|j9RH-BM9CeJd#G%oyKyc3Xfws_woE5gH$j{`k|3PCls z0QHOqt}M~F^_}Nj7F8VvsZ!rk0cysX$)FQ~bHttJ=>F&0W6`bmcYIVkH5! zk+xRXr(+@v0vKx_Z7bV=5%bh;>Pt$KwCy)TwGbF@uvDo%!#Mjjh3Z|9RBZFkhX+z& zd$Epv$4^Tl|BeeA%Sxrooh6K>qs7BXz=vSU8txDIPR&yM#(8x}9^327yU7OfxEbN^R z>`~^;-547v&qR$R5fW$BgGF23o$y;E<5v#h7nLRYP9{8L>9dLnFL1rS4{5bXAgdhw z%fQq7Ww1*m$&h!#xM<&xGpJ=gd4lMrukvaA89N{6@zwna)rPu~V^RKX>Jku>*5s`SvB#6&D(2!IdUG$$dFDwnehEmo z^R^N7^f?)S0u`2VK+=ZU#_~4=plZ7AXq-o`q_1@)ui6SYWHn0J77&@cjymn>H9+YF zy%hK)U`L;W>fVpbSs3EltdB9I2DFecR@IEBNLXL=8(%WeLt=9c8E+i?!KDfuh3d>( zU)ukzez(Hb=UP!Ab!&i@4(C3(O!((jWIg_%5XFm;ct{(vDedn$W#oi$#q^Dko3+)I+a5GPLtc1?P0_ zeRU9AKm!%Gx}|uXtmmkK)bjJO0H>|$IB_xvnlLR5?YL%$y{#8+?8-|N1}VB??)OqV z0ZOBOAWK&Gf=&~fM?fj7j+X@vPmA<+Ci)&W~&j}Ap={!j9SgJqA24;oZTxU-w9B- zhONjk>TL4<^(_koU^;qH*H_?s1`6pr=KiW8rO3%zA+#$%T=V+=n#pnt$xS(uQ{wNx z-6$HGEi@yS_pG$^{fcIs`&$rVrZY)m^&LKA*L3AR5avd;2+5-E*%ehB3l=W_^g#bc z5Qh8*jy6U`q#0vcL#1($98^Vb*}L%8%~M42YhGp{d7==o@(>RY5(-|%eqmI;>V12n z(IFSu1lHOmrm!ni)%%ZMw|#(Y#Q*p&_yJ`52F2oP*D3j@b^T zHdVH?exu$_=<1|(ET}z2(;`XwtZ(4By@OFYRQmumvEbH)bwqSb4g-JL!6)$m50Z1! z?)@t%yEx(3%WE?}&@eF_3yPDlovK#he-CA-1oD3y-Hl`i8?NWB)^iYhp-bIzvyz`h zJdel?E^7!?0$-foN3Cgc&g0)}hmXUFOq zA~pj&2Lk8pQDTHw2Fab)zev^~Z__$dW=6vf%u3QFAsc z2J-WAbJOqsE#SvIy;^2n+z+KXsfNld=D3^LSB?5?!WDV+yiMWogE{Rj=VvPl#Z@nP zW+;u}Sq$?X^!!30Wsh}O%Zd+@E|-1RSp*0Aid{kvOS{%c^x6Jh9PL8dB@@#nZ`Y1Y zMD`mmmf+P^=ujjaMnUVD%-=M^qlZ=xE?iGh_baa%-E@IY;$r_bjJ!0yAJU~OKYRN? zvc!4xUKjGP$%Aqd-akPP9hOW@p_*bnHZJ?;kbte&E z+DsILbbOEW^~MYXhkJluq~zs)3fVH7@j=tbf0mP!V~s#jI2AlO8!CP81^`+6QB&>J z`H10LMvemvk~!l@o4GGnFO~ZdV7fG&?=kHIBfDz#Xh=`mm`+x&DV@Yc|Jr_^9cBau z4qg^!6cF*ykEuH~r6RByra-kor0oMEu}w*R5~z)Fs@~RPW}w$zVs^9W-gT38Ma(Os zAl#a|)>NT|_7j_5*y1R-n^MFakCQKc{O{y*bIl+Jcyx_kfoKb2ynz3uu(RU38I*0sGiPOTPrpcN*b}wd}crEbR z9u)2n2oN-+AV=tP)ZXCoyjKAi{gWg-Tj$Fh0d`^tSba`WPA>r?O`VUKw|4CRpIF>4R-4P#Ut{@mN)@LsALXL7PMuU-iPISZ1d;$l&AW`{Jub4!{ z-JZ;Y8KyZo;6Hf(3DX#XdzL7MHkDRf8vivdLJ++^+Tp4Mb$$oi)Rde9;^boR;LRbE zeD=*$T#U5ra`>%VItuLHenc(RKu)~`8S`f}wdc9qeo7NU&bk*BFsG(3W1p-(Ii^}S zfxJfkb-Dw^p!v_Zc-_GS%YiL%blLsW`ytpV?Vb~a`OKKPr5I zu*yNMb$1K?Rbs<}&P<0#31T%2L6gu!jsm~p@mQY~;w_v%W)8^mFLUm8e3@}Wv+b}l zNuV%?=QdplM^j}oay;n#kgpOdYg^0lI5P$%FLQ}ME*I`pJZXx=;{Xr*mVX|Jz|Di; zTNJpH4!e;P-NySc%QD zAlJ=TVv8&6*0`7Ws#BY#ld!Jtezc^?Zj#cxk2$CIrKh?o7R1665h&0<1gnD zxkRB0-WYAI5Zl#I|NKk9CHiN|o|(qVfAVYFkOS$@F1a%S8YfIBv)(-1>l+=7r`cxD zsDG6B;s{pjFM?UlwbuKHbWX*F{{u}xvcK`N>>|rTXzoCHM1mSrf3+moMtW)h!#g%F z9*@qBj!hCfPm;ZJzU* zW$aLZ9cY$0%zkhOfArh(+otlO6G}WcD>P}gLxr%YZ$q)xxMPL9F7?3||7$ARb>Lti zmblc)&asK%5YO>wf2Jf@3aKDy!z$zyxHa1;5q}bFJwx4l>d-DUzf0tvTpl!P06Jd%7hgq5X z%(x=p`HvkwBhGt)O}(bYlwAuUwV0UZqK1+=Oh;NTDZ$5xo#LkZS2{MX9Lh}()qxXg z5yMAA++kv5r2Xniz#TG zPKUs)eCqX;gblr%Z~4_KPhH?bIW_&Tk~P%2D`BdzE7Uf;HL9m5An%mgt4L!4)UbY< zbz%zzN=2&u9Tsj-W!!Ds|HKQf+LZiPzSjxD&36aVe|!4kGiSy4Y$XFU`ZnXZzfvC# zW?0e(q}#zv_HpTh6i?{6IF~av-~$^uQ%3DH1Kc}*F5VG?&`9N|2U(W~yPo7GCR_+Dn3S3zy`IHgj6Nt8@uljwU9#IQ((AqJ`n=FB5t? z{L#YWfAXSnt3S9eC8&ChhNIWpPh+nWm0?#ZAAfrt1-*2#YfrwjL*^eh-!FV~$Ar#H zdjFQ4U~Wo3EX~6Qu6rKVTPg{JU$aBM_@|^Wn%hzE_i_*v5o(t&Ob5bNiE#pVq6G}- z{`pBU4Z`ZfN^b>KJdy2pTh98B804gW>>gP&e}nf-H3!)QTrntgZdtV~5FT820nH^F zyme-87T*&`poNv5-l=K_IzPZD@Jyj5&;Fng?9t~aExZ#A+@5aq ze{(xkaB45+G!+q`DEU^5kc}R!avlA2S>Fy=#_lnE4A(`)Z*-3Z`nhp2Vcs>t!2~>Z zP{c1E2>^!3Uo-nH*%fk?H7M+jt9Vk;l z=ZMbV1$P{=mGY!!s)qnJuUEIrlZaSpf93Y}fUgVO>X@wX+2IOVQ@O*M+|IBsk;<4W ztAI)-Ym!I0t+99QJ7@6Y07$l5NU?6`0W$pwtD0zMe5}*^P*#A1GyMY3Zv64Zk!=So z+j{#FxJYO&%f=&PGw)AQ! zL)y(*hCGTBE{|z&k~$nwv-nO$MV|xyS6ts~Jnsf2S~^SpZWieDM`qnMi-;QSuqv{f zgv3_DjXo=HCfxfBf5S@l8ZR6X-iR zZWo>R#c#PJe%We!On zO(^M!de_`;2wvv~%nH6Z;&ALGea?MUc#-?%?LYBgxF4#aAhqC%4$ATqA&jxOm1L*9 zMP5caT5d2$3oO6%i3_DO%BSg=dCeKSTz zbD73F5&?MQrjw!3?M?1;vdw-~9fY4f9w}Q$o1m3b)|S|lZuq_Oq}PijJGeVrd|gjv zQ$IG_qMqq-!`tn6nSuGSp=<&_!pG9$4oD{Jo+e`7rJ@_Icv&=>Z5Mr3Mi*9M!Yr{aejqhVme zDvzJ6kFJ-fAs(<5=JzCDo8>S;_P#!o6;=z#i5&F29-@FZr*|A8U9^U!KfnCD;$881 z_kI0!+gP}EW{f;Le`kkxx0i;LoihPG(?`jpy&f2_aqo{Imp#0rcIDmK9)iJ272W<_ zHYgPxS}L%49xE{=r;TquaILJ{C0tJ*YuE(ElvxZ>uTAmpfqD~Mbm=_pK5P&EdtJND z4*Oth0{M8Tc_1=5Z@=>@+8PE!G~e1kl!o*B1Qd1zv0!H1e{DJ=%P91E$6&Pgyc0IO z$Pi*56Nl~goH0`zZkTGOp8ISI0acd^#I8!af*o2rk5AfSbY91MGxuOT0U8Zj10+nX znpoP&7J!f9QJVj~1Nv@LXXW_dhXsrGul`7|fqiY+zjve(ajH_o<-(XN%4QCEUyLz9 z50)YNU(Q5`e_xEKdueV9cYhYXG=Aj^)Z-7F8=skj-;Uq+9slT{S{e$6G?Ov%*u|rZ zJ`&*Du~phbnFO!xDy81VMxyq^19o#9{ut!Wmt!I93m^aUx=^O91LJX}8p`^Hc<}3w z{7=%J82R?VU5<)K2uj-dK1xCY>`3Hd_UpcIovBzze*oN2eE14$Un>QAg-kgbV^ko2 zH+|W~$C2T*A-L$VT#gni8mjIhGWH+_uM_ndf5K#p}~kd72~Af zLBm=)R*+=REsyx4|4{YWf$RQQk)1PM%@cs+e=j0wPHFylFY(dZi&%Y>k<#Cw>+*xj z?7|i&J$sD*cQv1MMhklX)(j2%xr5q=>}qvuE6hnKWyyJ9huP|bWrFvE(Yt?nCTNcV zU1i)*)eNw1-}+Y~5u89XOCWcfRMJFJ6y08a@3=AJub-63VGj ze=zj5>nleA3A!%VowzgOjKux5KM99?fO+Y%*l$Zm=+*JiF123L%f2r5t=uAq10D|6Z@0xVg>SzlOPw&0A~uTm=Vqlc2y{fwZFU4-uA)AqM&3AmJmwvQdx-x~oKZ=NJQH z_g;TbHHe2QTOnP`fWx3wX~ZFZL32mkomKn zO|M74xgUnXe%6>cFz}>I6elJ^=x%F!&xvU8RcqBYlBU7Z>DFr1a2o8Rf6ge984vT4 zT;lPLcvT1M{YX8zGQ6T;IfTEg5>vClWp%3Im}oWs!_C zaj+{&rS_$lKRow5m!57P2L~GDIRq@Cfoa=?|K6O9hgIe9S^a{ouiiPMQtZ2y!)e`X>AZmiR7Q%#A7vYzYNX~$xr%5VOvZ!8ULj5gj|wS>TA ze0_J7bu>u%#Xd3+jD-`K-~6xkCqd+g+<_;Wk>JQ3rp0Y>M;AvOK2qR|# zyf>(4T-}}yBC2vv4aU=;fu3Q(mp%kEDlGDzBt(PrR8X#}L=?>If7}%l$QKVNBN?Xu zVRJpNq5RWx8vK#_MIXQEQ>%mboY|P;p@}|&{!0rLG;4h|zRM-UMAvkwvKS4H-txSp z)0qM!u^1**zEC)y@j4^&OftNi+_|FR91e|Vj>ge4C~#OgJjvp50!Y`-M7NeDLR?>& zb(j@@c3B2LWbX$Ry?b@Qa~tzSn_p!Qy;H`#)-j6@Lynmpj`?DvJbJps^EzQ zhuR3@HBl<4i_ElMA^L+`_z%H-#4s?gr!O7Lp}}R{1iGZ}e^GGkcxr9(3QCt$n&rfq`GiZX6wP@XWr$yz03xIPni! zp4^fIv&>s{o_dGF+0<=iyG*0NUAR#$YqJkePQEWtFo^`Ko|(ddS|6y*5}W4BjRWE( zt8Bq1VbJ5re>o<9F&4yZwV3~mN5j5Xt2b>Ili|c&-iLc$(_p4$_X*qh6o_Ze4bAC} zhdRE26sxLOXd7zLYq}W?*KV3mJm61-rv69SAs(@yeP}!P>6}zx-b=rxFdhz57q-?J zb%latP#V)QyKvayENs_(KN(W0Ols9aC_pM*PzYU4e}y55S`7A0``OOV+F%UG{WzFkE0nt%e(?23Ha4h3<7_XZz z{82D3S3VmJPS4eU)%{L|NX5S&_T7qu0$bBxdoNJnPiMp)3vx2F8<5TlNmHQuVm{k3 z)_7>-8r9%1Aww?9oXzZJ-{)q&9OYG~e*yEZ-}^Kp55w?{k7HjIDKOpg;H{i-8Z=eZ zjpwtFq37*WK1Y@$_`$Boy6boxRLK^K*(_6`^Tn?hTU%4$`O~%DAENPalR_z$>!re< z8UGh1m9fB3D;K9YPJ#Qr{aG^;G`L%ACCpAG!;pf@z2}*!aF?9Bu+B@{#)2!YaK|7wd3DzI$ii_zQ0z~xA>K3Qqw$eY7+8>MtUDhC3S(SBoxT+K+9JHui53Iw?njUDho*upR}V6M zP6nHW@rM2sDjesKe%H(z1JvoM-g=V+ur;)pvF@h9VbjkX#}{JY`6YwVCwrp6e&?|r zr)oC)Uz>aTl@d5oE{bn&>qZ42FnIRIWW79EzDKYT$ z`-7GI&Hu+Td|r(sB^u~W0to__H|JvK5pI22Ea-P-*nJbTD0d_6yKKUUS z>H|WoQX7+?{!w#F@n+7tFLHN6$|oLFUAbF~+{v(W`SkX%Rz`Wyqsvm;M8_ESKSqbG!2hX#7=|M}H^jR7HcH$Nt?Sm0e8KXB%7B#3!D zF6%m<1e}HMx2pCge*#U9hg0(|6^1tG6IYDMkf0E;c=1&%T+sBEez=)8?j;XtojVl^ zN7O$(?w$>VI_JBIzuFU_u7N+F+aeaOJasMoWFG?$PS_o&2oDBz1AOu#kOa9F&lM`| z$gn82Rj9l!4z{p31^VYl!ajxB9ovK{K)oPDWlkl->t9>Wf0)uyVDz!--6k!6&}8Q` z{P$y%uOD^yG#7+IV}T57rFt0bvMIk?u|R=@bJ}LAYbh{NTK7YeF&Gs0%}VU#qd)_& ziF#>L!0uG!dPqV7jP7gv+4wme+>_W2IlN5(v16W&qI;vjbj10=#$*yy6ST9NxMD#m zRH=~FHwA2}f1a27NF_mRyn;derp~FQSAPp{&H=-f8iDDbDR8+ZLgA=YB0LC!RLATj zNNxRFKF&l1PyNHj{*v*)8`iXq{SpOcGLpK5&cwn7+mp-ezoKF1c7cYS@kyYur5LzJ zvbV;@A;w}`)*^TwjQ6jT$+S_bVD5KU=6L+oEN9wCu6i<)%g>Fez@3l z*TGwegjvgv8$AztVfkZjh87|jrCwgBOWw>I7m{2J|LhG$d+}kHZV`L*_WU=!`XUjv zk_*3cf0dDNu)55z&OHp5;=|%qhJ(Opl+DA<&I7J�KrNwF4IQ>0Ogz>A1V&pKA=G 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

  • 0oau>=Pw_gz}v9_ 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�Oj@s`ain1c5-&VbL;M0&FmU-E%`~OBA@Sa#dfw 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

    fS{~D7GgxuucALOJQF(b>j?$Yyv9X$ z`7&|HFY^9kAQ^w$4rhM2_!lDdV_BxyDl&+V*VkyBuY}vfKA#OuRgimZYy3Ha)z=%U zCE~=%;1p^}$^1>j+kWMq-_B$>nQr*~MTU;O!TctZIjrk%-d%AnrW!{LF9A<)1JLs81c=7(a<8 zHd&t=za2xx0qr}{5tDgD;_3a;+LAPA)VJ}@J*yy64@t>tCD75(UV6o7l0uwR$(}rs z%hLBTb>WN(DiQPPc*~S{EsV6Jw`AU;5F%aqxi3>{u;^1e-kn-b%(O<>WW>=?KA*?C zo3DVFG&_IXpnQ}DO4abbnCdjb=A!O{zA`G5yDFlST<_t>giqPm{WNH$KTq6G`-5|D zm8?z(R)a(DW7(8Q864gnsDAU9#kno_B)M)^L-v&mPslSW-o$qHWF4Z`Wsm*QvO?baP(Y0TX204G3$PZ2%8{wO9E7T*0hVd2pU+(tT zK*2miOMj7uykIA{Y!x!jjI6t}{TLmALLJ6wmN{^?n6tS!%Hrk=yEusxk;KbAYegM& zYvEMF6|AtYi0C?|YF59e9>u42zAL;#B`%AYQ@sQmq2efUAd;(=Xb3+Qze2SM$@WaVP$@A_ zxE{5n*TUtKXXK#4AA;`rVb9rSI!KTAHh;fm0IicpZb_!p;?%ckTRKkx+yn~Ey|>oD zH%L5q*^Y{bSCurAoatD(TF8s-NCUP`y{vzK#^Qde&gTuq586CY}}r|QvU`GmVawH;y0#rYTM z4RBN9oC$o@2$6zSg2umD-@z;PO;wX|5gBz&tD8~jWb8RM{sX3R!p0n8tuPbW6|jH3 zJBQGko_OR(Z$ou$%mJTP3K8i>cD^&z4(r;db@y-85sd1eF+A58aOku>{i~yaFtAwU zS5;s@%IWAI8;^P-Cj9tUVU2dw8Mb`CC`c#vi-fuKQCcxGown=M&I%&o**eQHY7^4_ z-d&aS=nuhL5gjGS^6PH4uLsrc2V;M&ex@zo&qnATn>pmwT7f5Q@86t$QIF9vrPup4 z>*2JO@%GA#I;e~NW1sxi4qEJU@zk$%2wj!5_U@@}Y`dHPNvNV8hIZ8zXEyd@a!qzz zb4(-1;kS8PJqJ)#oPIcEB#V;Nk+E0(pzaHm?P`pm9Fti2x}ImqmuZ zY%1!;A%6MAl9m66%rCRV{ly-Py{Iy>=%*2?f4&n9YQ4C-Cp5WpXFYM8Gyg!?jUG61 zjDEZuQ%4Aj9wwVqcHzY0%!PlqMl|B(cCLA)pB?B^yti1FT}nKxuxsk{Y==&bnM2gZ zG{QF9rO}Jof)N)5gG-0gAU{8uo5F0x+q(OrTrcUk`MuSaGRV^1no{Y+<~A6w(t7^+ zdLx8anRygR_n@eLi;Uv6M(nS*Jg%ocgx-&*G7|HeAUpO{rO9Lzj{kq0ey>t$#o(ry z@zv+Xq2W8ThLCT^AG6I78N>v(Eu8u!(96Kt=i{rA|BQntbBAdA?@r9N9*On&ItuMe z%-maf-Jph_wK#7(2=9|Y%8%sw5SG5YKHjbatx~SaC>TIN;{Mc87Ux^Jp188DAI9CH zaFO8daD1pVvGKGW!C-&NcD=*;MTFf@)`mSABUoJ>^?qbMl{nBX($7pCLam_0M=`Bx z!YlgX$#2pFxLSFT_h?EL@nk~0UrVSLhQ=0eKAPkafmvf9EB;M?A@YCUbRlB5%=_uGW?WhC zpSW>(5OllPJ4YOvVeEZuV6E{Ow61;nQ1Gh-A9a4)gf>Ws(+`G>&)^lG zjlqsA2G$0DY!@hA{?O;v_8XZR&qpH;o{rJC|CPz(gZYNFAHkCCvN_B zWMZ@5T)zWvHle@EFsk6&Ak@8Q)IyGi6KlQCP00uJ;$VMaud?WWS+I86W@0zg2@aVx z($6Y)cVm9r>ZV+y%lMXN#lPFM&VjH zT)}y(1uqIxt#4LNB74`~EBo%W;v*w7!NqtEQjt~Dhgkh4$m=3IV77!Q1y}Q5H#*QY zS}XP_l8t{f&wY6D@563f*pnCIr^rqs3?2UNzt|6ho6lC7v5xz!JwGM4a|C%+hIWD7 zi{Q&KoD@1c23>zoN3|a_IL1nylFJj=#raRi{Q4MnJh;03;oKBnpU878Ki`XqHc{81 z;%P)JURR|IHDY+g#X~`T8sCqp?o51HhKhkbf{A}YlhEIM0-1_g&@}q&zU{y`JQY_L z`j7v_x}~|3TyL33dq<4#lgb6}gGZ*G?1T6-CD%JMQHhmox_*xLSo&x73&tDQL(*f} zU0S~js*<*$>$Ju|{uvOB;UosL7^Aj^kcztdz(57BpME z;@N){HiN>M*rBYgt=JTicA&I!5gjrU6qV671a-+22AZ>zI7g%A_d9n$;7IxFs|K8; zxeL5M|8D7Fty4~HwBRCHXm&D}H?z1XY8$9SaFN#d{?h1I83peVwctCg93+Lzy$rHqBS|0nF6$vN1EmqpCtnQaz%74xCoTHX9M(uzKMa034(lD-A70zcL$U0$ z!b6rW?baO@(TSd8`S@S5`auTv6l9J)@tc90x$mb*hkAJ5`Kx1+G6}U^;x&9BG}vaB zo5sEv!zmrv)aTPQY`olBp*K5>Us76&#h>bNO>xDCOA&oIw`O>s7iT-z)?BD^)a-wT z6t%GZv{^s=|C+hBgfMV#>zdl~Y$j~2#qQqN+XhK>OINPllW^1*idqtE!OO}3%2kUw zC=(KmM|QN}?tfc8DGe?|Uo$v;(6$X#?4#ZD-#JJrdOsiT7VChe-;#bm8#gI;qUc_t zT@SwP@C~ed!b94cs$w*FZ3t5NBcp!>ojfEp*FZvx_51m3GOZ3za+88J*VXl9PhmK$ zpyEa`CrQ$=J8SO49DFod>tB9N{}DG>01lC+E^Tl-MBT__ggo9s%oCN z*fa>ySK%s0lNo58i`+UAJBmH6Umf)3Td_-nlq}IWiOZ2H>Nj>ZgLF&jZDH3OepDTK z+``@rxu{Xi-otF9Gaf7Nq{n}?Lc*w^Nl$>2lqxm8OFEB%=tt$coxa?p7PDp@VgDXP znKX5rwdN(wlE0rlW;G1K`Rytv!9^bI#Bu%QWUe4uP2H`4Q)~m!u%Hz4Hq&Bk*c3OXEaw4CUDb! zxOPFd2j}wqV=NAh;#*jE)Xy(0?!2+y6)-b^h%~W<05zMkWANKSdIzpkqh zIoHIV?|-q3yk`=_21zYAeq=?Zjt2+nhrTyC=r#kHz0DQ3m$*nm%3ZNkR{hq$+PIE; zhKIx-yy9YA<}e(}g*d#{^N}vO>tDBJ@qbn@tnGs&A1P{PXa9fpYcpU3jojts;w6n& zYw|0GEg(7YR9piIK|;qO>k_Z)RO@yr?oZlRb`o%YzA43p21CICg?xjfr!2{#ES}oCK55&8S{8 z2)AmD#Q+xfqlbSPUT3xY!ERRnJYZ@7McfJV`8)&Ibu=^Ds%B zqaA*a3-|Jvjl#StM0itcGe&GA^$rD1z_YwfiEE+(Y`g(<{p1;ZaM-Kxv$6q7(UzGb zc8fTCLqV$X7pot~_(cbIv6HUuO?(xxnt>->bgyH6oFsqB7Cnb%#U3cQoVy#~%}u(T zA5pG&J2j{38UPH#xTkeo~Y$Mfg4)_Y6Fxa+1Z2B#q#p{C*7(EhjW6J;xes1#Plplo9+mM6TeLArv-s_&D;R(3?(d{buC{)>3{~Q%(@-t39lEOPuWQdOYLFas@-^N;<)*S1t-bvrTsID zzk?WfYZtkk%tb0}DLqu9ItIPQq2sp=xk&=Cy#mx@Q~160VbZ1b+$4*zs~etm%;M-5 z@8*BgoLnScz2AMQR~AsWb!VBzJ`Pg3nsW3>wME#Nc+62cm(iuI>QB{Kz{dIJ+f2!M zh(~^TXfiO1ZBJJzZit^kQMKm$?er-;O>RE$sB;V?d)?wQd&V)*BAU!0#l)+Bw?Ewv z9swVd*F79`KyunQ89~+6T?P&bjm6d;A zo_JYCaB`3~b|!EA^Nxwo¬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 delta 22229 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%0AJ|p|B_> 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%0D^gB~@k=iGFj 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 Date: Fri, 31 Jul 2026 20:42:35 +0200 Subject: [PATCH 128/131] route reference tests through public api --- .../methods/STALIGN_DIVERGENCES.md | 6 + .../methods/test_stalign_reference.py | 115 ++++++++++++------ 2 files changed, 87 insertions(+), 34 deletions(-) diff --git a/tests/experimental/methods/STALIGN_DIVERGENCES.md b/tests/experimental/methods/STALIGN_DIVERGENCES.md index 1a608ae70..4a2cf4461 100644 --- a/tests/experimental/methods/STALIGN_DIVERGENCES.md +++ b/tests/experimental/methods/STALIGN_DIVERGENCES.md @@ -15,6 +15,12 @@ Each row has an id. Every `xfail(strict=True)` in the test module cites its row `test_divergences_doc_covers_all_xfails` asserts the citation resolves — so this file cannot silently rot. +Comparisons go through the public API wherever one exists: point transforms via +`StalignResult.transform`, the image warp via `StalignResult.warp_image`, the landmark +initialisation via `fit_stalign_obs(..., niter=0)`. What stays white-box is what has no +public route — the objective and its gradients are not observable from outside, and the +regulariser and grids are preconditions rather than results. + ## Where it stands The port now reproduces the original **to machine precision** on everything that defines diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py index 1b63e39b7..685999144 100644 --- a/tests/experimental/methods/test_stalign_reference.py +++ b/tests/experimental/methods/test_stalign_reference.py @@ -26,6 +26,7 @@ import jax.numpy as jnp # noqa: E402 +from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign_obs # noqa: E402 from squidpy.experimental.methods.align_samples._stalign_impl import _core, _helpers # noqa: E402 from . import _stalign_fixtures as F # noqa: E402 @@ -113,6 +114,32 @@ def velocity_grid(primitives): return jnp.asarray(primitives["xv_upstream_0"]), jnp.asarray(primitives["xv_upstream_1"]) +@pytest.fixture(scope="module") +def fitted(primitives, velocity_grid): + """A :class:`StalignResult` carrying upstream's own fitted deformation. + + Point transforms are compared through the public result object rather than the + internal row-col helper, so what is pinned is what callers actually reach. + """ + return StalignResult( + affine=jnp.asarray(primitives["to_A"]), + velocity=jnp.asarray(primitives["velocity"]), + velocity_grid=velocity_grid, + aligned_points=jnp.zeros((0, 2)), + ) + + +def _transform_rc(result: StalignResult, points_rc, *, direction: str = "forward") -> np.ndarray: + """``result.transform`` on row-col points, for comparison with upstream. + + The public API speaks ``(x, y)`` and the reference speaks row-col, so the flip that + ``transform`` performs internally is undone on both sides here. Anything the flip + itself got wrong would still show up -- it is applied, not bypassed. + """ + got = result.transform(np.asarray(points_rc)[:, ::-1], direction=direction) + return np.asarray(got)[:, ::-1] + + # -------------------------------------------------------------------------------------- # Provenance -- the fixtures have to stay falsifiable # -------------------------------------------------------------------------------------- @@ -215,15 +242,35 @@ def test_transform_grid_backward_matches_upstream(primitives, target_grid, veloc assert error < EXACT -def test_transform_points_forward_matches_upstream(primitives, velocity_grid, record_property): - """``direction='forward'`` vs ``STalign.transform_points_source_to_target``.""" - got = _core.transform_points_row_col( - velocity_grid, - jnp.asarray(primitives["velocity"]), - jnp.asarray(primitives["to_A"]), - jnp.asarray(primitives["points"]), - direction="forward", +def test_warp_image_uses_the_upstream_grid(primitives, source_grid, target_grid, velocity_grid, record_property): + """``StalignResult.warp_image`` vs sampling on *upstream's own* backward grid. + + This is what ``align(by="images", out="images/...")`` materialises, and it had no + reference coverage: the two halves were each compared to upstream separately, but + nothing checked that the public method composes them the way upstream does. Feeding + upstream's ``grid_backward`` through the same interpolation isolates the composition. + """ + source_axes, source_image = source_grid + target_axes, _ = target_grid + result = StalignResult( + affine=jnp.asarray(primitives["to_A"]), + velocity=jnp.asarray(primitives["velocity"]), + velocity_grid=velocity_grid, + aligned_points=jnp.zeros((0, 2)), + query_axes=source_axes, + ref_axes=target_axes, ) + upstream_grid = jnp.asarray(np.moveaxis(np.asarray(primitives["grid_backward"]), -1, 0)) + expected = _core._interp(source_axes, source_image, upstream_grid) + + error = rel(result.warp_image(source_image), expected) + record_property("rel_error", error) + assert error < EXACT + + +def test_transform_points_forward_matches_upstream(fitted, primitives, record_property): + """``StalignResult.transform`` vs ``STalign.transform_points_source_to_target``.""" + got = _transform_rc(fitted, primitives["points"]) error = rel(got, primitives["points_forward"]) record_property("rel_error", error) assert error < EXACT @@ -434,27 +481,15 @@ def test_grid_length_is_stable_against_float_rounding(step): "squidpy ever deliberately adopts upstream's ordering." ), ) -def test_transform_points_backward_matches_upstream(primitives, velocity_grid): - got = _core.transform_points_row_col( - velocity_grid, - jnp.asarray(primitives["velocity"]), - jnp.asarray(primitives["to_A"]), - jnp.asarray(primitives["points"]), - direction="backward", - ) +def test_transform_points_backward_matches_upstream(fitted, primitives): + got = _transform_rc(fitted, primitives["points"], direction="backward") assert rel(got, primitives["points_backward"]) < EXACT -def test_backward_transform_inverts_better(primitives, velocity_grid, record_property): +def test_backward_transform_inverts_better(fitted, primitives, record_property): """D6, stated usefully: squidpy's backward map is the better inverse of the forward one.""" - points = jnp.asarray(primitives["points"]) - roundtrip = _core.transform_points_row_col( - velocity_grid, - jnp.asarray(primitives["velocity"]), - jnp.asarray(primitives["to_A"]), - jnp.asarray(primitives["points_forward"]), - direction="backward", - ) + points = np.asarray(primitives["points"]) + roundtrip = _transform_rc(fitted, primitives["points_forward"], direction="backward") ours = rel(roundtrip, points) theirs = rel(primitives["points_roundtrip"], points) record_property("roundtrip_squidpy", ours) @@ -546,7 +581,21 @@ def test_affine_from_points_is_equivalent_when_well_conditioned(primitives, reco """ source = np.asarray(primitives["landmarks_query"])[:, ::-1] target = np.asarray(primitives["landmarks_ref"])[:, ::-1] - linear, translation = _helpers.affine_from_points(jnp.asarray(source), jnp.asarray(target)) + # Reached through the public estimator: `niter=0` fits nothing, so the returned + # affine *is* the landmark initialisation. That also pins the wiring -- that + # `landmarks_*` actually reach the solver as its starting affine. + fit = fit_stalign_obs( + primitives["ref"], + primitives["query"], + landmarks_source=primitives["landmarks_query"], + landmarks_target=primitives["landmarks_ref"], + niter=0, + dx=F.RASTER_PARAMS["dx"], + blur=F.RASTER_PARAMS["blur"], + raster_expand=F.RASTER_PARAMS["expand"], + ) + affine = np.asarray(fit.affine) + linear, translation = affine[:2, :2], affine[:2, 2] ours = _residual(linear, translation, source, target) theirs = _residual(primitives["lt_well_L"], primitives["lt_well_T"], source, target) @@ -666,15 +715,13 @@ def test_converged_solution_matches_upstream(primitives, record_property): assert after < 0.4 * F.RASTER_PARAMS["dx"], f"reference did not converge: TRE {after:.2f}" assert after < before / 5.0, f"reference barely moved: TRE {before:.2f} -> {after:.2f}" - aligned = np.asarray( - _core.transform_points_row_col( - result["xv"], - result["v"], - result["A"], - jnp.asarray(primitives["query"])[:, ::-1], - direction="forward", - ) + converged = StalignResult( + affine=result["A"], + velocity=result["v"], + velocity_grid=result["xv"], + aligned_points=jnp.zeros((0, 2)), ) + aligned = _transform_rc(converged, np.asarray(primitives["query"])[:, ::-1]) displacement = np.linalg.norm(aligned - np.asarray(snapshot["aligned_points_rc"]), axis=1) percentile = float(np.percentile(displacement, 95)) record_property("p95_displacement", percentile) From b19d2e9696af49ba1dad79d0e0653c273c901563 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Fri, 31 Jul 2026 21:23:57 +0200 Subject: [PATCH 129/131] assess image path against reference --- docs/release/notes-dev.md | 3 + .../stalign_reference/converged_n500.npz | Bin 62629 -> 62626 bytes tests/_data/stalign_reference/energy.npz | Bin 32122 -> 32115 bytes tests/_data/stalign_reference/gradients.npz | Bin 53086 -> 53081 bytes .../stalign_reference/image_trajectory.npz | Bin 0 -> 103609 bytes .../image_trajectory_matched.npz | Bin 0 -> 67193 bytes tests/_data/stalign_reference/primitives.npz | Bin 172521 -> 172515 bytes .../_data/stalign_reference/trajectory_n1.npz | Bin 10274 -> 10271 bytes .../_data/stalign_reference/trajectory_n5.npz | Bin 24203 -> 24196 bytes .../stalign_reference/trajectory_n50.npz | Bin 24540 -> 24539 bytes .../methods/STALIGN_DIVERGENCES.md | 29 +++- .../methods/test_stalign_reference.py | 147 +++++++++++++++++- 12 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 tests/_data/stalign_reference/image_trajectory.npz create mode 100644 tests/_data/stalign_reference/image_trajectory_matched.npz diff --git a/docs/release/notes-dev.md b/docs/release/notes-dev.md index 5433ae54a..b6b1103f2 100644 --- a/docs/release/notes-dev.md +++ b/docs/release/notes-dev.md @@ -34,6 +34,9 @@ iteration (2.20 to 0.46 ms on the reference fixture, so `niter=5000` drops from ~11s to ~2.4s). Numerically unchanged: the reference suite still matches the original implementation at 1, 5, 50 and 500 iterations. +- The reference suite now covers `fit_stalign_image`, the last part of the experimental + STalign port without one. It reproduces the original to ~4e-12 on the affine and ~2e-12 on the + velocity field over a full trajectory. - The experimental STalign solver returns the per-iteration `energies` trace and `n_iter`, and accepts optional `tol` / `patience` early stopping. Off by default. Note the objective changes definition at iteration 50, when the mixture-weight E step engages, so the convergence window diff --git a/tests/_data/stalign_reference/converged_n500.npz b/tests/_data/stalign_reference/converged_n500.npz index 9bd3d77d0779b637d149f898ee0c6e25e71b8fc3..47cd3eacf6cc60cf7a397bccbe03936118ed10e4 100644 GIT binary patch delta 869 zcmV-r1DgD$=>wwa0}@b60|YGq000O8001EXw@W_UkrFC@Tmk?900000rB%yL+fWc4 zwtR)vjav~y9NXB5x-APtt4b}4t|G?|LP=s|J1r^VEBN8gh%@pyva=!5(Y={_=gjNQ z`1RZE*YCeh=np-+Szf1=J9pgAx1r~|BgZYus;QFgqO7u9POg%5owK}NC677(|M+R- zyg2XpojwzP(J8|LJ}?T#J>!YdFm{ZJkuz$>f{`*-kjef5XL5G~p1I*n7?+G0BZf`^ z*#v6}J~XLO;TP(eOw&~Oat>w9`+><3~WgKG>whx`$awaKs4rG#I7 zZ}tZMs~=Cu=cTa~VL!o{XKJA`vGz?0nYxK+^rzHga0CzH<_CFebnq& z>>8_oaN0gD5&y>6?CW3kRo~eRIn=&OsN0t@?xrRu8RP-(OtIHh`_wZ$Qy@X^bY55- zi+gYKSfZYX@%g5vHm5_pHMO&^LEeVA8*2Od_ko_)WRH4msoNJr_N}hY;-TK~Onkz5 z2A!QbJ7#N#Jk}i%!eCldr)+K(A&&UsGS>PQ{Sk>`@;ASP)h>@EdT%j2mk;8ApqNqwB-N)|NsB9 zx&kZi1-DB+++36D@KphzlR)t`2&Dr60C;v^F_RJT6qAwh8U>mJ002yr5%Cn0#PJ#d zIg{Vb97;BWs?!{6q7LW8U=s`002Rg5%Cn0WAYjSHIshwG66`FvGOwp zlXCz7S3#2z@f4H(@)`ycnE(J+LX#2k6q7RZ8Udq|P4hwlz>|>kMFF#u*7G?5Jd+Ug vI03_xN%S}*OYi^yMPF=Tb963laCuNm1qJ{B000pH5dgXa007eP00000aYLZR delta 844 zcmV-S1GD_1=>w(d0}@b60|YGq000O8001EX#hDDdkrFC@UjhIC00000rB%yr(@+o{ zw*3l=jX_OA;zyiBZ0Q0Ogalo%iu`N}NnF_uG*tZxewZ2cjP4||>7tR2?#6&!6s+*#elgo6|mMm}A>0QbH-@F}p zkKPM@XAeYwbjI+2*NlpB%eZHBj6I`al#G_KV&sf9WU~K>Gr7A3&(d(Fj2UCWm_w(6 zY=SihpPa`)!3c3K91-qk&`$qc9ZzJP9J zP=K=p3UfztFzU(m4esu+Hqhw|#|nAvVEZ(;0qh2U$kPS>C)kI`!#AvR=m$7kB0iCi zu~*J1?g-a8WE1GDI*3ocJCajv;-0Pj`&NK1^DLRTVaiG_S@uc4cQI+7rDI2+{7<6T09fG z#wwhDwvTJXzcn^ba}knz+Y9JL9smS$8hWrGGGl0B6tReQwsl6PNn-u}M_gEp9 z>PHUV@R_&|CXY4h*%zPI)aG=Ew>3U=$lDNiN3O4boakvy4yebDj{9QBzSY%PJk%T7 z!}?Cop|dw<|FgA69$OPjiFmf~yM+&Fj~^Lpxqg_TW;)w+md?q&fe7bXZ=KsQxmlz+ zOc8_5`-I&0q9oU&3H->6T)z!3iCcSm(-Zd^dY$2s^LK#sv+eOc{2SA8U&`H&j1P=U zdYTPdd;RXpe7J&d8FaqV)67rF%?+`sZ&c!aWc&lOpaMef1;v>RyI+&%@Kph$lRfb@ z2&e-90C;v^F_Yl&6qAne8U>vM002yr;PDib!SNaaJd@nb5K@T2*RUbY=M&BSL~~r-dTEf*s+b~Yd#U1#Rb>)e^) z_501YAHUzxA9{9+vdtTJ>A0V7f+%t)j$3V;u1Pn^wkb-{T&3%_WO-Ys4<-Np@Nwe2 zIB)nndnTfPGlm1aXH<+^#v`L+>=+HBWVDQgkuz$@)~7=P5@Z}+XZ9+Y-f-cu*(t4hyMUJnenN)$FQvo z3ivF6!rT!KCIeiraCd{VflX(8B;d6}>|<`{h&u;=PZ#(d;@bxgzi`H|_n=!EzNe^B zd5+;ze4j%$gN=TV)wZF&A08M_Be_paZ103W@v%1i2(MdkHq>+1wC`ih;}LvZnph#e zXV7_uOO;8ye{?tp7w{LsUxG7$?k{3Xo;74EjbB;QCbUTC4c9bGSwQ_a^4h z{AsR#+9xqM)VWFNaIPZU%?&37_5Be-7MN4#%rk!GKnm`3-Yrhm{bYFD;ysV+^Ua%D zPRIPV)bFkiyp8!jp3fEay*%W*GjFAPa;)!l2DJyzVY4&3Q!};$M=j!Ne+t*q4+(TN zWXE}A)c3l)Lqz@F(LER8l(!6^ujp{6E|6eoNgt zuj_%jYuI(hkLW)E^2T<4cgSx_hchR4zcRisu4q5^`u6%iQD*;|`uP%@pS0h{DfK%+ zVru_My)TUavy%cjcLhf&!)Qp8(Roz`i~;}vOp{Q16q7%C8U-r@002~zPu718pV%laVGClVp7m2DNzr00020IAh)b delta 804 zcmV+<1Ka%b`T_d-0TNJ40|YGq000O8001EX7;T2|krFC@PXYh{00000rIpWa+CUJ- zx7R!c>V;M%6l{YH>a{skX;rDsp(kWx8>$kBY||!<@)UXD&WPX08-$xfkUs5tX6N6x zGt1+btIuD5ymG(YeLpYiW!0be`X8>MB;J7yok!*|vM`vAs znNq)Tum0Y#jrJRW!4 z=c)SaPJK?h{MKF$4ZL;vKAz8&YkT>W@5Z|;dndd4e(dFLGp06sPxrqW+rv>!T1#1sH9H@K2M1{ssj zdk~Xddl~`SlX!bQ17`;SlYIvllh1o90r`{qdp`q*2mq6v2pN;ldk~Xddl~^7lYo3Z i0jQI{d_w~}3ILN)3Ko+ZeJTNTlSO?f2E2Iy0000iOpGr8 diff --git a/tests/_data/stalign_reference/gradients.npz b/tests/_data/stalign_reference/gradients.npz index ee10348ec56ee823618ccbf7bd8f44818159445a..4c4e4a98d915087420a5c69e20d00bed89f52e23 100644 GIT binary patch delta 775 zcmV+i1Ni*jo&(vQ0}@b60|YGq000O8001EX-{t*IkrFC@OacG^00000rB%yr(@+o{ zw*3l=jX|Wrj-A?$*wO_m2no7iW%(H+(zvo6XsP-Y{4g`h8F>=L*)-D8y`H)Ae&pYm z+s|KryrSRq7dEZWpk-K%Opt@g3nFAP?VhCXn|L;|lNPdyW_b zoLURvzr*Pn8ShMNTN?Y}p7A*0`y4jr@L@`y=-8P2NM2jy>{QR)(7un=j|b%A%Gio< z_rUW_E|n(s{?g$bTtZ(AeJRcmyg#rlem0=3;lI#x)KcRQP?vMDYpkNv&S8uA?~Kh; z^QXRlYM&&?q0UW4hjSICTIBN?k+nYdYLmuS@tEnH*;{?jG$O-Rl*NvlzjT+|YH0 zza(y*-OY)(8^{etkMQ3EQhz(kJNP%J!}*i9pBNt**Rd$%^Cr65C>LIIbP?ae{~Gm|jRKmo~vSqa-1vIw?5WIHjg9|8ZCi5p;g)efkoyGuP7rg*pZM4qeuUQsoE^J!7qq{}n#UdZn3-4! z-hJq%h6|NRynpn34$k0j4u2V*5p;hMTk@c25D z54i|`$=%Hqb`iOm(|lIs?s@@!0@N8mUZU0z@9I-$F(x3ub-%{QH8C9Ls6qE~LT;~D2mq5%2o#gl%n*}N&Kd#ElWfjD0TPp|&OHIVlj+Vq i15=*7T}|x_?Tk$g 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 delta 974 zcmaESnCs;S)x5rNUJ`83CIP4rBl!6-Et5(w*9&d>#No< z#VI$~m|tzR49N^#d3K?CO0w`GA%|_Alie!2_7#*L$=@Q}a@l!;-P@|P=l8c;AHKgn zJbvHbn#b?%ByZPvU3qnG*)I9@lRU4yE#1}ado|={)wY#gK_=oiSLyBD6PI@TQvLqw z`>$_XwrQfAvd>zKPifKQ{g> zQR!oI?1s(+-iYTvg#QYN$Qf3BoLFFY-0o>~&iTmpBc2mPZ}3f2y}>s@{KnZ%(Z}`& z)y(ZozjJQmZf2UVs&Pj0;ezs(*V*URMQZ0rJx*p@&%2R-XMJyn^uf!L_ceb@$klD= z+t`2L%7?q@q4kDax%?*bZ@5?+otrIw@9?$93suW|Z>sM%tACW7`a>|zyrAsO+1Oow zt*_aSF(H?U|uYS*2&azH!+vPuXVk%x+{&Be?si?ogG;O`zR`GVdFI7j% zTi%~}Vt73N&-`8TJpIz|K1bA6EuVJTZ2#7i3mdna-H)!=Z}}kNnzi_|r06xr?#w-R zcAK4r`Bo19^Ie{0Ug%n%d`zQ6d&?9glEIRZ7@ozG;&ST|i_0h1NCP&*StW=>9gaZxfvW8ngz#xv7< t7BE>bE}DLH0niGM>Hk60o#~bffu>nauUrVUe&X~U3z<~eWM_i%DgcDYzsLXp diff --git a/tests/_data/stalign_reference/trajectory_n1.npz b/tests/_data/stalign_reference/trajectory_n1.npz index aeb4a685d803b2bf0f2fb751d4b905310c19b58f..eb405f3b32aa85a57ae66e97f2772a196c09b765 100644 GIT binary patch delta 782 zcmV+p1M&Q#P@hl|P)h>@EdT%j2mk;8Apo7)SA~%hDt}l400000005;`U2mIE4E0|7 zE6`rp)HH*H6o~e^K1|&-Y3swDBEm=0EeREnty$Gy(LX#}Jr)iEUW#;d!M^sjkFU-1 z*PAcje!ihU^yp@JomTGBaX;QHyx5&MZc$cEmF!n#mF1$jO15>*@_Lip=luQd{ls~4 zUh$kg5`WPd!vWqh3dSAdfzdDyjEa#nYQ~C@GB%LO`)}xE?+$*J#%IE~WW zjtB5~Y0e7q?m_398mLUp`$wn!a{+%5{H?GC(ET->Z6Lcv{34gTn40|OsOMPjnydJ< z{eN2`|D8FruWvP0yzkvO${d*wRb6mEHJC~r*HhsfCSv>oUj_ioxMXn;hVgKvvb%SjBc!N z58$shIpoM=hj?qmko3f{rnV2HMiI5MOk`(ggjMPsKwr@5oL(T-&~TSfJHzxY>Hi_8 zwl_n>u{Lu37rZ8K?d5Gx-W%98#*gSf0Mf^{w-1PKN~irOdp|QiF|O!kCTQ*TKPz+L zn%dqIoA2~8;}dGKCKQvjCmI1xlg}qJAO$4=09QgTZg6=}O9ci10000B01E(3 M0ssKKB>(^b08>9~0ssI2 delta 774 zcmV+h1Nr=)P@+&0P)h>@EdT%j2mk;8Apilhyfu*$Dt}u700000005Y`O42GxI z{0h_ytpb$nE-av4n?sdWmD(J7Li4b3 z{QZ3W>C5+5?w5P$=0%m2-C3vm{(3Z=c6*)fVq4Z_vYBtoyb#T0vZ@L%ua?PO;lJO! z?R6eIFMs%*JvisiJUZaD$HL>*|?&g_JVc9JrS}T+Yzz|+adBCyNtU+{72ZN#;3+U z#dcwkje7%-Hh|X zcffv#ZU|SwxzWwczKxr3x;0$NgV--l*y%gE^Vqj=9#EG7zS`mtA1jkrsmGRhj=j|@ z;?_9s$>Z4E6`Nf3k!cN7Cineu&-zd6)}*0|obF#Hzij@hn`^BPsa_;29v#;Tp16pW4GC*UX53?*QM4i6g^r z_#fVTlgBOfJjPFJYIEA=w=yx*_!0d(Abo6mdq;c&_q-qF>_?9e9+&QECTQ*T ze^%zg6|tq*e05JVK5@;wkURBl3g1VMf3utdFe3#4v%EE1lhGwr2%iD~0C;v^Fq6?I z6q7-XlYJ)|2Am@R09Q?u(I*s>u_qbaL7B>(^b E0N!zVxBvhE diff --git a/tests/_data/stalign_reference/trajectory_n5.npz b/tests/_data/stalign_reference/trajectory_n5.npz index 0bbcaa5be0b10f7c00fdfae03dc27d4be23be2d3..b4e1212e05e06e516881d1712e5aaa4286760b12 100644 GIT binary patch delta 771 zcmV+e1N{7py#a*10TNJ40|YGq000O8001EX4SmCFkrFC@RssM300000rB%yrn@|v) zZu1qW8yeMd!9W13+h$RvRi!42t}M(WPHUS8a2q$uSM-NFqnr_A3%rS?qkBE~&g0D7 z8UB5}{qpU{8~RO8Zc(;*wICFiJ+tSTJ(N3NqRM!kOINz|P#*%ovx9l#xKE zg3QC3!=9W+K*0!bE;d5kUqU~|f6?`^M(`6tRzNp`EQGEHc>%o~z5>_}p|dnLHTD#` zl|ccUB~X|0Bf&Ve~0rGHzHHE$pj&t0X z{S;h1BTKMO0Q6rwAw|b4Z zHI7H*@zVH;vG;K1n;NK0{QafF{<(m?1ojqKL!A9FIa@(?hxkPgenp>j~fR(!Ld)ojE(z zw>#voH5^Lt*dX2(F=PXAEUE7UsZm1xEYsOynP8PVhd8h3a85^vH8#1+sGni_mh}5j zQs0{~;#eBFehXg1TYGsuz;b06|}KbYW~|lh9lglY?Cv0eX|3 zT`~bKlhIu=2L4$909Q?u&|DOg9bOs*vswTESCgS#6_Zh38UbpPY+e}#AzT0e000ik Bb%_7~ delta 771 zcmV+e1N{7iy#b580TNJ40|YGq000O8001EXrkW(|krFC@T>=0A00000rIk%@8bJ(( zr`P-n)C;XbDC`FdsMqFDrB$Uihn~4=dtsDg5TMLbMDNe173S9JZ?SiJ!+4gN9j>`R30;rxyKTj?0=$@vupfJjnC9$ zrkmcCM$Rcb9$aC!G+!f+K!X`65 zHTDU%3xgb=1<1`A$wBW(u5UQIVJ)$#jgJ|5t-1SuFt-8s2IT32?}U9w9=@@Tu@BHq zIWK(saML_Y(8+qveqd|{C%6fxTf?P1h&?`Gr|;;_W8cELkB>gQ+Tsu&E0b5L$Ch}G zz11t?);R9Tv5WmRfEzS*pt{)hM86lm(le-iS zI^Pr5{67?~>CJ>VG9%ajf|u~tUS1vHy~M6IenkHcNFUqY-VtBlJ?}?3`_bcr$EACk z30iyopOv|AMQj;1U)|G;PhB%F|K*KTvY*~lZ;$70j86}Tr~lkllWXR0XmZ_T`~d2lU`jo0fCd3T`~bR zlgeE(0tH%={#`Nwx|1D?u_!RJRr;^mR_wryYtT1 z&iMK3&6jUK-?%^Sv6JOhT6U&E=i^NrhMjKESrlbmCYxDNX1VTMC95hId9_ULbNPPv zz8gFRuXxUX9-VV%f&jb~EChFg2SF{^3Q9pPs01@XDp(@Z_uuU4*)=>37*;kH;^KUniTkS?7m*( zyZ7UPTwWSmBi{S$g{BT(ruP21{T{l2Zw%jzHDd3t$=edyE&i)q?_+A>A5yPVvHMt+ z)An$GLHuiD^H}eCU;Q1MkVD^diQDhVF=tbglMH#pnE~&9^*tIIo&iY6ou3sJr{dn3 zJQmdRFutLwsm*B{Z{?b~SCh9k?%Fjy{p&!_*JMXMHm>Q3HrrOu&)~Mc2u*Ctd5+H3 z?47c;C5M%XBPW(MK3DjV9PwlBnqJU4ja@Tm)BFq_vudp)_7`q{UdQ-4GWknfGt2ya z^8bn4HT^llkGYZif5L0x_Wis%6893l+VJT92cW%d`+JLjJ-6SJdiJy6li~z6H36BEP+l?xI0XOzRFffI6qB1?8Vke)002Q>b97;BWs@OZ6qD3m z8UcTk?_M$mGzS0xc9S7q6q7n%8U|Hc0037_lObLdlWt!c1`1pN09QeiAzl=dqF)*T P!IQmT83uS=00000R8w|e delta 777 zcmV+k1NQvezX9C80TNJ40|YGq000O8001EXG!daGkrFC@Tmk?900000rIpEU6G0G$ zn`@rJ;$l!7W6$DET*(0igakQovSy6~UZR;XBt&@%UYIWBD^11nkV7o}>ULHCwRKm` z-_MtyzI=b>e!2U8UesCDU-bI#FXQpJKj`(#UDZ^{cDbwaLNw>ex-PuDUL`k$|NrLg zp!d*w!SCdM-Z^*T(F3nNN{?%gTaU(L?@@Ua9<|5PBlB1xll@P0a(08Ag|V4|>@j_$j1lbo(iF4aNoG5jvBzNH(FyN__OdhJlfZzd!D251nCehP@?ggzmSg+X~qg@rzvEV`lP~JDNX}yXGo3 zZ4d8%$bVye9@Zjs?Yo}Ci(K2U8c*rkK1r!hK%N2eoLocpYE%1I-`X5b%vw@QjU&Tu z_)Ok+rj9%A^H_d*r?#eDc54$uL*2S;H?HmJ9|t_WlRNHX>)M{^@@;uKgS+}-Y;qHu zr*QU0_dj2IYFL{*3Ub*Hb4?7%kvLM<_QHf{`_h@FGj!(K`;XA;UF*EoIW#r(OUOaz z{M5DIiNdx0IVFzN$n~4>lDxH_*GKYR!E1~i(Z2)I%eKGw#5Z(Lds5DR^!VU$?jGlY z-o1WrWj0(8TMFl^dz|%&YiEY|)b}a*K6v~Cv!4R#S_L!_p($LGja^j;paK8@cy?bf zlOSFclm1;A0i}}}UNr%llTThU1vv!(092D8UKEp>UK$I<1poj+UvqR}Y-N)mUKEql zUK#;_lkQ$J0X35$Uor+(TL1u8O_LyA6q9XV8U+hn0038$@m>{^p H00000QdD@4 diff --git a/tests/experimental/methods/STALIGN_DIVERGENCES.md b/tests/experimental/methods/STALIGN_DIVERGENCES.md index 4a2cf4461..5eb2ab333 100644 --- a/tests/experimental/methods/STALIGN_DIVERGENCES.md +++ b/tests/experimental/methods/STALIGN_DIVERGENCES.md @@ -58,7 +58,7 @@ deliberately *not* bug-compatible with upstream (D4, D5, D6, D7, D9) — those s | **D7** | Landmark affine solve | normal equations for the plain least-squares fit, with explicit `np.linalg.inv` on the Gram matrix — `:897-910` | `skimage.transform.estimate_transform("affine")`, a Hartley-normalised homogeneous solve by SVD | `L` differs **6.4e-4**, `T` **7.2e-3**. Fit residual on clean landmarks: **21.7026** vs **21.6984** (1.9e-4 apart). On near-collinear landmarks: **7.4e-13** vs **5.6e+2** | **Different estimators, not the same one twice.** skimage minimises algebraic error, upstream geometric; upstream is a hair better on clean input and collapses when ill-conditioned. Keep squidpy's — see R7 | | **D8** | `lddmm()` cannot take a precomputed `xv`/`v` | `LDDMM` accepts both — `:1060-1064` | no such parameters | — | **No change needed.** The generator forces *upstream* onto squidpy's grid instead | | **D9** | Division guards | none | `jnp.maximum(…, 1e-12)` in `_update_mixture_weights` | inert for short runs | **squidpy is right.** Keep | -| **D10** | On-grid interpolation kink | normalises `(c-x0)/(x[-1]-x0)`, then `grid_sample(align_corners=True)` scales by `(n-1)` | `(c-x0)/(x[1]-x0)` | equal to ~1 ulp, but a sample landing exactly on a grid line can `floor()` to different neighbours | Not a defect in either. Fixtures are built off-grid on purpose and assert it | +| **D10** | On-grid interpolation kink | normalises `(c-x0)/(x[-1]-x0)`, then `grid_sample(align_corners=True)` scales by `(n-1)` | `(c-x0)/(x[1]-x0)` | equal to ~1 ulp, but a sample landing exactly on a grid line can `floor()` to different neighbours. **Measured cost: 1e-12 → 1e-3** on the velocity field (see below) | Not a defect in either. Every fixture is built off-grid on purpose and asserts it | ## Review — beyond the divergences @@ -110,6 +110,33 @@ sets it — so the shipped default runs a 5000-iteration gradient descent with `fit_stalign` now says so and shows how to enable x64. This suite requires `JAX_ENABLE_X64=1` and skips without it. +### D10 in practice: how the image path was nearly mis-assessed + +`fit_stalign_image` had no reference comparison at all — the last gap in the port's public +surface. Adding one produced a **5.3e-2** disagreement on the affine and **2.6e-1** on the +velocity field, which looked like a real port bug. + +It was not. The bisect is worth recording, because the same trap is waiting for anyone who +adds a fixture without the off-grid guard: + +| step | finding | +| --- | --- | +| energy at iteration 0 | matched to **4.6e-16** — the objective is identical | +| energy at iteration 1 | already 3e-3 apart — the *first gradient step* diverges | +| conditioning check | a 1e-12 nudge in `epV` moved the answer by 1e-13, linearly — not chaos | +| grids, axes, images, every parameter | verified identical on both sides | +| first hypothesis: padding | 11 % of the target grid samples the source out of domain, because each raster is centred on its own centre. **Wrong** — padding agrees on values *and* gradients | +| actual cause | centred pixel axes are **integers**, so an identity starting affine put every interpolation sample exactly on a grid line: D10, at full strength | + +Starting the fixture from a deliberately off-grid affine (a 0.0371449 rad rotation and a +non-integer shift, mirroring `fixtures.THETA`/`SHIFT`) drops the disagreement to **3.95e-12** +on the affine and **2.43e-12** on the velocity — with the 11 % padded samples still present. + +`image_trajectory_matched` keeps the degenerate case on purpose: both rasters cropped to a +common extent, so their axes are the *same* integers and grid coincidence persists even +off-grid. It still lands at ~1e-3, and `test_on_grid_sampling_costs_six_orders_of_magnitude` +pins that gap so the cost of D10 stays a measured number rather than a warning. + ### R7. `affine_from_points` silently changes the landmark estimator — open Following on from D7: `estimate_transform("affine")` is not a drop-in for upstream's diff --git a/tests/experimental/methods/test_stalign_reference.py b/tests/experimental/methods/test_stalign_reference.py index 685999144..91b1b0dcd 100644 --- a/tests/experimental/methods/test_stalign_reference.py +++ b/tests/experimental/methods/test_stalign_reference.py @@ -26,7 +26,11 @@ import jax.numpy as jnp # noqa: E402 -from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign_obs # noqa: E402 +from squidpy.experimental.methods.align_samples import ( # noqa: E402 + StalignResult, + fit_stalign_image, + fit_stalign_obs, +) from squidpy.experimental.methods.align_samples._stalign_impl import _core, _helpers # noqa: E402 from . import _stalign_fixtures as F # noqa: E402 @@ -147,7 +151,17 @@ def _transform_rc(result: StalignResult, points_rc, *, direction: str = "forward @pytest.mark.parametrize( "name", - ["primitives", "energy", "gradients", "trajectory_n1", "trajectory_n5", "trajectory_n50", "converged_n500"], + [ + "primitives", + "energy", + "gradients", + "trajectory_n1", + "trajectory_n5", + "trajectory_n50", + "converged_n500", + "image_trajectory", + "image_trajectory_matched", + ], ) def test_every_fixture_carries_provenance(name): """Without this the .npz files are unfalsifiable magic numbers within a year.""" @@ -675,6 +689,135 @@ def test_trajectory_matches_upstream(primitives, niter, record_property): assert error < budget, f"n={niter} {name}: {error:.3e} exceeds {budget:.0e}" +#: Matches ``IMAGE_PARAMS`` / ``IMAGE_ITERS`` in the generator. The image path works in +#: pixel units, so the kernel width and velocity step are far below the micron-scale +#: point-cloud defaults. +_IMAGE_PARAMS = {"a": 8.0, "p": 2.0, "expand": 2.0, "nt": 2, "diffeo_start": 4, "epV": 1.0} +_IMAGE_ITERS = 12 + + +@pytest.fixture(scope="module") +def image_reference(): + return _load("image_trajectory") + + +def test_image_path_axes_match_the_reference(image_reference): + """The image entry point's coordinate convention, checked before its results. + + ``fit_stalign_image`` centres pixel coordinates rather than using the point path's + physical microns. The solver agreeing on rasters says nothing about that convention, + so it is pinned separately -- if it drifted, the comparisons below would still pass + while aligning in a different frame. + """ + fit = fit_stalign_image(image_reference["ref"], image_reference["query"], niter=0, **_IMAGE_PARAMS) + + for got, expected in ( + (fit.query_axes[0], image_reference["source_axis_0"]), + (fit.query_axes[1], image_reference["source_axis_1"]), + (fit.ref_axes[0], image_reference["target_axis_0"]), + (fit.ref_axes[1], image_reference["target_axis_1"]), + ): + np.testing.assert_allclose(np.asarray(got), expected, rtol=0, atol=1e-12) + + +def _run_image(fixture, axes_ref: bool = True): + """Drive the solver on the image fixture, from the reference's own starting affine.""" + source = (jnp.asarray(fixture["source_axis_0"]), jnp.asarray(fixture["source_axis_1"])) + target = (jnp.asarray(fixture["target_axis_0"]), jnp.asarray(fixture["target_axis_1"])) + return _core.lddmm( + source, + jnp.asarray(fixture["query"]), + target, + jnp.asarray(fixture["ref"]), + L=fixture["start_L"], + T=fixture["start_T"], + niter=_IMAGE_ITERS, + **_IMAGE_PARAMS, + ) + + +@pytest.mark.parametrize("name", ["A", "v", "WM", "WA", "WB"]) +def test_image_path_matches_upstream(image_reference, name, record_property): + """The image entry point vs upstream's LDDMM on the same images, axes and start. + + Closes the last gap in the port's public surface: ``align(by="images")`` goes straight + through here and had no reference comparison at all. + + Note that ~11 % of the target grid samples the source through padding, because the two + rasters have different shapes and each is centred on its own centre. That agrees to + 1e-12 as well -- padding was the first suspect for an earlier disagreement and turned + out to be innocent on both values and gradients. + """ + result = _run_image(image_reference) + error = rel(result[name], image_reference[name]) + record_property("rel_error", error) + assert error < 1e-10, f"{name}: {error:.3e}" + + +def test_image_energy_trace_matches_upstream(image_reference, record_property): + """Every iteration of the objective, not just the endpoint. + + A trajectory can agree at the end while disagreeing throughout; comparing the whole + trace is what localises a disagreement to the step it starts at. + """ + result = _run_image(image_reference) + got = np.asarray(result["energies"]) + expected = np.asarray(image_reference["energies"]) + worst = max(abs(got[i] - expected[i]) / abs(expected[i]) for i in range(expected.size)) + record_property("worst_iteration_rel_error", worst) + assert worst < 1e-10 + + +def test_image_warp_matches_upstream(image_reference, record_property): + """``warp_image`` vs ``STalign.transform_image_source_to_target``. + + Exactly what ``align(by="images", out="images/...")`` writes, against upstream's own + image-warping composition rather than a reassembled one. + """ + result = _run_image(image_reference) + fit = StalignResult( + affine=result["A"], + velocity=result["v"], + velocity_grid=result["xv"], + aligned_points=jnp.zeros((0, 2)), + query_axes=(jnp.asarray(image_reference["source_axis_0"]), jnp.asarray(image_reference["source_axis_1"])), + ref_axes=(jnp.asarray(image_reference["target_axis_0"]), jnp.asarray(image_reference["target_axis_1"])), + ) + error = rel(fit.warp_image(jnp.asarray(image_reference["query"])), image_reference["warped"]) + record_property("rel_error", error) + assert error < 1e-9 + + +def test_on_grid_sampling_costs_six_orders_of_magnitude(record_property): + """Ledger row D10, measured rather than asserted. + + The control fixture crops both rasters to a common extent, so their axes are the + *same* integers and interpolation samples keep landing exactly on grid lines -- where + upstream and squidpy can floor() to different neighbours. Same solver, same inputs + otherwise, yet accuracy drops from 1e-12 to ~1e-3 on the velocity field. This is why + both fixtures start from a deliberately off-grid affine, and why the point-cloud + fixture asserts `test_fixture_samples_are_off_grid`. + """ + matched = _load("image_trajectory_matched") + axes = (jnp.asarray(matched["axis_0"]), jnp.asarray(matched["axis_1"])) + result = _core.lddmm( + axes, + jnp.asarray(matched["query"]), + axes, + jnp.asarray(matched["ref"]), + L=matched["start_L"], + T=matched["start_T"], + niter=_IMAGE_ITERS, + **_IMAGE_PARAMS, + ) + degraded = rel(result["v"], matched["v"]) + record_property("rel_error_on_grid", degraded) + assert 1e-6 < degraded < 1e-1, ( + f"on-grid sampling now costs {degraded:.3e}; if this has become exact, D10 is gone " + f"and the off-grid fixture design can be simplified" + ) + + def test_velocity_grid_is_the_one_the_reference_used(primitives, source_grid): """The trajectory comparison is only meaningful on a shared velocity grid. From 12ecd75bd096b500bf80e28349ae00052a2e9e6f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 20:11:26 +0000 Subject: [PATCH 130/131] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 387bbc31b..face49285 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -286,7 +286,10 @@ addopts = [ ] markers = [ "internet: tests that require internet", - "reference: numerical comparison against the vendored STalign reference (slow, needs JAX_ENABLE_X64=1, scheduled runs only)", + """\ + reference: numerical comparison against the vendored STalign reference (slow, needs JAX_ENABLE_X64=1, scheduled runs \ + only)\ + """, # the below markers are just there because we import from anndata.tests.helpers, # which means pytest sees them despite us not using them. "gpu: tests that require GPU", From 8e093205fad6572ed3bc2c7b4e54c086ca2fded4 Mon Sep 17 00:00:00 2001 From: selmanozleyen Date: Sat, 1 Aug 2026 00:40:18 +0200 Subject: [PATCH 131/131] extend STalign alignment API --- .../methods/align_samples/_stalign.py | 177 ++++++++++++++++-- .../align_samples/_stalign_impl/_core.py | 44 ++++- tests/experimental/methods/test_stalign.py | 62 +++++- 3 files changed, 258 insertions(+), 25 deletions(-) diff --git a/src/squidpy/experimental/methods/align_samples/_stalign.py b/src/squidpy/experimental/methods/align_samples/_stalign.py index 625462987..af08e8b2b 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign.py @@ -38,28 +38,86 @@ class StalignResult: #: images. ``None`` for point-cloud fits, where no raster survives the call. query_axes: tuple[JaxArray, JaxArray] | None = None ref_axes: tuple[JaxArray, JaxArray] | None = None + match_weights: JaxArray | None = None + artifact_weights: JaxArray | None = None + background_weights: JaxArray | None = None + energies: JaxArray | None = None + n_iter: int | None = None - def warp_image(self, image: JaxArray) -> JaxArray: - """Resample a query-frame ``(c, y, x)`` image onto the reference grid. + def deformation_grid( + self, + *, + direction: Literal["forward", "backward"] = "forward", + query_axes: tuple[JaxArray, JaxArray] | None = None, + ref_axes: tuple[JaxArray, JaxArray] | None = None, + ) -> JaxArray: + """Return a dense row-column coordinate transform for visualisation. - A diffeomorphism cannot be expressed as a SpatialData transformation -- the - available types are affine at most -- so an aligned image has to be materialised - rather than registered. + ``direction="forward"`` evaluates the query grid in the reference frame; + ``"backward"`` evaluates the reference grid in the query frame. The returned + array has shape ``(2, rows, columns)``. """ import jax.numpy as jnp - from ._stalign_impl._core import _interp, _transform_grid_backward, jax_dtype + from ._stalign_impl._core import _grid_points, _transform_grid_backward, transform_points_row_col - if self.query_axes is None or self.ref_axes is None: + source_axes = query_axes if query_axes is not None else self.query_axes + target_axes = ref_axes if ref_axes is not None else self.ref_axes + if source_axes is None or target_axes is None: raise ValueError( - "This result was fitted on point clouds, so it carries no raster axes to " - "warp an image with. Fit with `align(in_='images/', ...)` instead." + "This result was fitted on point clouds and carries no raster axes. " + "Pass both `query_axes=` and `ref_axes=`, or fit with " + "`align(in_='images/', ...)`." ) + if direction == "backward": + return _transform_grid_backward(target_axes, self.velocity_grid, self.velocity, self.affine) + if direction != "forward": + raise ValueError(f"Expected `direction` to be 'forward' or 'backward', found {direction!r}.") + + grid = _grid_points(source_axes) + points = jnp.moveaxis(grid, 0, -1).reshape((-1, 2)) + transformed = transform_points_row_col( + self.velocity_grid, self.velocity, self.affine, points, direction="forward" + ) + return jnp.moveaxis(transformed.reshape((*grid.shape[1:], 2)), -1, 0) + + def warp_image( + self, + image: JaxArray, + *, + direction: Literal["forward", "backward"] = "forward", + query_axes: tuple[JaxArray, JaxArray] | None = None, + ref_axes: tuple[JaxArray, JaxArray] | None = None, + ) -> JaxArray: + """Resample an image through the fitted transformation. + + A diffeomorphism cannot be expressed as a SpatialData transformation -- the + available types are affine at most -- so an aligned image has to be materialised + rather than registered. ``direction="forward"`` maps a query-frame image onto + the reference grid; ``"backward"`` maps a reference-frame image onto the query + grid. Explicit axes allow results fitted from point clouds to warp their density + rasters without pretending those rasters are original image elements. + """ + import jax.numpy as jnp + + from ._stalign_impl._core import _interp, jax_dtype + arr = jnp.asarray(image, dtype=jax_dtype()) if arr.ndim == 2: arr = arr[None] - grid = _transform_grid_backward(self.ref_axes, self.velocity_grid, self.velocity, self.affine) - return _interp(self.query_axes, arr, grid) + if direction not in {"forward", "backward"}: + raise ValueError(f"Expected `direction` to be 'forward' or 'backward', found {direction!r}.") + source_axes = query_axes if query_axes is not None else self.query_axes + target_axes = ref_axes if ref_axes is not None else self.ref_axes + grid = self.deformation_grid( + direction="backward" if direction == "forward" else "forward", + query_axes=source_axes, + ref_axes=target_axes, + ) + sampling_axes = source_axes if direction == "forward" else target_axes + if sampling_axes is None: # guarded by deformation_grid; keeps the type checker honest + raise AssertionError("missing sampling axes") + return _interp(sampling_axes, arr, grid) def transform( self, @@ -92,6 +150,9 @@ def fit_stalign_obs( *, landmarks_source: npt.ArrayLike | None = None, landmarks_target: npt.ArrayLike | None = None, + initial_affine: npt.ArrayLike | None = None, + initial_velocity: npt.ArrayLike | None = None, + velocity_grid: tuple[npt.ArrayLike, npt.ArrayLike] | None = None, # rasterization dx: float = 30.0, blur: float | Sequence[float] = (2.0, 1.0, 0.5), @@ -111,6 +172,8 @@ def fit_stalign_obs( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + muA: npt.ArrayLike | None = None, + muB: npt.ArrayLike | None = None, tol: float | None = None, patience: int = 25, ) -> StalignResult: @@ -126,6 +189,12 @@ def fit_stalign_obs( landmarks_source, landmarks_target Optional corresponding ``(x, y)`` landmark arrays used to initialise the affine. Must be provided together. + initial_affine + Optional homogeneous ``(3, 3)`` affine in public ``(x, y)`` coordinates. + Mutually exclusive with landmark initialisation. + initial_velocity, velocity_grid + Optional continuation state. The velocity has shape ``(nt, rows, columns, 2)``; + its components and the two grid axes use the solver's row-column convention. dx, blur, raster_expand Rasterization of the point clouds into density images: grid spacing, Gaussian blur scale(s), and field-of-view padding factor. @@ -140,6 +209,9 @@ def fit_stalign_obs( sigmaM, sigmaB, sigmaA, sigmaR, sigmaP Noise scales for the matching, background, artifact, regularisation, and landmark-point terms of the objective. + muA, muB + Optional per-channel artifact and background means. ``None`` estimates the + corresponding mean during fitting, matching upstream STalign's default. tol, patience Stop once the objective's relative improvement over the last ``patience`` iterations falls below ``tol``. ``tol=None`` (default) always runs ``niter``. @@ -171,6 +243,8 @@ def fit_stalign_obs( if (landmarks_source is None) != (landmarks_target is None): raise ValueError("Expected both landmark arrays to be provided together.") + if initial_affine is not None and landmarks_source is not None: + raise ValueError("`initial_affine` is mutually exclusive with landmark initialisation.") # The solver runs internally in row-col (y, x); inputs are (x, y) -- swap at the boundary. source_rc = validate_points(query, name="query")[:, ::-1] @@ -179,7 +253,15 @@ def fit_stalign_obs( target_grid, target_image = rasterize_cloud(target_rc, dx=dx, blur=blur, expand=raster_expand) dtype = jax_dtype() - if landmarks_source is None: + if initial_affine is not None: + affine_xy = jnp.asarray(initial_affine, dtype=dtype) + if affine_xy.shape != (3, 3): + raise ValueError(f"Expected `initial_affine` to have shape (3, 3), found {affine_xy.shape}.") + swap = jnp.asarray([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], dtype=dtype) + affine_rc = swap @ affine_xy @ swap + linear, translation = affine_rc[:2, :2], affine_rc[:2, 2] + src_lm = tgt_lm = None + elif landmarks_source is None: linear, translation = jnp.eye(2, dtype=dtype), jnp.zeros(2, dtype=dtype) src_lm = tgt_lm = None else: @@ -195,6 +277,8 @@ def fit_stalign_obs( target_image, L=linear, T=translation, + initial_velocity=initial_velocity, + velocity_grid=velocity_grid, points_source=src_lm, points_target=tgt_lm, a=a, @@ -211,6 +295,8 @@ def fit_stalign_obs( sigmaA=sigmaA, sigmaR=sigmaR, sigmaP=sigmaP, + muA=muA, + muB=muB, tol=tol, patience=patience, ) @@ -220,6 +306,11 @@ def fit_stalign_obs( velocity=result["v"], velocity_grid=result["xv"], aligned_points=aligned_rc[:, ::-1], + match_weights=result["WM"], + artifact_weights=result["WA"], + background_weights=result["WB"], + energies=result["energies"], + n_iter=int(result["n_iter"]), # No raster axes: the grids here are the internal density rasters at `dx` # resolution, not a frame any real image lives on. Offering `warp_image` off them # would quietly resample the caller's image onto a coarse, unrelated grid. @@ -233,6 +324,11 @@ def fit_stalign_image( *, ref_scale: tuple[float, float] = (1.0, 1.0), query_scale: tuple[float, float] = (1.0, 1.0), + ref_axes: tuple[npt.ArrayLike, npt.ArrayLike] | None = None, + query_axes: tuple[npt.ArrayLike, npt.ArrayLike] | None = None, + initial_affine: npt.ArrayLike | None = None, + initial_velocity: npt.ArrayLike | None = None, + velocity_grid: tuple[npt.ArrayLike, npt.ArrayLike] | None = None, # LDDMM registration a: float = 20.0, p: float = 2.0, @@ -248,6 +344,8 @@ def fit_stalign_image( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + muA: npt.ArrayLike | None = None, + muB: npt.ArrayLike | None = None, tol: float | None = None, patience: int = 25, ) -> StalignResult: @@ -262,6 +360,13 @@ def fit_stalign_image( Physical size of one pixel as ``(y, x)``. Defaults to pixel units. Pass the element's scale when the two images have different resolutions, otherwise the fit is done in mismatched coordinates. + ref_axes, query_axes + Optional explicit physical row and column axes. Both pairs must be supplied; + they are mutually exclusive with non-unit ``ref_scale``/``query_scale``. + initial_affine + Optional homogeneous ``(3, 3)`` affine in public ``(x, y)`` coordinates. + initial_velocity, velocity_grid + Optional continuation state in the solver's row-column convention. a, p, expand, nt, niter, diffeo_start LDDMM controls, as in :func:`fit_stalign_obs`. Note ``a`` is a length in the *same* units as ``ref_scale`` -- the default of 20 suits pixel units, where @@ -277,6 +382,9 @@ def fit_stalign_image( sigmaM, sigmaB, sigmaA, sigmaR, sigmaP Noise scales for the matching, background, artifact, regularisation, and landmark-point terms of the objective. + muA, muB + Optional per-channel artifact and background means. ``None`` estimates the + corresponding mean during fitting, matching upstream STalign's default. tol, patience Stop once the objective's relative improvement over the last ``patience`` iterations falls below ``tol``. ``tol=None`` (default) always runs ``niter``. @@ -319,16 +427,46 @@ def axes(image: JaxArray, scale: tuple[float, float]) -> tuple[JaxArray, JaxArra (jnp.arange(cols, dtype=dtype) - (cols - 1) / 2.0) * scale[1], ) - source_grid = axes(source_image, query_scale) - target_grid = axes(target_image, ref_scale) + if (query_axes is None) != (ref_axes is None): + raise ValueError("Expected both `query_axes` and `ref_axes` to be provided together.") + + def explicit_axes(value: tuple[npt.ArrayLike, npt.ArrayLike], image: JaxArray, name: str): + resolved = (jnp.asarray(value[0], dtype=dtype), jnp.asarray(value[1], dtype=dtype)) + expected = image.shape[1:] + if resolved[0].ndim != 1 or resolved[1].ndim != 1 or tuple(map(len, resolved)) != expected: + raise ValueError(f"Expected `{name}` lengths {expected}, found {tuple(map(len, resolved))}.") + if len(resolved[0]) < 2 or len(resolved[1]) < 2: + raise ValueError(f"Expected each `{name}` axis to contain at least two coordinates.") + return resolved + + if query_axes is None: + source_grid = axes(source_image, query_scale) + target_grid = axes(target_image, ref_scale) + else: + if query_scale != (1.0, 1.0) or ref_scale != (1.0, 1.0): + raise ValueError("Explicit axes are mutually exclusive with non-unit image scales.") + source_grid = explicit_axes(query_axes, source_image, "query_axes") + target_grid = explicit_axes(ref_axes, target_image, "ref_axes") + + if initial_affine is None: + linear, translation = jnp.eye(2, dtype=dtype), jnp.zeros(2, dtype=dtype) + else: + affine_xy = jnp.asarray(initial_affine, dtype=dtype) + if affine_xy.shape != (3, 3): + raise ValueError(f"Expected `initial_affine` to have shape (3, 3), found {affine_xy.shape}.") + swap = jnp.asarray([[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], dtype=dtype) + affine_rc = swap @ affine_xy @ swap + linear, translation = affine_rc[:2, :2], affine_rc[:2, 2] result = lddmm( source_grid, source_image, target_grid, target_image, - L=jnp.eye(2, dtype=dtype), - T=jnp.zeros(2, dtype=dtype), + L=linear, + T=translation, + initial_velocity=initial_velocity, + velocity_grid=velocity_grid, a=a, p=p, expand=expand, @@ -343,6 +481,8 @@ def axes(image: JaxArray, scale: tuple[float, float]) -> tuple[JaxArray, JaxArra sigmaA=sigmaA, sigmaR=sigmaR, sigmaP=sigmaP, + muA=muA, + muB=muB, tol=tol, patience=patience, ) @@ -353,4 +493,9 @@ def axes(image: JaxArray, scale: tuple[float, float]) -> tuple[JaxArray, JaxArra aligned_points=jnp.zeros((0, 2), dtype=dtype), query_axes=source_grid, ref_axes=target_grid, + match_weights=result["WM"], + artifact_weights=result["WA"], + background_weights=result["WB"], + energies=result["energies"], + n_iter=int(result["n_iter"]), ) diff --git a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py index 0b7ded685..8100c9244 100644 --- a/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py +++ b/src/squidpy/experimental/methods/align_samples/_stalign_impl/_core.py @@ -469,6 +469,8 @@ def lddmm( *, L: np.ndarray | jax.Array, T: np.ndarray | jax.Array, + initial_velocity: np.ndarray | jax.Array | None = None, + velocity_grid: tuple[np.ndarray | jax.Array, np.ndarray | jax.Array] | None = None, points_source: np.ndarray | jax.Array | None = None, points_target: np.ndarray | jax.Array | None = None, a: float = 500.0, @@ -485,6 +487,8 @@ def lddmm( sigmaA: float = 5.0, sigmaR: float = 5e5, sigmaP: float = 2e1, + muA: np.ndarray | jax.Array | None = None, + muB: np.ndarray | jax.Array | None = None, tol: float | None = None, patience: int = 25, ) -> dict[str, Any]: @@ -523,17 +527,41 @@ def lddmm( source_landmarks = jnp.asarray(points_source, dtype=jax_dtype()) target_landmarks = jnp.asarray(points_target, dtype=jax_dtype()) - xv = _build_velocity_grid(x_source, a=a, expand=expand) - velocity = jnp.zeros((nt, xv[0].shape[0], xv[1].shape[0], 2), dtype=jax_dtype()) + if (initial_velocity is None) != (velocity_grid is None): + raise ValueError("Expected `initial_velocity` and `velocity_grid` to be provided together.") + if velocity_grid is None: + xv = _build_velocity_grid(x_source, a=a, expand=expand) + velocity = jnp.zeros((nt, xv[0].shape[0], xv[1].shape[0], 2), dtype=jax_dtype()) + else: + xv = (jnp.asarray(velocity_grid[0], dtype=jax_dtype()), jnp.asarray(velocity_grid[1], dtype=jax_dtype())) + velocity = jnp.asarray(initial_velocity, dtype=jax_dtype()) + if velocity.ndim != 4: + raise ValueError(f"Expected `initial_velocity` to be four-dimensional, found {velocity.shape}.") + expected = (velocity.shape[0], xv[0].shape[0], xv[1].shape[0], 2) + if velocity.shape != expected: + raise ValueError( + "Expected `initial_velocity` to have shape " + f"(nt, {xv[0].shape[0]}, {xv[1].shape[0]}, 2), found {velocity.shape}." + ) kernel, ll, dv_prod = _build_regularizer(xv, a=a, p=p) match_weights = jnp.full(target_image.shape[1:], 0.5, dtype=target_image.dtype) background_weights = jnp.full(target_image.shape[1:], 0.4, dtype=target_image.dtype) artifact_weights = jnp.full(target_image.shape[1:], 0.1, dtype=target_image.dtype) - muA = jnp.mean(target_image, axis=(1, 2)) - muB = jnp.zeros_like(muA) - estimate_muA = True - estimate_muB = True + n_channels = target_image.shape[0] + + def mixture_mean(value: np.ndarray | jax.Array | None, *, name: str, default: jax.Array) -> jax.Array: + if value is None: + return default + mean = jnp.asarray(value, dtype=target_image.dtype) + if mean.shape != (n_channels,): + raise ValueError(f"Expected `{name}` to have shape ({n_channels},), found {mean.shape}.") + return mean + + artifact_mean = mixture_mean(muA, name="muA", default=jnp.mean(target_image, axis=(1, 2))) + background_mean = mixture_mean(muB, name="muB", default=jnp.zeros(n_channels, dtype=target_image.dtype)) + estimate_muA = muA is None + estimate_muB = muB is None final = _lddmm_run( linear, @@ -542,8 +570,8 @@ def lddmm( match_weights, artifact_weights, background_weights, - muA, - muB, + artifact_mean, + background_mean, x_source=x_source, source_image=source_image, x_target=x_target, diff --git a/tests/experimental/methods/test_stalign.py b/tests/experimental/methods/test_stalign.py index 0dd933e5c..cb00ef8ea 100644 --- a/tests/experimental/methods/test_stalign.py +++ b/tests/experimental/methods/test_stalign.py @@ -17,7 +17,7 @@ pytest.importorskip("jax") from squidpy.experimental.methods import ALIGN -from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign_obs +from squidpy.experimental.methods.align_samples import StalignResult, fit_stalign_image, fit_stalign_obs # Flat solver kwargs (assembled into the config internally) -- smallest possible solve. _TINY = {"dx": 0.5, "blur": 1.0, "a": 1.0, "expand": 1.0, "nt": 1, "niter": 1, "epV": 1.0} @@ -57,6 +57,11 @@ def test_stalign_fit_returns_diffeomorphism() -> None: assert np.all(np.isfinite(np.asarray(result.aligned_points))) assert result.affine.shape == (3, 3) assert result.velocity.ndim == 4 + assert result.match_weights is not None + assert result.artifact_weights is not None + assert result.background_weights is not None + assert result.energies is not None + assert result.n_iter == 1 def test_stalign_transform_matches_aligned_points() -> None: @@ -101,6 +106,29 @@ def test_stalign_fit_with_landmarks() -> None: assert result.aligned_points.shape == query.shape +def test_stalign_fit_with_explicit_affine_in_xy() -> None: + ref, query = _points_xy(), _points_xy() + affine = np.array([[1.0, 0.0, 3.0], [0.0, 1.0, 4.0], [0.0, 0.0, 1.0]]) + + result = fit_stalign_obs( + ref, query, initial_affine=affine, niter=0, **{k: v for k, v in _TINY.items() if k != "niter"} + ) + + np.testing.assert_allclose(np.asarray(result.aligned_points), query + np.array([3.0, 4.0]), atol=1e-5) + + +def test_stalign_image_accepts_explicit_axes_and_warps_both_directions() -> None: + image = np.arange(20, dtype=float).reshape(4, 5) + axes = (np.linspace(10.0, 13.0, 4), np.linspace(20.0, 24.0, 5)) + + result = fit_stalign_image(image, image, query_axes=axes, ref_axes=axes, niter=0, a=1.0, nt=1) + + np.testing.assert_allclose(np.asarray(result.warp_image(image, direction="forward"))[0], image, atol=1e-5) + np.testing.assert_allclose(np.asarray(result.warp_image(image, direction="backward"))[0], image, atol=1e-5) + assert result.deformation_grid(direction="forward").shape == (2, 4, 5) + assert result.deformation_grid(direction="backward").shape == (2, 4, 5) + + def test_stalign_fit_rejects_non_2d_input() -> None: with pytest.raises(ValueError, match=r"Expected `query` to have shape `\(n, 2\)`"): fit_stalign_obs(_points_xy(), np.zeros((5, 3)), **_TINY) @@ -128,6 +156,38 @@ def test_lddmm_accepts_zero_iterations() -> None: assert result["A"].shape == (3, 3) +def test_lddmm_accepts_fixed_mixture_means() -> None: + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + from squidpy.experimental.methods.align_samples._stalign_impl._helpers import rasterize_cloud + + axes, image = rasterize_cloud(_points_xy()[:, ::-1], dx=0.5, blur=1.0, expand=1.1) + rgb = np.repeat(np.asarray(image), 3, axis=0) + result = lddmm( + axes, + rgb, + axes, + rgb, + L=np.eye(2), + T=np.zeros(2), + niter=1, + a=1.0, + nt=1, + muA=np.ones(3), + muB=np.zeros(3), + ) + + assert np.all(np.isfinite(np.asarray(result["A"]))) + + +def test_lddmm_rejects_wrong_mixture_mean_shape() -> None: + from squidpy.experimental.methods.align_samples._stalign_impl._core import lddmm + from squidpy.experimental.methods.align_samples._stalign_impl._helpers import rasterize_cloud + + grid = rasterize_cloud(_points_xy()[:, ::-1], dx=0.5, blur=1.0, expand=1.1) + with pytest.raises(ValueError, match=r"Expected `muA` to have shape \(1,\)"): + lddmm(*grid, *grid, L=np.eye(2), T=np.zeros(2), niter=0, a=1.0, nt=1, muA=np.ones(3)) + + def test_default_dtype_is_unchanged() -> None: """Guards the reference suite's blast radius.

    dKRPFkAn=N|$OR%! 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 diff --git a/tests/_data/stalign_reference/trajectory_n1.npz b/tests/_data/stalign_reference/trajectory_n1.npz index 604b19403243929f4fb6493dcef058f38486e452..aeb4a685d803b2bf0f2fb751d4b905310c19b58f 100644 GIT binary patch delta 8221 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 delta 8194 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 diff --git a/tests/_data/stalign_reference/trajectory_n5.npz b/tests/_data/stalign_reference/trajectory_n5.npz index b07e7a7f746357c8a324917e3646c4381c29c96c..0bbcaa5be0b10f7c00fdfae03dc27d4be23be2d3 100644 GIT binary patch delta 22304 zcmV)2K+M1Rya9{70TNJ40|YGq000O8001EXrkW(|krE<*bO-=0A00000 zrIk%@8bJ((r`P-n)C;XbDC`FdsMqFDrB$Uihn~`R30;rxyKTj?0=$@ zvupfJjnC9$rkmcCM$Rcb9$aC!G z+!f+K!X`65HTDU%3xgb=1<1`A$wBW(u5UQIVJ)$Lsf~{rd9AtoFt-8s2IT32?}U9w z9=@@Tu@BHqIWK(saML_Y(8+qveqd|{C%6fxTf?P1h&?`Gr|;;_W8cELkB>gQ+Tsu& zE0b5L$Ch}Gz11t?);R9Tv5WmRf zEzS*pf349e?#fks+Wy_be{JqO%th#$H$A~Fa!s#lJPX(KNJf4Fcm~LGxQ6W2r}l8{ zni&!B9pF1Pam?`>{)hM86lm(le-iSI^Pr5{67?~>CJ>VG9%ajf|u~tUS1vHy~M6IenkHcNFUqY-VtBlJ?}?3 z`_bcr$EACk30iyopOv|AMQj;1U)|G;PhB%F&H01V^cm{(343qo@ECjIf zEqjwJ2OWQzQUCw|0001#n29%3|NqDBL<>bJM2e84O(nUaWKGGZY(@RS)ln;V1%Yp%Ae{jOIpI@_|=a(fk} z_WW;CW^M9{Ql3)(?+<@*p+)VJG%_AV{7^Y}nhY6_vzj>r6$tw-AJ#ug!M%D7$-t*& zm?URe_Hxkh*qn6m@~KidF&#FGo~^-o&Gx4%=A|gsqz?BglA4cQBK)p$0;cVKSAB|YW{%7~9l~kmx z3^XWNs>jus{r2oBxa>HRvXW8(0mbYin_H>y zzUexf;!}zC3Wid|-%2RtT{ye%;Xhn@>HO`Icsbncx_qT#%ka>)juxj=iZk!cHA#OH zRp_W3_n7#v1XelM|0%Da;-cW;zCO(o{Qi<9now4aPbW67&tEFRl=^&hu^=5AhL#@Y zIhUb#Lwb1McRD7l64^7nDxkh%+UMyTI@Z}eKi$kr#>{~id)pIf;E=GlTJe;Ek>K~* zt|HZ7+r+_a-dv4Us$0+Z&5=P4V%LAmPNzZYs{c-o{S^@L%S#GcUxS{O^pQIEVnlt( z@D=5&g_Ty+c(-8zdP8P+7g^L|N#X9RYO!3bh_ceUxm1Juuj|y;>*j!Xr=i`lNXOv3 zV0J2dHg0X<$t-?MgVl*Dj@&aj7zz#!i!Y$!*x(*58;)EMdoO-oy+}q*i`9Qu_2zuc ztMmRByS@^`zQaxDQb>4YsBH8fS2^w&$DX-%xCn1{omlxaq!gOv*SnvYmSMZ;-IR-M zB~akmU2rC(3cP#|PHZSCf$ZuTgcE5i&U_?SvpCE~NTr^QDWBEPLP zZqS8{SlNt`pwMiPTwcUnjHY1Ik?ZdX#T2M&=i2o&QNd&L;;3g@AlwbIikNL556vGo3*v~R#spe>Hb{P-vTJ88)|lYR)T%bG;7n(3@EiX+F$uwg})J# zC8C{AVCyN6;nzq8n~AUev~LQbT$bN7B1wh%y|e1cR1lBv@U4GJvaUu^&#))yX92