pymmcore-plus 0.11.1__py3-none-any.whl → 0.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. pymmcore_plus/_build.py +4 -1
  2. pymmcore_plus/_cli.py +5 -5
  3. pymmcore_plus/_logger.py +4 -1
  4. pymmcore_plus/_util.py +2 -1
  5. pymmcore_plus/core/_config.py +5 -3
  6. pymmcore_plus/core/_config_group.py +2 -1
  7. pymmcore_plus/core/_metadata.py +2 -2
  8. pymmcore_plus/core/_mmcore_plus.py +2 -4
  9. pymmcore_plus/core/_property.py +3 -1
  10. pymmcore_plus/core/_sequencing.py +8 -6
  11. pymmcore_plus/core/events/__init__.py +3 -3
  12. pymmcore_plus/core/events/_device_signal_view.py +8 -6
  13. pymmcore_plus/core/events/_norm_slot.py +2 -4
  14. pymmcore_plus/core/events/_prop_event_mixin.py +7 -4
  15. pymmcore_plus/core/events/_protocol.py +5 -2
  16. pymmcore_plus/install.py +4 -2
  17. pymmcore_plus/mda/_engine.py +2 -3
  18. pymmcore_plus/mda/_protocol.py +1 -1
  19. pymmcore_plus/mda/_runner.py +29 -21
  20. pymmcore_plus/mda/_thread_relay.py +5 -3
  21. pymmcore_plus/mda/handlers/_5d_writer_base.py +3 -1
  22. pymmcore_plus/mda/handlers/_img_sequence_writer.py +2 -1
  23. pymmcore_plus/mda/handlers/_ome_zarr_writer.py +6 -6
  24. pymmcore_plus/mda/handlers/_tensorstore_handler.py +3 -2
  25. pymmcore_plus/mda/handlers/_util.py +1 -1
  26. pymmcore_plus/metadata/schema.py +23 -23
  27. pymmcore_plus/metadata/serialize.py +5 -1
  28. pymmcore_plus/model/_config_file.py +2 -1
  29. pymmcore_plus/model/_config_group.py +2 -1
  30. pymmcore_plus/model/_core_device.py +3 -1
  31. pymmcore_plus/model/_core_link.py +2 -1
  32. pymmcore_plus/model/_device.py +2 -1
  33. pymmcore_plus/model/_microscope.py +3 -1
  34. pymmcore_plus/model/_pixel_size_config.py +2 -1
  35. pymmcore_plus/model/_property.py +2 -1
  36. {pymmcore_plus-0.11.1.dist-info → pymmcore_plus-0.12.0.dist-info}/METADATA +3 -4
  37. pymmcore_plus-0.12.0.dist-info/RECORD +59 -0
  38. pymmcore_plus-0.11.1.dist-info/RECORD +0 -59
  39. {pymmcore_plus-0.11.1.dist-info → pymmcore_plus-0.12.0.dist-info}/WHEEL +0 -0
  40. {pymmcore_plus-0.11.1.dist-info → pymmcore_plus-0.12.0.dist-info}/entry_points.txt +0 -0
  41. {pymmcore_plus-0.11.1.dist-info → pymmcore_plus-0.12.0.dist-info}/licenses/LICENSE +0 -0
pymmcore_plus/_build.py CHANGED
@@ -11,12 +11,15 @@ import subprocess
11
11
  import tempfile
12
12
  from contextlib import contextmanager
13
13
  from pathlib import Path
14
- from typing import Iterator, Sequence
14
+ from typing import TYPE_CHECKING
15
15
  from urllib.request import Request, urlopen
16
16
 
17
17
  from rich import print
18
18
  from rich.prompt import Prompt
19
19
 
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Iterator, Sequence
22
+
20
23
  MM_REPO = "micro-manager/micro-manager"
21
24
  MMCORE_AND_DEV = "micro-manager/mmCoreAndDevices"
22
25
  MM_REPO_URL = f"https://github.com/{MM_REPO}.git"
pymmcore_plus/_cli.py CHANGED
@@ -6,7 +6,7 @@ import sys
6
6
  import time
7
7
  from contextlib import suppress
8
8
  from pathlib import Path
9
- from typing import List, Optional, Union, cast
9
+ from typing import Optional, Union, cast
10
10
 
11
11
  try:
12
12
  import typer
@@ -190,10 +190,10 @@ def run(
190
190
  None, help="Asymmetric range of z-stack below position."
191
191
  ),
192
192
  z_step: Optional[float] = typer.Option(None, help="Step size of z-stack."),
