Skip to content

Commit

Permalink
improve repr
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 committed Jul 11, 2024
1 parent 600486f commit 278b904
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/useq/_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ class WellPlatePlan(FrozenModel, Sequence[Position]):
selected_wells: Union[Tuple[Tuple[int, ...], Tuple[int, ...]], None] = None
well_points_plan: RelativeMultiPointPlan = Field(default_factory=RelativePosition)

def __repr_args__(self) -> Iterable[Tuple[str | None, Any]]:
for item in super().__repr_args__():
if item[0] == "selected_wells":
# improve repr for selected_wells
yield "selected_wells", _expression_repr(item[1])
else:
yield item

@field_validator("plate", mode="before")
@classmethod
def _validate_plate(cls, value: Any) -> Any:
Expand Down Expand Up @@ -389,3 +397,50 @@ def _index_to_row_name(index: int) -> str:
name = chr(index % 26 + 65) + name
index = index // 26 - 1
return name


def _find_pattern(seq: Sequence[int]) -> tuple[list[int] | None, int | None]:
n = len(seq)

# Try different lengths of the potential repeating pattern
for pattern_length in range(1, n // 2 + 1):
pattern = list(seq[:pattern_length])
repetitions = n // pattern_length

# Check if the pattern repeats enough times
if np.array_equal(pattern * repetitions, seq[: pattern_length * repetitions]):
return (pattern, repetitions)

return None, None


def _pattern_repr(pattern: Sequence[int]) -> str:
"""Turn pattern into a slice object if possible."""
start = pattern[0]
stop = pattern[-1] + 1
step = pattern[1] - pattern[0]
if all(pattern[i] == pattern[0] + i * step for i in range(1, len(pattern))):
if step == 1:
if start == 0:
return f"slice({stop})"
return f"slice({start}, {stop})"
return f"slice({start}, {stop}, {step})"
return repr(pattern)


class _Repr:
def __init__(self, string: str) -> None:
self._string = string

def __repr__(self) -> str:
return self._string


def _expression_repr(expr: tuple[Sequence[int], Sequence[int]]) -> _Repr:
"""Try to represent an index expression as slice objects if possible."""
e0, e1 = expr
ptrn1, repeats = _find_pattern(e1)
if ptrn1 is None:
return _Repr(str(expr))
ptrn0 = e0[:: len(ptrn1)]
return _Repr(f"({_pattern_repr(ptrn0)}, {_pattern_repr(ptrn1)})")
20 changes: 20 additions & 0 deletions tests/test_well_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,23 @@ def test_plate_plan_equality() -> None:
pp3 = useq.WellPlatePlan.model_validate_json(pp.model_dump_json())

assert pp == pp2 == pp3


def test_plate_repr() -> None:
# both can be reduced
pp = useq.WellPlatePlan(
plate=96, a1_center_xy=(0, 0), selected_wells=np.s_[1:5, 3:12:2]
)
assert "selected_wells=(slice(1, 5), slice(3, 12, 2))" in repr(pp)

# can't be reduced
pp = useq.WellPlatePlan(
plate=96, a1_center_xy=(0, 0), selected_wells=[(1, 1, 1, 2), (7, 3, 4, 2)]
)
assert "selected_wells=((1, 1, 1, 2), (7, 3, 4, 2))" in repr(pp)

# one can be reduced
pp = useq.WellPlatePlan(
plate=96, a1_center_xy=(0, 0), selected_wells=np.s_[(1, 2, 2, 3), 1:5]
)
assert "selected_wells=((1, 2, 2, 3), slice(1, 5))" in repr(pp)

0 comments on commit 278b904

Please sign in to comment.