pymmcore-plus 0.15.4__py3-none-any.whl → 0.17.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 (35) hide show
  1. pymmcore_plus/__init__.py +20 -1
  2. pymmcore_plus/_accumulator.py +23 -5
  3. pymmcore_plus/_cli.py +44 -26
  4. pymmcore_plus/_discovery.py +344 -0
  5. pymmcore_plus/_ipy_completion.py +1 -1
  6. pymmcore_plus/_logger.py +3 -3
  7. pymmcore_plus/_util.py +9 -245
  8. pymmcore_plus/core/_device.py +57 -13
  9. pymmcore_plus/core/_mmcore_plus.py +20 -23
  10. pymmcore_plus/core/_property.py +35 -29
  11. pymmcore_plus/core/_sequencing.py +2 -0
  12. pymmcore_plus/core/events/_device_signal_view.py +8 -1
  13. pymmcore_plus/experimental/simulate/__init__.py +88 -0
  14. pymmcore_plus/experimental/simulate/_objects.py +670 -0
  15. pymmcore_plus/experimental/simulate/_render.py +510 -0
  16. pymmcore_plus/experimental/simulate/_sample.py +156 -0
  17. pymmcore_plus/experimental/unicore/__init__.py +2 -0
  18. pymmcore_plus/experimental/unicore/_device_manager.py +46 -13
  19. pymmcore_plus/experimental/unicore/core/_config.py +706 -0
  20. pymmcore_plus/experimental/unicore/core/_unicore.py +834 -18
  21. pymmcore_plus/experimental/unicore/devices/_device_base.py +13 -0
  22. pymmcore_plus/experimental/unicore/devices/_hub.py +50 -0
  23. pymmcore_plus/experimental/unicore/devices/_stage.py +46 -1
  24. pymmcore_plus/experimental/unicore/devices/_state.py +6 -0
  25. pymmcore_plus/install.py +149 -18
  26. pymmcore_plus/mda/_engine.py +268 -73
  27. pymmcore_plus/mda/handlers/_5d_writer_base.py +16 -5
  28. pymmcore_plus/mda/handlers/_tensorstore_handler.py +7 -1
  29. pymmcore_plus/metadata/_ome.py +553 -0
  30. pymmcore_plus/metadata/functions.py +2 -1
  31. {pymmcore_plus-0.15.4.dist-info → pymmcore_plus-0.17.0.dist-info}/METADATA +7 -4
  32. {pymmcore_plus-0.15.4.dist-info → pymmcore_plus-0.17.0.dist-info}/RECORD +35 -27
  33. {pymmcore_plus-0.15.4.dist-info → pymmcore_plus-0.17.0.dist-info}/WHEEL +1 -1
  34. {pymmcore_plus-0.15.4.dist-info → pymmcore_plus-0.17.0.dist-info}/entry_points.txt +0 -0
  35. {pymmcore_plus-0.15.4.dist-info → pymmcore_plus-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
23
23
  class PyDeviceManager:
24
24
  """Manages loaded Python devices."""
25
25
 
26
- __slots__ = ("_devices",)
26
+ __slots__ = ("_devices", "_executor")
27
27
 
28
28
  def __init__(self) -> None:
29
29
  self._devices: dict[str, Device] = {}
@@ -53,9 +53,8 @@ class PyDeviceManager:
53
53
 
54
54
  # Initialize all devices in parallel
55
55
  with ThreadPoolExecutor() as executor:
56
- for future in as_completed(
57
- executor.submit(self.initialize, label) for label in labels
58
- ):
56
+ futures = [executor.submit(self.initialize, label) for label in labels]
57
+ for future in as_completed(futures):
59
58
  future.result()
60
59
 
61
60
  def wait_for(
@@ -75,17 +74,25 @@ class PyDeviceManager:
75
74
  )
76
75
  time.sleep(polling_interval)
77
76
 
78
- def wait_for_device_type(self, dev_type: int, timeout_ms: float = 5000) -> None:
77
+ def wait_for_device_type(
78
+ self, dev_type: int, timeout_ms: float = 5000, *, parallel: bool = True
79
+ ) -> None:
79
80
  if not (labels := self.get_labels_of_type(dev_type)):
80
81
  return # pragma: no cover
81
-
82
- # Wait for all python devices of the given type in parallel
83
- with ThreadPoolExecutor() as executor:
84
- futures = (
85
- executor.submit(self.wait_for, lbl, timeout_ms) for lbl in labels
86
- )
87
- for future in as_completed(futures):
88
- future.result() # Raises any exceptions from wait_for_device
82
+ if not parallel:
83
+ for lbl in labels:
84
+ self.wait_for(lbl, timeout_ms)
85
+ else:
86
+ # Wait for all python devices of the given type in parallel
87
+ # it's critical that this be a list comprehension,
88
+ # not a generator expression, otherwise the executor may be shut down
89
+ # before any tasks are actually submitted
90
+ with ThreadPoolExecutor() as executor:
91
+ futures = [
92
+ executor.submit(self.wait_for, lbl, timeout_ms) for lbl in labels
93
+ ]
94
+ for future in as_completed(futures):
95
+ future.result() # Raises any exceptions from wait_for_device
89
96
 
90
97
  def get_initialization_state(self, label: str) -> DeviceInitializationState:
91
98
  """Return the initialization state of the device with the given label."""
@@ -171,3 +178,29 @@ class PyDeviceManager:
171
178
  for label, device in self._devices.items()
172
179
  if dev_type == DeviceType.Any or device.type() == dev_type
173
180
  )
181
+
182
+ def get_loaded_peripherals(self, hub_label: str) -> tuple[DeviceLabel, ...]:
183
+ """Get labels of all loaded devices whose parent is the given hub.
184
+
185
+ Parameters
186
+ ----------
187
+ hub_label : str
188
+ The label of the hub device.
189
+
190
+ Returns
191
+ -------
192
+ tuple[DeviceLabel, ...]
193
+ Labels of all loaded devices that have this hub as their parent.
194
+ """
195
+ # Verify the hub exists and is actually a hub device
196
+ if hub_label not in self._devices:
197
+ return ()
198
+ hub_device = self._devices[hub_label]
199
+ if hub_device.type() != DeviceType.Hub:
200
+ return ()
201
+
202
+ return tuple(
203
+ cast("DeviceLabel", label)
204
+ for label, device in self._devices.items()
205
+ if device.get_parent_label() == hub_label
206
+ )