193
- z_relative: Optional[List[float]] = typer.Option(
193
+ z_relative: Optional[list[float]] = typer.Option(
194
194
  None, "-zr", help="Relative z-positions to acquire (may use multiple times)."
195
195
  ),
196
- z_absolute: Optional[List[float]] = typer.Option(
196
+ z_absolute: Optional[list[float]] = typer.Option(
197
197
  None, "-za", help="Absolute z-positions to acquire (may use multiple times)."
198
198
  ),
199
199
  t_interval: Optional[float] = typer.Option(
@@ -207,7 +207,7 @@ def run(
207
207
  axis_order: Optional[str] = typer.Option(
208
208
  None, help="Order of axes to acquire (e.g. 'TPCZ')."
209
209
  ),
210
- channel: Optional[List[str]] = typer.Option(
210
+ channel: Optional[list[str]] = typer.Option(
211
211
  None,
212
212
  help="\bChannel to acquire. Argument is a string of the following form:\n"
213
213
  '\b - name: "DAPI"\n'
@@ -294,7 +294,7 @@ def run(
294
294
 
295
295
  @app.command()
296
296
  def build_dev(
297
- devices: Optional[List[str]] = typer.Argument(
297
+ devices: Optional[list[str]] = typer.Argument(
298
298
  None, help=f"Device adapters to build. Defaults to {DEFAULT_PACKAGES}"
299
299
  ),
300
300
  dest: Path = typer.Option(
pymmcore_plus/_logger.py CHANGED
@@ -6,7 +6,10 @@ import sys
6
6
  from contextlib import contextmanager
7
7
  from logging.handlers import RotatingFileHandler
8
8
  from pathlib import Path
9
- from typing import ClassVar, Iterator
9
+ from typing import TYPE_CHECKING, ClassVar
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Iterator
10
13
 
11
14
  __all__ = ["logger"]
12
15
 
pymmcore_plus/_util.py CHANGED
@@ -18,8 +18,9 @@ from typing import TYPE_CHECKING, cast, overload
18
18
  from platformdirs import user_data_dir
19
19
 
20
20
  if TYPE_CHECKING:
21
+ from collections.abc import Iterator
21
22
  from re import Pattern
22
- from typing import Any, Callable, Iterator, Literal, TypeVar
23
+ from typing import Any, Callable, Literal, TypeVar
23
24
 
24
25
  QtConnectionType = Literal["AutoConnection", "DirectConnection", "QueuedConnection"]
25
26
 
@@ -3,15 +3,17 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections import defaultdict
6
- from typing import TYPE_CHECKING, Any, Iterable, Iterator, Tuple, overload
6
+ from typing import TYPE_CHECKING, Any, overload
7
7
 
8
8
  import pymmcore
9
9
 
10
10
  if TYPE_CHECKING:
11
+ from collections.abc import Iterable, Iterator
12
+
11
13
  from typing_extensions import TypeAlias # py310
12
14
 
13
- DevPropValueTuple: TypeAlias = Tuple[str, str, str]
14
- DevPropTuple: TypeAlias = Tuple[str, str]
15
+ DevPropValueTuple: TypeAlias = tuple[str, str, str]
16
+ DevPropTuple: TypeAlias = tuple[str, str]
15
17
 
16
18
 
17
19
  class Configuration(pymmcore.Configuration):
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Iterator, Literal, MutableMapping, overload
3
+ from collections.abc import Iterator, MutableMapping
4
+ from typing import TYPE_CHECKING, Any, Literal, overload
4
5
 
5
6
  import pymmcore
6
7
 
@@ -1,8 +1,8 @@
1
1
  """pythonic wrapper on pymmcore.Metadata object."""
2
2
 
3
- from collections.abc import Mapping
3
+ from collections.abc import ItemsView, Iterator, KeysView, Mapping, ValuesView
4
4
  from types import new_class
5
- from typing import Any, ItemsView, Iterator, KeysView, ValuesView, cast
5
+ from typing import Any, cast
6
6
 
7
7
  import pymmcore
8
8
 
@@ -10,17 +10,14 @@ from collections import defaultdict
10
10
  from contextlib import contextmanager, suppress
11
11
  from datetime import datetime
12
12
  from pathlib import Path
13
+ from re import Pattern
13
14
  from textwrap import dedent
14
15
  from threading import RLock, Thread
15
16
  from typing import (
16
17
  TYPE_CHECKING,
17
18
  Any,
18
19
  Callable,
19
- Iterable,
20
- Iterator,
21
20
  NamedTuple,
22
- Pattern,
23
- Sequence,
24
21
  TypeVar,
25
22
  overload,
26
23
  )
@@ -51,6 +48,7 @@ from ._sequencing import can_sequence_events
51
48
  from .events import CMMCoreSignaler, PCoreSignaler, _get_auto_core_callback_class
52
49
 
53
50
  if TYPE_CHECKING:
51
+ from collections.abc import Iterable, Iterator, Sequence
54
52
  from typing import Literal, TypedDict
55
53
 
56
54
  import numpy as np
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from functools import cached_property
4
- from typing import TYPE_CHECKING, Any, Sequence, TypedDict
4
+ from typing import TYPE_CHECKING, Any, TypedDict
5
5
 
6
6
  from pymmcore import g_Keyword_Label, g_Keyword_State
7
7
 
@@ -9,6 +9,8 @@ from ._constants import DeviceType, PropertyType
9
9
  from .events._device_signal_view import _DevicePropValueSignal
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from collections.abc import Sequence
13
+
12
14
  from ._mmcore_plus import CMMCorePlus
13
15
 
14
16
 
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from itertools import product
4
- from typing import TYPE_CHECKING, Literal, Sequence, Tuple, overload
4
+ from typing import TYPE_CHECKING, Literal, overload
5
5
 
6
6
  from useq import AcquireImage, MDAEvent
7
7
 
8
8
  from pymmcore_plus.core._constants import DeviceType
9
9
 
10
10
  if TYPE_CHECKING:
11
+ from collections.abc import Sequence
12
+
11
13
  from pymmcore_plus import CMMCorePlus
12
14
 
13
15
 
@@ -18,12 +20,12 @@ class SequencedEvent(MDAEvent):
18
20
  calculate sequences for x, y, z, and exposure based on an a sequence of events.
19
21
  """
20
22
 
21
- events: Tuple[MDAEvent, ...] # noqa: UP006
23
+ events: tuple[MDAEvent, ...]
22
24
 
23
- exposure_sequence: Tuple[float, ...] # noqa: UP006
24
- x_sequence: Tuple[float, ...] # noqa: UP006
25
- y_sequence: Tuple[float, ...] # noqa: UP006
26
- z_sequence: Tuple[float, ...] # noqa: UP006
25
+ exposure_sequence: tuple[float, ...]
26
+ x_sequence: tuple[float, ...]
27
+ y_sequence: tuple[float, ...]
28
+ z_sequence: tuple[float, ...]
27
29
 
28
30
  # technically this is more like a field, but it requires a core instance
29
31
  # to getConfigData for channels, so we leave it as a method.
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, List, Type
1
+ from typing import TYPE_CHECKING, Any
2
2
 
3
3
  from pymmcore_plus._util import signals_backend
4
4
 
@@ -18,7 +18,7 @@ __all__ = [
18
18
  ]
19
19
 
20
20
 
21
- def _get_auto_core_callback_class() -> Type[PCoreSignaler]:
21
+ def _get_auto_core_callback_class() -> type[PCoreSignaler]:
22
22
  if signals_backend() == "qt":
23
23
  from ._qsignals import QCoreSignaler
24
24
 
@@ -26,7 +26,7 @@ def _get_auto_core_callback_class() -> Type[PCoreSignaler]:
26
26
  return CMMCoreSignaler
27
27
 
28
28
 
29
- def __dir__() -> List[str]: # pragma: no cover
29
+ def __dir__() -> list[str]: # pragma: no cover
30
30
  return [*list(globals()), "QCoreSignaler"]
31
31
 
32
32
 
@@ -1,14 +1,16 @@
1
- from typing import TYPE_CHECKING, Any, Optional
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
2
4
 
3
5
  if TYPE_CHECKING:
4
6
  from pymmcore_plus.core import CMMCorePlus
5
7
 
6
- from ._prop_event_mixin import _C
8
+ from ._prop_event_mixin import _C
7
9
 
8
10
 
9
11
  class _DevicePropValueSignal:
10
12
  def __init__(
11
- self, device_label: str, property_name: Optional[str], mmcore: "CMMCorePlus"
13
+ self, device_label: str, property_name: str | None, mmcore: CMMCorePlus
12
14
  ) -> None:
13
15
  self._dev = device_label
14
16
  self._prop = property_name
@@ -18,13 +20,13 @@ class _DevicePropValueSignal:
18
20
  sig = self._mmc.events.devicePropertyChanged(self._dev, self._prop)
19
21
  return sig.connect(callback) # type: ignore
20
22
 
21
- def disconnect(self, callback: _C) -> None:
23
+ def disconnect(self, callback: _C | None = None) -> None:
22
24
  sig = self._mmc.events.devicePropertyChanged(self._dev, self._prop)
23
- return sig.disconnect(callback) # type: ignore
25
+ sig.disconnect(callback)
24
26
 
25
27
  def emit(self, *args: Any) -> Any:
26
28
  """Emits the signal with the given arguments."""
27
29
  self._mmc.events.devicePropertyChanged(self._dev, self._prop).emit(*args)
28
30
 
29
- def __call__(self, property: str) -> "_DevicePropValueSignal":
31
+ def __call__(self, property: str) -> _DevicePropValueSignal:
30
32
  return _DevicePropValueSignal(self._dev, property, self._mmc)
@@ -8,13 +8,11 @@ from types import MethodType
8
8
  from typing import TYPE_CHECKING, Any, Callable, Union
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing import Tuple
12
-
13
11
  from typing_extensions import TypeGuard # py310
14
12
 
15
- MethodRef = Tuple[weakref.ReferenceType[object], str, Callable | None]
13
+ MethodRef = tuple[weakref.ReferenceType[object], str, Callable | None]
16
14
  NormedCallback = Union[MethodRef, Callable]
17
- StoredSlot = Tuple[NormedCallback, int | None]
15
+ StoredSlot = tuple[NormedCallback, int | None]
18
16
  ReducerFunc = Callable[[tuple, tuple], tuple]
19
17
 
20
18
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Tuple, TypeVar
3
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, TypeVar
4
4
 
5
5
  from ._norm_slot import denormalize_slot, normalize_slot
6
6
  from ._protocol import PCoreSignaler
@@ -8,8 +8,8 @@ from ._protocol import PCoreSignaler
8
8
  if TYPE_CHECKING:
9
9
  from ._norm_slot import NormedCallback
10
10
 
11
- PropKey = Tuple[str, str | None, NormedCallback]
12
- PropKeyDict = Dict[PropKey, Callable]
11
+ PropKey = tuple[str, str | None, NormedCallback]
12
+ PropKeyDict = dict[PropKey, Callable]
13
13
 
14
14
 
15
15
  _C = TypeVar("_C", bound=Callable[..., Any])
@@ -64,8 +64,11 @@ class _PropertySignal:
64
64
  self._events.propertyChanged.connect(_wrapper)
65
65
  return callback
66
66
 
67
- def disconnect(self, callback: Callable) -> None:
67
+ def disconnect(self, callback: Callable | None = None) -> None:
68
68
  """Disconnect `callback` from this device and/or property."""
69
+ if callback is None:
70
+ self._events.propertyChanged.disconnect()
71
+ return
69
72
  key = (self._device, self._property, normalize_slot(callback))
70
73
  cb = self._events.property_callbacks.pop(key, None)
71
74
  if cb is None:
@@ -12,8 +12,11 @@ class PSignalInstance(Protocol):
12
12
  def connect(self, slot: Callable) -> Any:
13
13
  """Connect slot to this signal."""
14
14
 
15
- def disconnect(self, slot: Callable) -> Any:
16
- """Disconnect slot from this signal."""
15
+ def disconnect(self, slot: Optional[Callable] = None) -> Any:
16
+ """Disconnect slot from this signal.
17
+
18
+ If `None`, all slots should be disconnected.
19
+ """
17
20
 
18
21
  def emit(self, *args: Any) -> Any:
19
22
  """Emits the signal with the given arguments."""
pymmcore_plus/install.py CHANGED
@@ -10,7 +10,7 @@ import tempfile
10
10
  from contextlib import contextmanager, nullcontext
11
11
  from pathlib import Path
12
12
  from platform import system
13
- from typing import TYPE_CHECKING, Callable, ContextManager, Iterator, Protocol
13
+ from typing import TYPE_CHECKING, Callable, Protocol
14
14
  from urllib.request import urlopen, urlretrieve
15
15
 
16
16
  import typer
@@ -18,6 +18,8 @@ import typer
18
18
  from pymmcore_plus._util import USER_DATA_MM_PATH
19
19
 
20
20
  if TYPE_CHECKING:
21
+ from collections.abc import Iterator
22
+ from contextlib import AbstractContextManager
21
23
 
22
24
  class _MsgLogger(Protocol):
23
25
  def __call__(self, text: str, color: str = "", emoji: str = "") -> None: ...
@@ -70,7 +72,7 @@ def _get_download_name(url: str) -> str:
70
72
  return ""
71
73
 
72
74
 
73
- def _get_spinner(log_msg: _MsgLogger) -> Callable[[str], ContextManager]:
75
+ def _get_spinner(log_msg: _MsgLogger) -> Callable[[str], AbstractContextManager]:
74
76
  if log_msg is _pretty_print:
75
77
  spinner = _spinner
76
78
  else:
@@ -5,10 +5,7 @@ from contextlib import suppress
5
5
  from itertools import product
6
6
  from typing import (
7
7
  TYPE_CHECKING,
8
- Iterable,
9
- Iterator,
10
8
  NamedTuple,
11
- Sequence,
12
9
  cast,
13
10
  )
14
11
 
@@ -29,6 +26,8 @@ from pymmcore_plus.metadata import (
29
26
  from ._protocol import PMDAEngine
30
27
 
31
28
  if TYPE_CHECKING:
29
+ from collections.abc import Iterable, Iterator, Sequence
30
+
32
31
  from numpy.typing import NDArray
33
32
 
34
33
  from pymmcore_plus.core import CMMCorePlus
@@ -4,7 +4,7 @@ from abc import abstractmethod
4
4
  from typing import TYPE_CHECKING, Protocol, runtime_checkable
5
5
 
6
6
  if TYPE_CHECKING:
7
- from typing import Iterable, Iterator
7
+ from collections.abc import Iterable, Iterator
8
8
 
9
9
  from numpy.typing import NDArray
10
10
  from useq import MDAEvent, MDASequence
@@ -2,17 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import time
4
4
  import warnings
5
- from contextlib import nullcontext
5
+ from collections.abc import Iterable, Iterator, Sequence
6
+ from contextlib import AbstractContextManager, nullcontext
6
7
  from pathlib import Path
7
- from typing import (
8
- TYPE_CHECKING,
9
- Any,
10
- ContextManager,
11
- Iterable,
12
- Iterator,
13
- Sequence,
14
- Tuple,
15
- )
8
+ from typing import TYPE_CHECKING, Any
16
9
  from unittest.mock import MagicMock
17
10
 
18
11
  from useq import MDASequence
@@ -39,7 +32,7 @@ MSG = (
39
32
 
40
33
 
41
34
  class GeneratorMDASequence(MDASequence):
42
- axis_order: Tuple[str, ...] = () # noqa: UP006
35
+ axis_order: tuple[str, ...] = ()
43
36
 
44
37
  @property
45
38
  def sizes(self) -> dict[str, int]: # pragma: no cover
@@ -77,7 +70,10 @@ class MDARunner:
77
70
 
78
71
  self._canceled = False
79
72
  self._sequence: MDASequence | None = None
80
- self._reset_timer()
73
+ # timer for the full sequence, reset only once at the beginning of the sequence
74
+ self._sequence_t0: float = 0.0
75
+ # event clock, reset whenever `event.reset_event_timer` is True
76
+ self._t0: float = 0.0
81
77
 
82
78
  def set_engine(self, engine: PMDAEngine) -> PMDAEngine | None:
83
79
  """Set the [`PMDAEngine`][pymmcore_plus.mda.PMDAEngine] to use for the MDA run.""" # noqa: E501
@@ -212,11 +208,19 @@ class MDARunner:
212
208
 
213
209
  def seconds_elapsed(self) -> float:
214
210
  """Return the number of seconds since the start of the acquisition."""
211
+ return time.perf_counter() - self._sequence_t0
212
+
213
+ def event_seconds_elapsed(self) -> float:
214
+ """Return the number of seconds on the "event clock".
215
+
216
+ This is the time since either the start of the acquisition or the last
217
+ event with `reset_event_timer` set to `True`.
218
+ """
215
219
  return time.perf_counter() - self._t0
216
220
 
217
221
  def _outputs_connected(
218
222
  self, output: SingleOutput | Sequence[SingleOutput] | None
219
- ) -> ContextManager:
223
+ ) -> AbstractContextManager:
220
224
  """Context in which output handlers are connected to the frameReady signal."""
221
225
  if output is None:
222
226
  return nullcontext()
@@ -272,9 +276,12 @@ class MDARunner:
272
276
  teardown_event = getattr(engine, "teardown_event", lambda e: None)
273
277
  event_iterator = getattr(engine, "event_iterator", iter)
274
278
  _events: Iterator[MDAEvent] = event_iterator(events)
275
- self._reset_timer()
279
+ self._reset_event_timer()
280
+ self._sequence_t0 = self._t0
276
281
 
277
282
  for event in _events:
283
+ if event.reset_event_timer:
284
+ self._reset_event_timer()
278
285
  # If cancelled break out of the loop
279
286
  if self._wait_until_event(event) or not self._running:
280
287
  break
@@ -284,17 +291,18 @@ class MDARunner:
284
291
  engine.setup_event(event)
285
292
 
286
293
  try:
287
- elapsed_ms = self.seconds_elapsed() * 1000
294
+ runner_time_ms = self.seconds_elapsed() * 1000
288
295
  # this is a bit of a hack to pass the time into the engine
289
- # it is used for intra-event time calculations
296
+ # it is used for intra-event time calculations inside the engine.
290
297
  # we pop it off after the event is executed.
291
- event.metadata["runner_t0"] = self._t0
298
+ event.metadata["runner_t0"] = self._sequence_t0
292
299
  output = engine.exec_event(event) or () # in case output is None
293
300
  for payload in output:
294
301
  img, event, meta = payload
295
302
  event.metadata.pop("runner_t0", None)
303
+ # if the engine calculated its own time, don't overwrite it
296
304
  if "runner_time_ms" not in meta:
297
- meta["runner_time_ms"] = elapsed_ms
305
+ meta["runner_time_ms"] = runner_time_ms
298
306
  with exceptions_logged():
299
307
  self._signals.frameReady.emit(img, event, meta)
300
308
  finally:
@@ -321,7 +329,7 @@ class MDARunner:
321
329
  logger.info("MDA Started: %s", sequence)
322
330
  return self._engine
323
331
 
324
- def _reset_timer(self) -> None:
332
+ def _reset_event_timer(self) -> None:
325
333
  self._t0 = time.perf_counter() # reference time, in seconds
326
334
 
327
335
  def _check_canceled(self) -> bool:
@@ -374,7 +382,7 @@ class MDARunner:
374
382
  go_at = event.min_start_time + self._paused_time
375
383
  # We need to enter a loop here checking paused and canceled.
376
384
  # otherwise you'll potentially wait a long time to cancel
377
- remaining_wait_time = go_at - self.seconds_elapsed()
385
+ remaining_wait_time = go_at - self.event_seconds_elapsed()
378
386
  while remaining_wait_time > 0:
379
387
  self._signals.awaitingEvent.emit(event, remaining_wait_time)
380
388
  while self._paused and not self._canceled:
@@ -385,7 +393,7 @@ class MDARunner:
385
393
  if self._canceled:
386
394
  break
387
395
  time.sleep(min(remaining_wait_time, 0.5))
388
- remaining_wait_time = go_at - self.seconds_elapsed()
396
+ remaining_wait_time = go_at - self.event_seconds_elapsed()
389
397
 
390
398
  # check canceled again in case it was canceled
391
399
  # during the waiting loop
@@ -11,7 +11,9 @@ from pymmcore_plus._util import listeners_connected
11
11
  from .events import _get_auto_MDA_callback_class
12
12
 
13
13
  if TYPE_CHECKING:
14
- from typing import Any, ContextManager, Iterator
14
+ from collections.abc import Iterator
15
+ from contextlib import AbstractContextManager
16
+ from typing import Any
15
17
 
16
18
  from pymmcore_plus.core.events._protocol import PSignalInstance
17
19
  from pymmcore_plus.mda import PMDASignaler
@@ -24,7 +26,7 @@ def mda_listeners_connected(
24
26
  name_map: dict[str, str] | None = ...,
25
27
  asynchronous: Literal[False],
26
28
  wait_on_exit: bool = ...,
27
- ) -> ContextManager[None]: ...
29
+ ) -> AbstractContextManager[None]: ...
28
30
 
29
31
 
30
32
  @overload
@@ -34,7 +36,7 @@ def mda_listeners_connected(
34
36
  name_map: dict[str, str] | None = ...,
35
37
  asynchronous: Literal[True] = ...,
36
38
  wait_on_exit: bool = ...,
37
- ) -> ContextManager[MDARelayThread]: ...
39
+ ) -> AbstractContextManager[MDARelayThread]: ...
38
40
 
39
41
 
40
42
  @contextmanager
@@ -3,11 +3,13 @@ from __future__ import annotations
3
3
  import warnings
4
4
  from abc import abstractmethod
5
5
  from collections import defaultdict
6
- from typing import TYPE_CHECKING, Generic, Mapping, Protocol, TypeVar
6
+ from typing import TYPE_CHECKING, Generic, Protocol, TypeVar
7
7
 
8
8
  from ._util import position_sizes
9
9
 
10
10
  if TYPE_CHECKING:
11
+ from collections.abc import Mapping
12
+
11
13
  import numpy as np
12
14
  import useq
13
15
 
@@ -7,9 +7,10 @@ provided.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from collections.abc import Mapping, Sequence
10
11
  from itertools import count
11
12
  from pathlib import Path
12
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, Mapping, Sequence, cast
13
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast
13
14
 
14
15
  from pymmcore_plus.metadata.serialize import json_dumps
15
16
 
@@ -6,7 +6,7 @@ import os.path
6
6
  import shutil
7
7
  import tempfile
8
8
  from contextlib import suppress
9
- from typing import TYPE_CHECKING, Any, Literal, MutableMapping, Protocol
9
+ from typing import TYPE_CHECKING, Any, Literal, Protocol
10
10
 
11
11
  import numpy as np
12
12
 
@@ -15,8 +15,10 @@ from pymmcore_plus.metadata.serialize import to_builtins
15
15
  from ._5d_writer_base import _5DWriterBase
16
16
 
17
17
  if TYPE_CHECKING:
18
+ from collections.abc import MutableMapping, Sequence
19
+ from contextlib import AbstractAsyncContextManager
18
20
  from os import PathLike
19
- from typing import ContextManager, Sequence, TypedDict
21
+ from typing import TypedDict
20
22
 
21
23
  import xarray as xr
22
24
  import zarr
@@ -24,7 +26,7 @@ if TYPE_CHECKING:
24
26
  from numcodecs.abc import Codec
25
27
 
26
28
  class ZarrSynchronizer(Protocol):
27
- def __getitem__(self, key: str) -> ContextManager: ...
29
+ def __getitem__(self, key: str) -> AbstractAsyncContextManager: ...
28
30
 
29
31
  class ArrayCreationKwargs(TypedDict, total=False):
30
32
  compressor: str | Codec
@@ -276,9 +278,7 @@ class OMEZarrWriter(_5DWriterBase["zarr.Array"]):
276
278
  self._group.attrs["multiscales"] = scales
277
279
  ary.attrs["_ARRAY_DIMENSIONS"] = dims
278
280
  if seq := self.current_sequence:
279
- ary.attrs["useq_MDASequence"] = to_builtins(
280
- seq.model_dump(exclude_unset=True)
281
- )
281
+ ary.attrs["useq_MDASequence"] = to_builtins(seq)
282
282
 
283
283
  return ary
284
284
 
@@ -14,7 +14,8 @@ from pymmcore_plus.metadata.serialize import json_dumps, json_loads
14
14
  from ._util import position_sizes
15
15
 
16
16
  if TYPE_CHECKING:
17
- from typing import Literal, Mapping, Sequence, TypeAlias
17
+ from collections.abc import Mapping, Sequence
18
+ from typing import Literal, TypeAlias
18
19
 
19
20
  import numpy as np
20
21
  import tensorstore as ts
@@ -258,7 +259,7 @@ class TensorStoreHandler:
258
259
  # expand the sizes to include the largest size we encounter for each axis
259
260
  # in the case of positions with subsequences, we'll still end up with a
260
261
  # jagged array, but it won't take extra space, and we won't get index errors
261
- max_sizes = seq.sizes.copy()
262
+ max_sizes = dict(seq.sizes)
262
263
  for psize in position_sizes(seq):
263
264
  for k, v in psize.items():
264
265
  max_sizes[k] = max(max_sizes.get(k, 0), v)
@@ -29,7 +29,7 @@ def position_sizes(seq: useq.MDASequence) -> list[dict[str, int]]:
29
29
  `{dim: size}` pairs for each dimension in the sequence. Dimensions with no size
30
30
  will be omitted, though singletons will be included.
31
31
  """
32
- main_sizes = seq.sizes.copy()
32
+ main_sizes = dict(seq.sizes)
33
33
  main_sizes.pop("p", None) # remove position
34
34
 
35
35
  if not seq.stage_positions:
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List, Literal, Optional, Tuple, TypedDict, Union
1
+ from typing import Any, Literal, Optional, TypedDict, Union
2
2
 
3
3
  import useq
4
4
  from typing_extensions import NotRequired
@@ -17,7 +17,7 @@ __all__ = [
17
17
  "SystemInfo",
18
18
  ]
19
19
 
20
- AffineTuple = Tuple[float, float, float, float, float, float]
20
+ AffineTuple = tuple[float, float, float, float, float, float]
21
21
 
22
22
 
23
23
  class PropertyInfo(TypedDict):
@@ -53,9 +53,9 @@ class PropertyInfo(TypedDict):
53
53
  value: Optional[str]
54
54
  data_type: Literal["undefined", "float", "int", "str"]
55
55
  is_read_only: bool
56
- allowed_values: NotRequired[Tuple[str, ...]]
56
+ allowed_values: NotRequired[tuple[str, ...]]
57
57
  is_pre_init: NotRequired[bool]
58
- limits: NotRequired[Tuple[float, float]]
58
+ limits: NotRequired[tuple[float, float]]
59
59
  sequenceable: NotRequired[bool]
60
60
  sequence_max_length: NotRequired[int]
61
61
  # device_label: str
@@ -104,14 +104,14 @@ class DeviceInfo(TypedDict):
104
104
  name: str
105
105
  type: str
106
106
  description: str
107
- properties: Tuple[PropertyInfo, ...]
107
+ properties: tuple[PropertyInfo, ...]
108
108
 
109
109
  # hub devices and non-peripheral devices will have no parent_label
110
110
  parent_label: NotRequired[str]
111
111
  # state device only
112
- labels: NotRequired[Tuple[str, ...]]
112
+ labels: NotRequired[tuple[str, ...]]
113
113
  # hub device only
114
- child_names: NotRequired[Tuple[str, ...]]
114
+ child_names: NotRequired[tuple[str, ...]]
115
115
  # stage/focus device only
116
116
  is_continuous_focus_drive: NotRequired[bool]
117
117
  focus_direction: NotRequired[Literal["Unknown", "TowardSample", "AwayFromSample"]]
@@ -159,7 +159,7 @@ class SystemInfo(TypedDict):
159
159
  pymmcore_plus_version: str
160
160
  mmcore_version: str
161
161
  device_api_version: str
162
- device_adapter_search_paths: Tuple[str, ...]
162
+ device_adapter_search_paths: tuple[str, ...]
163
163
  system_configuration_file: Optional[str]
164
164
  primary_log_file: str
165
165
  sequence_buffer_size_mb: int
@@ -215,7 +215,7 @@ class ImageInfo(TypedDict):
215
215
  """ # noqa: E501
216
216
 
217
217
  camera_label: str
218
- plane_shape: Tuple[int, ...]
218
+ plane_shape: tuple[int, ...]
219
219
  dtype: str
220
220
 
221
221
  height: int
@@ -238,8 +238,8 @@ class ImageInfo(TypedDict):
238
238
  pixel_size_um: float
239
239
  magnification_factor: NotRequired[float]
240
240
  pixel_size_affine: NotRequired[AffineTuple]
241
- roi: NotRequired[Tuple[int, int, int, int]]
242
- multi_roi: NotRequired[Tuple[List[int], List[int], List[int], List[int]]]
241
+ roi: NotRequired[tuple[int, int, int, int]]
242
+ multi_roi: NotRequired[tuple[list[int], list[int], list[int], list[int]]]
243
243
 
244
244
  # # this will be != 1 for things like multi-camera device,
245
245
  # # or any "single" device adapter that manages multiple detectors, like PMTs, etc..
@@ -250,7 +250,7 @@ class StagePosition(TypedDict):
250
250
  """Represents the position of a single stage device."""
251
251
 
252
252
  device_label: str
253
- position: Union[float, Tuple[float, float]]
253
+ position: Union[float, tuple[float, float]]
254
254
 
255
255
 
256
256
  class Position(TypedDict):
@@ -276,7 +276,7 @@ class Position(TypedDict):
276
276
  x: NotRequired[float]
277
277
  y: NotRequired[float]
278
278
  z: NotRequired[float]
279
- all_stages: NotRequired[List[StagePosition]]
279
+ all_stages: NotRequired[list[StagePosition]]
280
280
 
281
281
 
282
282
  class PropertyValue(TypedDict):
@@ -312,7 +312,7 @@ class ConfigPreset(TypedDict):
312
312
  """
313
313
 
314
314
  name: str
315
- settings: Tuple[PropertyValue, ...]
315
+ settings: tuple[PropertyValue, ...]
316
316
 
317
317
 
318
318
  class PixelSizeConfigPreset(ConfigPreset):
@@ -350,7 +350,7 @@ class ConfigGroup(TypedDict):
350
350
  """
351
351
 
352
352
  name: str
353
- presets: Tuple[ConfigPreset, ...]
353
+ presets: tuple[ConfigPreset, ...]
354
354
 
355
355
 
356
356
  class SummaryMetaV1(TypedDict):
@@ -395,14 +395,14 @@ class SummaryMetaV1(TypedDict):
395
395
  format: Literal["summary-dict"]
396
396
  version: Literal["1.0"]
397
397
  datetime: NotRequired[str]
398
- devices: Tuple[DeviceInfo, ...]
398
+ devices: tuple[DeviceInfo, ...]
399
399
  system_info: SystemInfo
400
- image_infos: Tuple[ImageInfo, ...]
401
- config_groups: Tuple[ConfigGroup, ...]
402
- pixel_size_configs: Tuple[PixelSizeConfigPreset, ...]
400
+ image_infos: tuple[ImageInfo, ...]
401
+ config_groups: tuple[ConfigGroup, ...]
402
+ pixel_size_configs: tuple[PixelSizeConfigPreset, ...]
403
403
  position: Position
404
404
  mda_sequence: NotRequired[useq.MDASequence]
405
- extra: NotRequired[Dict[str, Any]]
405
+ extra: NotRequired[dict[str, Any]]
406
406
 
407
407
 
408
408
  class FrameMetaV1(TypedDict):
@@ -462,10 +462,10 @@ class FrameMetaV1(TypedDict):
462
462
  camera_device: Optional[str]
463
463
  exposure_ms: float
464
464
  position: Position
465
- property_values: Tuple[PropertyValue, ...]
465
+ property_values: tuple[PropertyValue, ...]
466
466
  runner_time_ms: float
467
467
  mda_event: NotRequired[useq.MDAEvent]
468
468
  hardware_triggered: NotRequired[bool]
469
469
  images_remaining_in_buffer: NotRequired[int]
470
- camera_metadata: NotRequired[Dict[str, Any]]
471
- extra: NotRequired[Dict[str, Any]]
470
+ camera_metadata: NotRequired[dict[str, Any]]
471
+ extra: NotRequired[dict[str, Any]]
@@ -2,10 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import sys
5
+ from collections.abc import Mapping, Sequence
5
6
  from contextlib import suppress
6
7
  from datetime import timedelta
8
+ from enum import Enum
7
9
  from types import MappingProxyType
8
- from typing import TYPE_CHECKING, Any, Mapping, Sequence
10
+ from typing import TYPE_CHECKING, Any
9
11
 
10
12
  import numpy as np
11
13
 
@@ -35,6 +37,8 @@ def encode_hook(obj: Any, raises: bool = True) -> Any:
35
37
  return obj.item()
36
38
  if isinstance(obj, timedelta):
37
39
  return obj.total_seconds()
40
+ if isinstance(obj, Enum):
41
+ return obj.value
38
42
  if raises:
39
43
  raise NotImplementedError(f"Cannot serialize object of type {type(obj)}")
40
44
  return obj
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import warnings
6
- from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence
6
+ from typing import TYPE_CHECKING, Any, Callable
7
7
 
8
8
  from pymmcore_plus import CFGCommand, DeviceType, FocusDirection, Keyword
9
9
  from pymmcore_plus._util import timestamp
@@ -15,6 +15,7 @@ from ._pixel_size_config import DEFAULT_AFFINE, PixelSizePreset
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  import io
18
+ from collections.abc import Iterable, Sequence
18
19
  from typing import TypeAlias
19
20
 
20
21
  Executor: TypeAlias = Callable[[Microscope, Sequence[str]], None]
@@ -4,7 +4,8 @@ from dataclasses import dataclass, field
4
4
  from typing import TYPE_CHECKING, NamedTuple
5
5
 
6
6
  if TYPE_CHECKING:
7
- from typing import Container, Final, MutableMapping
7
+ from collections.abc import Container, MutableMapping
8
+ from typing import Final
8
9
 
9
10
  from typing_extensions import Self # py311
10
11
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Container
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from pymmcore_plus import CMMCorePlus, DeviceType, Keyword
7
7
 
@@ -9,6 +9,8 @@ from ._device import Device
9
9
  from ._property import Property
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from collections.abc import Container
13
+
12
14
  from pymmcore_plus.model._core_link import ErrCallback
13
15
 
14
16
  CORE = Keyword.CoreDevice.value
@@ -5,8 +5,9 @@ from dataclasses import fields
5
5
  from typing import TYPE_CHECKING, Protocol
6
6
 
7
7
  if TYPE_CHECKING:
8
+ from collections.abc import Container, Iterable
8
9
  from dataclasses import Field
9
- from typing import Any, Callable, ClassVar, Container, Iterable, TypeVar
10
+ from typing import Any, Callable, ClassVar, TypeVar
10
11
 
11
12
  from typing_extensions import TypeAlias # py310
12
13
 
@@ -12,7 +12,8 @@ from ._core_link import CoreObject
12
12
  from ._property import Property
13
13
 
14
14
  if TYPE_CHECKING:
15
- from typing import Any, Callable, Container, Iterable
15
+ from collections.abc import Container, Iterable
16
+ from typing import Any, Callable
16
17
 
17
18
  from typing_extensions import (
18
19
  Self, # py311
@@ -4,7 +4,7 @@ import os
4
4
  from copy import deepcopy
5
5
  from dataclasses import dataclass, field, fields
6
6
  from pathlib import Path
7
- from typing import TYPE_CHECKING, Any, Callable, Container, Iterable
7
+ from typing import TYPE_CHECKING, Any, Callable
8
8
 
9
9
  from pymmcore_plus import DeviceType, Keyword
10
10
 
@@ -14,6 +14,8 @@ from ._device import AvailableDevice, Device, get_available_devices
14
14
  from ._pixel_size_config import PixelSizeGroup
15
15
 
16
16
  if TYPE_CHECKING:
17
+ from collections.abc import Container, Iterable
18
+
17
19
  from pymmcore_plus import CMMCorePlus
18
20
  from pymmcore_plus.metadata.schema import SummaryMetaV1
19
21
 
@@ -6,7 +6,8 @@ from typing import TYPE_CHECKING
6
6
  from ._config_group import ConfigGroup, ConfigPreset, Setting
7
7
 
8
8
  if TYPE_CHECKING:
9
- from typing import Any, Container, Final, Iterable, MutableMapping
9
+ from collections.abc import Container, Iterable, MutableMapping
10
+ from typing import Any, Final
10
11
 
11
12
  from typing_extensions import (
12
13
  Self, # py311
@@ -8,7 +8,8 @@ from pymmcore_plus import CMMCorePlus, PropertyType
8
8
  from ._core_link import CoreObject
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing import Any, Callable, Container
11
+ from collections.abc import Container
12
+ from typing import Any, Callable
12
13
 
13
14
  from typing_extensions import TypeAlias # py310
14
15
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymmcore-plus
3
- Version: 0.11.1
3
+ Version: 0.12.0
4
4
  Summary: pymmcore superset providing improved APIs, event handling, and a pure python acquisition engine
5
5
  Project-URL: Source, https://github.com/pymmcore-plus/pymmcore-plus
6
6
  Project-URL: Tracker, https://github.com/pymmcore-plus/pymmcore-plus/issues
@@ -16,7 +16,6 @@ Classifier: License :: OSI Approved :: BSD License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
@@ -24,7 +23,7 @@ Classifier: Programming Language :: Python :: 3.12
24
23
  Classifier: Topic :: System :: Hardware
25
24
  Classifier: Topic :: System :: Hardware :: Hardware Drivers
26
25
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
26
+ Requires-Python: >=3.9
28
27
  Requires-Dist: numpy>=1.17.3
29
28
  Requires-Dist: platformdirs>=3.0.0
30
29
  Requires-Dist: psygnal>=0.7
@@ -33,7 +32,7 @@ Requires-Dist: rich>=10.2.0
33
32
  Requires-Dist: tensorstore
34
33
  Requires-Dist: typer>=0.4.2
35
34
  Requires-Dist: typing-extensions
36
- Requires-Dist: useq-schema>=0.4.7
35
+ Requires-Dist: useq-schema>=0.5.0
37
36
  Requires-Dist: wrapt>=1.14
38
37
  Provides-Extra: cli
39
38
  Requires-Dist: rich>=10.2.0; extra == 'cli'
@@ -0,0 +1,59 @@
1
+ pymmcore_plus/__init__.py,sha256=y2y48MqOnY-B1h7QnJc36HRDuXEegMsPNqKq51bu8DQ,1415
2
+ pymmcore_plus/_build.py,sha256=RPTAuwCZWGL5IDJj4JZo1DIIouUsIqS3vnbPbG2_bRE,10993
3
+ pymmcore_plus/_cli.py,sha256=rtbxbru85YcbEVjpmC0-EwlTe4TUYVaUBe2Kb-ZELmk,14252
4
+ pymmcore_plus/_logger.py,sha256=YZt1ueX5mHtNGPXMqJQMrNdy46KSGlMReCOcmzjhBvQ,5153
5
+ pymmcore_plus/_util.py,sha256=49gAx6G4wzD7MuGd7mtniXbq28qCRXihDuJcRZW7chM,20278
6
+ pymmcore_plus/install.py,sha256=Vr_Z2Q9tNWEu0x-iw6HC2h1iv6K9H9tgI9aRFtckgQU,8576
7
+ pymmcore_plus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ pymmcore_plus/seq_tester.py,sha256=6PqLFHpd0SvnzYVtE80AxytiqCVtB1ULiU1myzO3G7w,3768
9
+ pymmcore_plus/core/__init__.py,sha256=r9bvL6sqgN72ZV7785WuRKT-o1VlsbYQcAKSy3rNS9k,897
10
+ pymmcore_plus/core/_adapter.py,sha256=eu2BhGe_dnoQrIsh-u3poxWXsiF2Y8pfbKIGWbUgOk8,2857
11
+ pymmcore_plus/core/_config.py,sha256=jatzHm6D-JwvlAPhvNYfzzrqXoKCpQ-EwKu6LhFeruc,10645
12
+ pymmcore_plus/core/_config_group.py,sha256=jE0TxM93VuwOa7UrwIBjMBGSSa9oTr84uEn1aMhSBIE,8499
13
+ pymmcore_plus/core/_constants.py,sha256=55lKxI38TXceowsM_Z7ShWZ-DfQiBJB_qqzaU8G9PSc,11743
14
+ pymmcore_plus/core/_device.py,sha256=Uy5A58Jj_bIY5n6OymtTJPRnYkktoCq6ZtQV4KcLwPo,7756
15
+ pymmcore_plus/core/_metadata.py,sha256=RT6dNZ9YkRlFd85hd_Vd6zLVjjbX4Cn831t6wAYDOvI,2618
16
+ pymmcore_plus/core/_mmcore_plus.py,sha256=CGtHubJhsnt8Bzd3DjcsmL8sfxixU5P19mhjnVXn5a4,87596
17
+ pymmcore_plus/core/_property.py,sha256=gO2Zy1I2y0wBAgQAVHnz1Vm7E2PYegGuLffNA1QBc6Y,8315
18
+ pymmcore_plus/core/_sequencing.py,sha256=e1INgj-UGGVG67B_eXbH11RVVwYRI7ZokVpGkXLCIbQ,11797
19
+ pymmcore_plus/core/events/__init__.py,sha256=_CIWCstUu2-xTqarK9fS_ENpZu7VxStLP4VNfcVmaBs,1094
20
+ pymmcore_plus/core/events/_device_signal_view.py,sha256=t-NfBdg3E4rms4vDFxkkR5XtrpLxaBT7mfPwkpIsbVk,1079
21
+ pymmcore_plus/core/events/_norm_slot.py,sha256=8DCBoLHGh7cbB1OB19IJYwL6sFBFmkD8IakfBOvFbw8,2907
22
+ pymmcore_plus/core/events/_prop_event_mixin.py,sha256=FvJJnpEKrOR-_Sp3-NNCwFoUUHwmNKiHruo0Y1vybsY,4042
23
+ pymmcore_plus/core/events/_protocol.py,sha256=V4st91mw6LoogII2c05vJxD5SIQU24va86J0iqJWqXU,7528
24
+ pymmcore_plus/core/events/_psygnal.py,sha256=owaKlW2zpvocXDbAW4kHovBoVv4Fjfn-S5oUJrVWsD4,1646
25
+ pymmcore_plus/core/events/_qsignals.py,sha256=gr-GDiSVLhFhSfaoKrdTz2y3I_2IUg62bYDGuGrB3j0,3018
26
+ pymmcore_plus/mda/__init__.py,sha256=NF4OReQbShxZeLFaNaLPyMwkr1e5j5zMZmzHvHeSBzE,298
27
+ pymmcore_plus/mda/_engine.py,sha256=wHbRDTdIK7G5kERMv81uBe6mYrqvuerTTM9bMlU0haE,24392
28
+ pymmcore_plus/mda/_protocol.py,sha256=10CDJ9H57oX1z0oqK3eShXyQhufHvvu3_8wdaCYpPIg,3254
29
+ pymmcore_plus/mda/_runner.py,sha256=ieSUh1st1DWvvvToaoHb01-3JCcqt7UbSx9LRGr9ehw,16256
30
+ pymmcore_plus/mda/_thread_relay.py,sha256=Ww-9gyvLEzwRhnpL1dpze71wL7IRlhH8K3Q1dmJIxgs,6193
31
+ pymmcore_plus/mda/events/__init__.py,sha256=UZFBlIzTmKqgMw_vVSZSSAN1tkAu8qccYb-aXXBRc3I,1192
32
+ pymmcore_plus/mda/events/_protocol.py,sha256=9Q7LjYOgEWQGS8gHMV97UXM9bhoVW2OeyoPyNsQbwzw,1659
33
+ pymmcore_plus/mda/events/_psygnal.py,sha256=TdN1mFGpTPXmEs9iwFKSC1svv87PDZkT2JZvl0tEGrQ,640
34
+ pymmcore_plus/mda/events/_qsignals.py,sha256=tULQg-e_NX197DxJXaWHn1zLJ-4tzc9QyOAnsobEDtA,554
35
+ pymmcore_plus/mda/handlers/_5d_writer_base.py,sha256=c9cA0n8DOBoZcy9asue5eV7jW8hFVC0XEewroFgDNHA,11925
36
+ pymmcore_plus/mda/handlers/__init__.py,sha256=yQFRVDdCyu5t2JilobHGPC8lgCY4htNF5dzctrteSZA,305
37
+ pymmcore_plus/mda/handlers/_img_sequence_writer.py,sha256=fAj6CB90RXYJ2jJIRDFxZgQb-TfwuRPWGCPFopCijRI,11549
38
+ pymmcore_plus/mda/handlers/_ome_tiff_writer.py,sha256=pqqdl3KQd0tH5Gp4rHVgYqqh2Y8iwoKRXTjwq1JLy1E,6239
39
+ pymmcore_plus/mda/handlers/_ome_zarr_writer.py,sha256=tjq7v3K3wrbPmTSlkvnEDTyMilno2sqyDkZxOjAh7FE,12275
40
+ pymmcore_plus/mda/handlers/_tensorstore_handler.py,sha256=0DpHf0SHcHgpbmlIZ2YLK3krDFcEe_aEQSk10a8WHvg,14672
41
+ pymmcore_plus/mda/handlers/_util.py,sha256=pZydpKAXtQ_gjq5x1yNK1D0hfS7NUL2nH9ivOBg4abc,1600
42
+ pymmcore_plus/metadata/__init__.py,sha256=r_2dI4qbc5GPl3MP6ye-W7-c1RBZZXkCgFqJ4HaPJOA,699
43
+ pymmcore_plus/metadata/functions.py,sha256=RsXuYS6ytIRicefRF0h7NiGfDhmimKbpgL9Vrp1p548,12188
44
+ pymmcore_plus/metadata/schema.py,sha256=Zsh7D-UptVisP_AiDcAdUDoXZsQfZNSJIrCkIgxtt5Q,17241
45
+ pymmcore_plus/metadata/serialize.py,sha256=hpXJm0tzILELf6OYECMg0sQhuf-h25ob6_DDl-TUUME,3805
46
+ pymmcore_plus/model/__init__.py,sha256=zKZkkSpNK4ERu-VMdi9gvRrj1aXAjNaYxlYB5PdYSg0,479
47
+ pymmcore_plus/model/_config_file.py,sha256=cnTF9fcclnwhnwl-qfZxSyVO75d_ljMD3CQeGrXzzMI,13280
48
+ pymmcore_plus/model/_config_group.py,sha256=vL_-EWH-Nsb8xTgFqpYIFaJzBk_RDBFchBnQ61DMSvI,3407
49
+ pymmcore_plus/model/_core_device.py,sha256=cucoGtFue71yOqC5VcT7Uvk8mY4EhPmX0pyhXsgakXE,2402
50
+ pymmcore_plus/model/_core_link.py,sha256=dsbT0gncfa3TAORSaWUrZR9rcI_nOLX9e5BTmyo-UYo,2737
51
+ pymmcore_plus/model/_device.py,sha256=ZQs6luAt2KXpDKpR2DwoHnh3izM6xWX1ckIxFhpHP9U,15748
52
+ pymmcore_plus/model/_microscope.py,sha256=69VV6cuevinOK_LhYEkQygHGesvCZefdn9YNt3mV618,11353
53
+ pymmcore_plus/model/_pixel_size_config.py,sha256=smoOmT54nSkg52RaSQzTFG0YwyMR_SEq_lkS-JyJW9U,3514
54
+ pymmcore_plus/model/_property.py,sha256=fl2wUldY3H9qK9aoIcZLJCr7wDcxOFLoIjvR9rHnAnk,3359
55
+ pymmcore_plus-0.12.0.dist-info/METADATA,sha256=nvAEtFjaYJkMQrJY657Fgd5ouJzee72mCfKrSTc9BXU,9221
56
+ pymmcore_plus-0.12.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
57
+ pymmcore_plus-0.12.0.dist-info/entry_points.txt,sha256=NtFyndrQzBpUNJyil-8e5hMGke2utAf7mkGavTLcLOY,51
58
+ pymmcore_plus-0.12.0.dist-info/licenses/LICENSE,sha256=OHJjRpOPKKRc7FEnpehNWdR5LRBdBhUtIFG-ZI0dCEA,1522
59
+ pymmcore_plus-0.12.0.dist-info/RECORD,,
@@ -1,59 +0,0 @@
1
- pymmcore_plus/__init__.py,sha256=y2y48MqOnY-B1h7QnJc36HRDuXEegMsPNqKq51bu8DQ,1415
2
- pymmcore_plus/_build.py,sha256=PU6rm_4l-SGMurPQT5RuloUMkrgqKTI0Y9ePbPmWOmo,10928
3
- pymmcore_plus/_cli.py,sha256=m6OEbKM90lJ6v5429cbsIultyB86iqXaDN0hSSyj810,14258
4
- pymmcore_plus/_logger.py,sha256=IQl--kuEQqUwV9C4P1sY-7J5IW6V7q45wsi2NbfnAbM,5088
5
- pymmcore_plus/_util.py,sha256=mNabztc90ck1rN_15RtaYjk6acyP2azSJGzMkyeJELs,20247
6
- pymmcore_plus/install.py,sha256=a85-KCifBetKSQXwH1DqEkz7il5iqFcl9LR0qFIVvbk,8503
7
- pymmcore_plus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- pymmcore_plus/seq_tester.py,sha256=6PqLFHpd0SvnzYVtE80AxytiqCVtB1ULiU1myzO3G7w,3768
9
- pymmcore_plus/core/__init__.py,sha256=r9bvL6sqgN72ZV7785WuRKT-o1VlsbYQcAKSy3rNS9k,897
10
- pymmcore_plus/core/_adapter.py,sha256=eu2BhGe_dnoQrIsh-u3poxWXsiF2Y8pfbKIGWbUgOk8,2857
11
- pymmcore_plus/core/_config.py,sha256=5Vzwv2QEuJSXgKjERSHiTzwmpWyizY_aa4n3OjpPyPc,10620
12
- pymmcore_plus/core/_config_group.py,sha256=w-psUtMk3X6MzqU17j4D2yZ_ugLP7u0-mvPIXPuIxwQ,8472
13
- pymmcore_plus/core/_constants.py,sha256=55lKxI38TXceowsM_Z7ShWZ-DfQiBJB_qqzaU8G9PSc,11743
14
- pymmcore_plus/core/_device.py,sha256=Uy5A58Jj_bIY5n6OymtTJPRnYkktoCq6ZtQV4KcLwPo,7756
15
- pymmcore_plus/core/_metadata.py,sha256=64RdptyRGRqtRJ8pWMlAyevSgYMAE1Hzs8TB3DBQHAA,2618
16
- pymmcore_plus/core/_mmcore_plus.py,sha256=twgVOJ3yF67ljvcWOtU0HexuLCNpQlqsN9yxIqwqdqI,87569
17
- pymmcore_plus/core/_property.py,sha256=sd28rZE-_l4HaIJxqJlUX7DlM-Fa9_Xr6cLERwOtBYc,8283
18
- pymmcore_plus/core/_sequencing.py,sha256=ueyuqsUPbfH7FLMdqFUpHoxSZfBRHHkYmv12lOt72hI,11847
19
- pymmcore_plus/core/events/__init__.py,sha256=8NjFkVdRVRiiLUQ_brGQSlTaK3rqNizdNEX17NEXAis,1106
20
- pymmcore_plus/core/events/_device_signal_view.py,sha256=st_di51xOmKKOM9-yY8ouKlTZqG-7NWorRaoaN1sJ44,1065
21
- pymmcore_plus/core/events/_norm_slot.py,sha256=cp6VeH5h98G7bPWrKzTsHJmzCIbi2D4sv6bSdywQbsk,2937
22
- pymmcore_plus/core/events/_prop_event_mixin.py,sha256=XfoXrMjRRXPHv6XkavBmSuz-6nItpJ8oG60DqK2WBEA,3939
23
- pymmcore_plus/core/events/_protocol.py,sha256=Cf9uGZe_uP8nIa8rsaDIX5RCW5pNQQt2juLL-rriRn4,7448
24
- pymmcore_plus/core/events/_psygnal.py,sha256=owaKlW2zpvocXDbAW4kHovBoVv4Fjfn-S5oUJrVWsD4,1646
25
- pymmcore_plus/core/events/_qsignals.py,sha256=gr-GDiSVLhFhSfaoKrdTz2y3I_2IUg62bYDGuGrB3j0,3018
26
- pymmcore_plus/mda/__init__.py,sha256=NF4OReQbShxZeLFaNaLPyMwkr1e5j5zMZmzHvHeSBzE,298
27
- pymmcore_plus/mda/_engine.py,sha256=gN_F9iJyEzHCIBVBYUCL6fnRlbBcDbpR8cmkwK5d_1I,24372
28
- pymmcore_plus/mda/_protocol.py,sha256=vuotAc2P1zl2EX4wGjfaasarQHNqtF5UpOwPtUqHG2Y,3245
29
- pymmcore_plus/mda/_runner.py,sha256=edgRQo-YRzF5f2VEVjATsTV70wUjuRcUFF5XtRLgguE,15498
30
- pymmcore_plus/mda/_thread_relay.py,sha256=wjP1tag7nN2Sr0RzaPnYRqHl8XjAQg2MpXOt0ONLcQ8,6112
31
- pymmcore_plus/mda/events/__init__.py,sha256=UZFBlIzTmKqgMw_vVSZSSAN1tkAu8qccYb-aXXBRc3I,1192
32
- pymmcore_plus/mda/events/_protocol.py,sha256=9Q7LjYOgEWQGS8gHMV97UXM9bhoVW2OeyoPyNsQbwzw,1659
33
- pymmcore_plus/mda/events/_psygnal.py,sha256=TdN1mFGpTPXmEs9iwFKSC1svv87PDZkT2JZvl0tEGrQ,640
34
- pymmcore_plus/mda/events/_qsignals.py,sha256=tULQg-e_NX197DxJXaWHn1zLJ-4tzc9QyOAnsobEDtA,554
35
- pymmcore_plus/mda/handlers/_5d_writer_base.py,sha256=myFcpj_uqoBkYb_1JFB0GjmRhFxW-8jyZAhqoO8CRfo,11893
36
- pymmcore_plus/mda/handlers/__init__.py,sha256=yQFRVDdCyu5t2JilobHGPC8lgCY4htNF5dzctrteSZA,305
37
- pymmcore_plus/mda/handlers/_img_sequence_writer.py,sha256=bOnw9htxovIqUhJCoJml5YayaWD30IN2uuB_TijNVHk,11522
38
- pymmcore_plus/mda/handlers/_ome_tiff_writer.py,sha256=pqqdl3KQd0tH5Gp4rHVgYqqh2Y8iwoKRXTjwq1JLy1E,6239
39
- pymmcore_plus/mda/handlers/_ome_zarr_writer.py,sha256=ReJW8bnk-vQk4J1Zd66swkQATg17uV_hzpoBosUWuP4,12253
40
- pymmcore_plus/mda/handlers/_tensorstore_handler.py,sha256=gweY2WoXgjPmiQbfTgL3TwB4b-u8cjlrlWEr-5n84OM,14642
41
- pymmcore_plus/mda/handlers/_util.py,sha256=p-8Gg5Q2Jo0zyYdniP9a0NirUOnuKNLWzwjhzcKqskg,1601
42
- pymmcore_plus/metadata/__init__.py,sha256=r_2dI4qbc5GPl3MP6ye-W7-c1RBZZXkCgFqJ4HaPJOA,699
43
- pymmcore_plus/metadata/functions.py,sha256=RsXuYS6ytIRicefRF0h7NiGfDhmimKbpgL9Vrp1p548,12188
44
- pymmcore_plus/metadata/schema.py,sha256=bP0SMwMfvgTfmTKMIWveAXbw5fDnE18kmFYu-FLenAM,17260
45
- pymmcore_plus/metadata/serialize.py,sha256=XB-epU7-bJfmP6HItBH9t0-lOsJuW7xSFjc5y3mO1Jg,3701
46
- pymmcore_plus/model/__init__.py,sha256=zKZkkSpNK4ERu-VMdi9gvRrj1aXAjNaYxlYB5PdYSg0,479
47
- pymmcore_plus/model/_config_file.py,sha256=46xdrVLa193uNbQxq0puzWYDcfFIqRAw6M8v9TVu3U4,13249
48
- pymmcore_plus/model/_config_group.py,sha256=O9DzPyKe2Q3iInsQNbDsQfJeCWVcnzgMHGD_zXTMBw0,3376
49
- pymmcore_plus/model/_core_device.py,sha256=afNYGIRFunUPYNYJ_yHJwOjo7HO9FW8qGWTMeiKRmNU,2370
50
- pymmcore_plus/model/_core_link.py,sha256=myE0qa2pEWEinbsIhTN4uB_c-lr9dtUG3npLgsHEI_0,2706
51
- pymmcore_plus/model/_device.py,sha256=IuSTdxR6OwGnmhTgV77QDEwIwuZOLbmU-gCnSdd2H-c,15717
52
- pymmcore_plus/model/_microscope.py,sha256=EFhSUBdnVcwZHRFxcTk346Ha0o9WZyjsQzpk1k0JCwc,11321
53
- pymmcore_plus/model/_pixel_size_config.py,sha256=eoEQrXqOKaRBcdLtaTX15rIWMF3QJ1fNgTrgXEFfvBs,3483
54
- pymmcore_plus/model/_property.py,sha256=gJM7SFjLB2HnN0E8HOn4qVlB2wAxmkEFxSJFjKauZEk,3328
55
- pymmcore_plus-0.11.1.dist-info/METADATA,sha256=b8a-4eD6Cn6UhAzBUibyCjSK23_7A422cK3bcXcxXRI,9271
56
- pymmcore_plus-0.11.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
57
- pymmcore_plus-0.11.1.dist-info/entry_points.txt,sha256=NtFyndrQzBpUNJyil-8e5hMGke2utAf7mkGavTLcLOY,51
58
- pymmcore_plus-0.11.1.dist-info/licenses/LICENSE,sha256=OHJjRpOPKKRc7FEnpehNWdR5LRBdBhUtIFG-ZI0dCEA,1522
59
- pymmcore_plus-0.11.1.dist-info/RECORD,,