Skip to content

Commit

Permalink
refactor: slight changes to well plate registry
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 committed Jul 7, 2024
1 parent 69813f6 commit 7cc1c96
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 107 deletions.
10 changes: 3 additions & 7 deletions src/useq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,8 @@
from useq._hardware_autofocus import AnyAutofocusPlan, AutoFocusPlan, AxesBasedAF
from useq._mda_event import MDAEvent, PropertyTuple
from useq._mda_sequence import MDASequence
from useq._plate import (
WellPlate,
WellPlatePlan,
known_well_plate_keys,
register_well_plates,
)
from useq._plate import WellPlate, WellPlatePlan
from useq._plate_registry import register_well_plates, registered_well_plate_keys
from useq._position import Position, RelativePosition
from useq._time import (
AnyTimePlan,
Expand All @@ -42,6 +38,7 @@
"AcquireImage",
"Action",
"register_well_plates",
"registered_well_plate_keys",
"AnyAutofocusPlan",
"AnyGridPlan",
"AnyTimePlan",
Expand All @@ -54,7 +51,6 @@
"GridRowsColumns",
"GridWidthHeight",
"HardwareAutofocus",
"known_well_plate_keys",
"MDAEvent",
"MDASequence",
"MultiPhaseTimePlan",
Expand Down
102 changes: 5 additions & 97 deletions src/useq/_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
from contextlib import suppress
from functools import cached_property
from typing import (
TYPE_CHECKING,
Any,
Callable,
Iterable,
List,
Mapping,
Sequence,
Tuple,
Union,
Expand All @@ -23,20 +22,9 @@

from useq._base_model import FrozenModel
from useq._grid import GridRowsColumns, RandomPoints, Shape, _PointsPlan
from useq._plate_registry import _PLATE_REGISTRY
from useq._position import Position, PositionBase, RelativePosition

if TYPE_CHECKING:
from collections.abc import Callable
from typing import Required, TypedDict

class KnownPlateKwargs(TypedDict, total=False):
rows: Required[int]
columns: Required[int]
well_spacing: Required[tuple[float, float] | float]
well_size: tuple[float, float] | float | None
circular_wells: bool
name: str


class _SliceType:
@classmethod
Expand Down Expand Up @@ -120,7 +108,7 @@ def from_str(cls, name: str) -> WellPlate:
Use `useq.register_well_plates` to add new plates to the registry.
"""
try:
obj = _KNOWN_PLATES[name]
obj = _PLATE_REGISTRY[name]
except KeyError as e:
raise ValueError(
f"Unknown plate name {name!r}. "
Expand All @@ -140,7 +128,8 @@ class WellPlatePlan(FrozenModel, Sequence[Position]):
----------
plate : WellPlate | str | int
The well-plate definition. Minimally including rows, columns, and well spacing.
If expressed as a string, it is assumed to be a key in `WellPlate.KNOWN_PLATES`.
If expressed as a string, it is assumed to be a key in
`useq.registered_well_plate_keys`.
a1_center_xy : tuple[float, float]
The stage coordinates in µm of the center of well A1 (top-left corner).
rotation : float | None
Expand Down Expand Up @@ -464,84 +453,3 @@ def _index_to_row_name(index: int) -> str:
name = chr(index % 26 + 65) + name
index = index // 26 - 1
return name


# ---------------------------- Known Plates ----------------------------

PlateOrKwargs = Union["KnownPlateKwargs", WellPlate]
_KNOWN_PLATES: dict[str, PlateOrKwargs] = {
"6-well": {"rows": 2, "columns": 3, "well_spacing": 39.12, "well_size": 34.8},
"12-well": {"rows": 3, "columns": 4, "well_spacing": 26, "well_size": 22},
"24-well": {"rows": 4, "columns": 6, "well_spacing": 19, "well_size": 15.6},
"48-well": {"rows": 6, "columns": 8, "well_spacing": 13, "well_size": 11.1},
"96-well": {"rows": 8, "columns": 12, "well_spacing": 9, "well_size": 6.4},
"384-well": {
"rows": 16,
"columns": 24,
"well_spacing": 4.5,
"well_size": 3.4,
"circular_wells": False,
},
"1536-well": {
"rows": 32,
"columns": 48,
"well_spacing": 2.25,
"well_size": 1.7,
"circular_wells": False,
},
}


@overload
def register_well_plates(
plates: Mapping[str, PlateOrKwargs],
/,
**kwargs: PlateOrKwargs,
) -> None: ...
@overload
def register_well_plates(
plates: Iterable[tuple[str, PlateOrKwargs]],
/,
**kwargs: PlateOrKwargs,
) -> None: ...
@overload
def register_well_plates(**kwargs: PlateOrKwargs) -> None: ...
def register_well_plates(
plates: Mapping[str, PlateOrKwargs] | Iterable[tuple[str, PlateOrKwargs]] = (),
/,
**kwargs: PlateOrKwargs,
) -> None:
"""Register well-plate definitions to allow lookup by key.
Added keys will override existing keys if they already exist.
Values may either be WellPlate instances, or dictionaries with the following keys:
- rows: Required[int]
- columns: Required[int]
- well_spacing: Required[tuple[float, float]]
- well_size: tuple[float, float] | None
- circular_wells: bool
- name: str
Examples
--------
>>> import useq
>>> useq.register_well_plates(
... {
... "custom-square-plate": {
... "rows": 8, "columns": 8, "well_spacing": 9.3, "well_size": 7.1
... },
... "silly-plate": {"rows": 1, "columns": 1, "well_spacing": 100}
... }
... )
"""
_KNOWN_PLATES.update(plates, **kwargs)


def known_well_plate_keys() -> set[str]:
"""Return a set of all registered well-plate keys.
These keys may be used as an argument to `WellPlatePlan.plate` to select a plate
definition.
"""
return set(_KNOWN_PLATES)
112 changes: 112 additions & 0 deletions src/useq/_plate_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from __future__ import annotations

from typing import TYPE_CHECKING, overload

if TYPE_CHECKING:
from typing import Iterable, Mapping, Required, TypeAlias, TypedDict

from useq._plate import WellPlate

class KnownPlateKwargs(TypedDict, total=False):
rows: Required[int]
columns: Required[int]
well_spacing: Required[tuple[float, float] | float]
well_size: tuple[float, float] | float | None
circular_wells: bool
name: str

PlateOrKwargs: TypeAlias = KnownPlateKwargs | WellPlate


_PLATE_REGISTRY: dict[str, PlateOrKwargs] = {
"6-well": {"rows": 2, "columns": 3, "well_spacing": 39.12, "well_size": 34.8},
"12-well": {"rows": 3, "columns": 4, "well_spacing": 26, "well_size": 22},
"24-well": {"rows": 4, "columns": 6, "well_spacing": 19, "well_size": 15.6},
"48-well": {"rows": 6, "columns": 8, "well_spacing": 13, "well_size": 11.1},
"96-well": {"rows": 8, "columns": 12, "well_spacing": 9, "well_size": 6.4},
"384-well": {
"rows": 16,
"columns": 24,
"well_spacing": 4.5,
"well_size": 3.4,
"circular_wells": False,
},
"1536-well": {
"rows": 32,
"columns": 48,
"well_spacing": 2.25,
"well_size": 1.7,
"circular_wells": False,
},
"coverslip-18mm-square": {
"rows": 1,
"columns": 1,
"well_spacing": 0.0,
"well_size": 18.0,
"circular_wells": False,
"name": "18mm coverslip",
},
"coverslip-22mm-square": {
"rows": 1,
"columns": 1,
"well_spacing": 0.0,
"well_size": 22.0,
"circular_wells": False,
"name": "22mm coverslip",
},
}


@overload
def register_well_plates(
plates: Mapping[str, PlateOrKwargs],
/,
**kwargs: PlateOrKwargs,
) -> None: ...
@overload
def register_well_plates(
plates: Iterable[tuple[str, PlateOrKwargs]],
/,
**kwargs: PlateOrKwargs,
) -> None: ...
@overload
def register_well_plates(**kwargs: PlateOrKwargs) -> None: ...
def register_well_plates(
plates: Mapping[str, PlateOrKwargs] | Iterable[tuple[str, PlateOrKwargs]] = (),
/,
**kwargs: PlateOrKwargs,
) -> None:
"""Register well-plate definitions to allow lookup by key.
Added keys will override existing keys if they already exist.
Values may either be WellPlate instances, or dictionaries with the following keys:
- rows: Required[int]
- columns: Required[int]
- well_spacing: Required[tuple[float, float]]
- well_size: tuple[float, float] | None
- circular_wells: bool
- name: str
Examples
--------
>>> import useq
>>> useq.register_well_plates(
... {
... "custom-square-plate": {
... "rows": 8, "columns": 8, "well_spacing": 9.3, "well_size": 7.1
... },
... "silly-plate": {"rows": 1, "columns": 1, "well_spacing": 100}
... }
... )
"""
_PLATE_REGISTRY.update(plates, **kwargs)


def registered_well_plate_keys() -> set[str]:
"""Return a set of all registered well-plate keys.
These keys may be used as an argument to `WellPlatePlan.plate` to select a plate
definition.
"""
return set(_PLATE_REGISTRY)
7 changes: 4 additions & 3 deletions tests/test_well_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

import useq
from useq import _plate
from useq import _plate, _plate_registry


def test_plate_plan() -> None:
Expand Down Expand Up @@ -68,7 +68,8 @@ def test_plate_errors() -> None:

def test_custom_plate(monkeypatch: pytest.MonkeyPatch) -> None:
plates: dict = {}
monkeypatch.setattr(_plate, "_KNOWN_PLATES", plates)
monkeypatch.setattr(_plate_registry, "_PLATE_REGISTRY", plates)
monkeypatch.setattr(_plate, "_PLATE_REGISTRY", plates)

useq.register_well_plates(
{
Expand All @@ -79,7 +80,7 @@ def test_custom_plate(monkeypatch: pytest.MonkeyPatch) -> None:
myplate={"rows": 8, "columns": 8, "well_spacing": 9, "well_size": 10},
)
assert "myplate" in plates
assert "silly" in useq.known_well_plate_keys()
assert "silly" in useq.registered_well_plate_keys()

with pytest.raises(ValueError, match="Unknown plate name"):
useq.WellPlatePlan(plate="bad", a1_center_xy=(0, 0))
Expand Down

0 comments on commit 7cc1c96

Please sign in to comment.