Skip to content

Commit

Permalink
build: drop python 3.8 (#205)
Browse files Browse the repository at this point in the history
* drop py38

* cast to float
  • Loading branch information
tlambert03 authored Nov 21, 2024
1 parent ede3511 commit 96affb8
Show file tree
Hide file tree
Showing 19 changed files with 85 additions and 85 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.11", "3.12", "3.13"]
include:
- os: ubuntu-latest
python-version: "3.8"
- os: macos-latest
- os: windows-latest
python-version: "3.10"

test-minreqs:
Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ name = "useq-schema"
description = "Schema for multi-dimensional microscopy experiments"
readme = "README.md"
keywords = ["microscopy", "schema"]
requires-python = ">=3.8"
requires-python = ">=3.9"
license = { text = "BSD 3-Clause License" }
authors = [
{ email = "[email protected]", name = "Talley Lambert" },
Expand All @@ -23,7 +23,6 @@ classifiers = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down Expand Up @@ -73,7 +72,7 @@ packages = ["src/useq"]
# https://beta.ruff.rs/docs/rules/
[tool.ruff]
line-length = 88
target-version = "py38"
target-version = "py39"
src = ["src", "tests"]

[tool.ruff.lint]
Expand Down
6 changes: 3 additions & 3 deletions src/useq/_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
TYPE_CHECKING,
Any,
ClassVar,
Iterable,
Optional,
Type,
TypeVar,
Union,
)
Expand All @@ -16,6 +14,8 @@
from pydantic import BaseModel, ConfigDict

if TYPE_CHECKING:
from collections.abc import Iterable

from typing_extensions import Self

ReprArgs = Iterable[tuple[str | None, Any]]
Expand Down Expand Up @@ -84,7 +84,7 @@ class MutableModel(_ReplaceableModel):

class UseqModel(FrozenModel):
@classmethod
def from_file(cls: Type[_Y], path: Union[str, Path]) -> _Y:
def from_file(cls: type[_Y], path: Union[str, Path]) -> _Y:
"""Return an instance of this class from a file. Supports JSON and YAML."""
path = Path(path)
if path.suffix in {".yaml", ".yml"}:
Expand Down
18 changes: 8 additions & 10 deletions src/useq/_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@
import contextlib
import math
import warnings
from collections.abc import Iterable, Iterator, Sequence
from enum import Enum
from typing import (
TYPE_CHECKING,
Annotated,
Any,
Callable,
Iterable,
Iterator,
Optional,
Sequence,
Tuple,
Union,
)

import numpy as np
from annotated_types import Ge, Gt
from pydantic import Field, field_validator, model_validator
from typing_extensions import Annotated, Self, TypeAlias
from typing_extensions import Self, TypeAlias

from useq._point_visiting import OrderMode, TraversalOrder
from useq._position import (
Expand Down Expand Up @@ -76,11 +74,11 @@ class _GridPlan(_MultiPointPlan[PositionT]):
Engines MAY override this even if provided.
"""

overlap: Tuple[float, float] = Field((0.0, 0.0), frozen=True)
overlap: tuple[float, float] = Field((0.0, 0.0), frozen=True)
mode: OrderMode = Field(OrderMode.row_wise_snake, frozen=True)

@field_validator("overlap", mode="before")
def _validate_overlap(cls, v: Any) -> Tuple[float, float]:
def _validate_overlap(cls, v: Any) -> tuple[float, float]:
with contextlib.suppress(TypeError, ValueError):
v = float(v)
if isinstance(v, float):
Expand Down Expand Up @@ -156,7 +154,7 @@ def iter_grid_positions(
def __iter__(self) -> Iterator[PositionT]: # type: ignore [override]
yield from self.iter_grid_positions()

def _step_size(self, fov_width: float, fov_height: float) -> Tuple[float, float]:
def _step_size(self, fov_width: float, fov_height: float) -> tuple[float, float]:
dx = fov_width - (fov_width * self.overlap[0]) / 100
dy = fov_height - (fov_height * self.overlap[1]) / 100
return dx, dy
Expand Down Expand Up @@ -409,7 +407,7 @@ def __iter__(self) -> Iterator[RelativePosition]: # type: ignore [override]
seed = np.random.RandomState(self.random_seed)
func = _POINTS_GENERATORS[self.shape]

points: list[Tuple[float, float]] = []
points: list[tuple[float, float]] = []
needed_points = self.num_points
start_at = self.start_at
if isinstance(start_at, RelativePosition):
Expand Down Expand Up @@ -454,7 +452,7 @@ def num_positions(self) -> int:


def _is_a_valid_point(
points: list[Tuple[float, float]],
points: list[tuple[float, float]],
x: float,
y: float,
min_dist_x: float,
Expand Down
6 changes: 3 additions & 3 deletions src/useq/_hardware_autofocus.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, Optional, Tuple
from typing import Any, Optional

from pydantic import PrivateAttr

Expand Down Expand Up @@ -42,7 +42,7 @@ def event(self, event: MDAEvent) -> Optional[MDAEvent]:
if not self.should_autofocus(event):
return None

updates: Dict[str, Any] = {"action": self.as_action()}
updates: dict[str, Any] = {"action": self.as_action()}
if event.z_pos is not None and event.sequence is not None:
zplan = event.sequence.z_plan
if zplan and zplan.is_relative and "z" in event.index:
Expand Down Expand Up @@ -71,7 +71,7 @@ class AxesBasedAF(AutoFocusPlan):
is change, (in other words: every time the position is changed.).
"""

axes: Tuple[str, ...]
axes: tuple[str, ...]
_previous: dict = PrivateAttr(default_factory=dict)

def should_autofocus(self, event: MDAEvent) -> bool:
Expand Down
12 changes: 7 additions & 5 deletions src/useq/_iter_sequence.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from __future__ import annotations

from functools import lru_cache
from functools import cache
from itertools import product
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Iterator, cast
from typing import TYPE_CHECKING, Any, cast

from typing_extensions import TypedDict

Expand All @@ -14,6 +14,8 @@
from useq._z import AnyZPlan # noqa: TCH001 # noqa: TCH001

if TYPE_CHECKING:
from collections.abc import Iterator

from useq._mda_sequence import MDASequence
from useq._position import Position, PositionBase, RelativePosition

Expand All @@ -39,17 +41,17 @@ class PositionDict(TypedDict, total=False):
z_pos: float


@lru_cache(maxsize=None)
@cache
def _iter_axis(seq: MDASequence, ax: str) -> tuple[Channel | float | PositionBase, ...]:
return tuple(seq.iter_axis(ax))


@lru_cache(maxsize=None)
@cache
def _sizes(seq: MDASequence) -> dict[str, int]:
return {k: len(list(_iter_axis(seq, k))) for k in seq.axis_order}


@lru_cache(maxsize=None)
@cache
def _used_axes(seq: MDASequence) -> str:
return "".join(k for k in seq.axis_order if _sizes(seq)[k])

Expand Down
13 changes: 4 additions & 9 deletions src/useq/_mda_event.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
# don't add __future__.annotations here
# pydantic2 isn't rebuilding the model correctly

from collections.abc import Mapping, Sequence
from types import MappingProxyType
from typing import (
TYPE_CHECKING,
Any,
Dict,
List,
Mapping,
NamedTuple,
Optional,
Sequence,
Tuple,
)

import numpy as np
Expand All @@ -29,7 +25,7 @@
if TYPE_CHECKING:
from useq._mda_sequence import MDASequence

ReprArgs = Sequence[Tuple[Optional[str], Any]]
ReprArgs = Sequence[tuple[Optional[str], Any]]


class Channel(UseqModel):
Expand Down Expand Up @@ -182,7 +178,6 @@ class MDAEvent(UseqModel):
`False`.
"""

# MappingProxyType is not subscriptable on Python 3.8
index: Mapping[str, int] = Field(default_factory=lambda: MappingProxyType({}))
channel: Optional[Channel] = None
exposure: Optional[float] = Field(default=None, gt=0.0)
Expand All @@ -193,8 +188,8 @@ class MDAEvent(UseqModel):
z_pos: Optional[float] = None
slm_image: Optional[SLMImage] = None
sequence: Optional["MDASequence"] = Field(default=None, repr=False)
properties: Optional[List[PropertyTuple]] = None
metadata: Dict[str, Any] = Field(default_factory=dict)
properties: Optional[list[PropertyTuple]] = None
metadata: dict[str, Any] = Field(default_factory=dict)
action: AnyAction = Field(default_factory=AcquireImage)
keep_shutter_open: bool = False
reset_event_timer: bool = False
Expand Down
25 changes: 10 additions & 15 deletions src/useq/_mda_sequence.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
from __future__ import annotations

from collections.abc import Iterable, Iterator, Mapping, Sequence
from contextlib import suppress
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
Iterator,
Mapping,
Optional,
Sequence,
Tuple,
Union,
)
from uuid import UUID, uuid4
Expand Down Expand Up @@ -182,24 +177,24 @@ class MDASequence(UseqModel):
```
"""

metadata: Dict[str, Any] = Field(default_factory=dict)
axis_order: Tuple[str, ...] = AXES
metadata: dict[str, Any] = Field(default_factory=dict)
axis_order: tuple[str, ...] = AXES
# note that these are BOTH just `Sequence[Position]` but we retain the distinction
# here so that WellPlatePlans are preserved in the model instance.
stage_positions: Union[WellPlatePlan, Tuple[Position, ...]] = Field(
stage_positions: Union[WellPlatePlan, tuple[Position, ...]] = Field(
default_factory=tuple, union_mode="left_to_right"
)
grid_plan: Optional[MultiPointPlan] = Field(
default=None, union_mode="left_to_right"
)
channels: Tuple[Channel, ...] = Field(default_factory=tuple)
channels: tuple[Channel, ...] = Field(default_factory=tuple)
time_plan: Optional[AnyTimePlan] = None
z_plan: Optional[AnyZPlan] = None
autofocus_plan: Optional[AnyAutofocusPlan] = None
keep_shutter_open_across: Tuple[str, ...] = Field(default_factory=tuple)
keep_shutter_open_across: tuple[str, ...] = Field(default_factory=tuple)

_uid: UUID = PrivateAttr(default_factory=uuid4)
_sizes: Optional[Dict[str, int]] = PrivateAttr(default=None)
_sizes: Optional[dict[str, int]] = PrivateAttr(default=None)

@property
def uid(self) -> UUID:
Expand All @@ -225,7 +220,7 @@ def _validate_keep_shutter_open_across(cls, v: tuple[str, ...]) -> tuple[str, ..
return v

@field_validator("channels", mode="before")
def _validate_channels(cls, value: Any) -> Tuple[Channel, ...]:
def _validate_channels(cls, value: Any) -> tuple[Channel, ...]:
if isinstance(value, str) or not isinstance(
value, Sequence
): # pragma: no cover
Expand All @@ -245,7 +240,7 @@ def _validate_channels(cls, value: Any) -> Tuple[Channel, ...]:
@field_validator("stage_positions", mode="before")
def _validate_stage_positions(
cls, value: Any
) -> Union[WellPlatePlan, Tuple[Position, ...]]:
) -> Union[WellPlatePlan, tuple[Position, ...]]:
if isinstance(value, np.ndarray):
if value.ndim == 1:
value = [value]
Expand Down Expand Up @@ -402,7 +397,7 @@ def _check_order(
raise ValueError(err) # pragma: no cover

@property
def shape(self) -> Tuple[int, ...]:
def shape(self) -> tuple[int, ...]:
"""Return the shape of this sequence.
!!! note
Expand Down
Loading

0 comments on commit 96affb8

Please sign in to comment.