pymmcore-plus 0.15.4__tar.gz → 0.17.0__tar.gz
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.
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/PKG-INFO +7 -4
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/pyproject.toml +16 -6
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/__init__.py +20 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_accumulator.py +23 -5
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_cli.py +44 -26
- pymmcore_plus-0.17.0/src/pymmcore_plus/_discovery.py +344 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_ipy_completion.py +1 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_logger.py +3 -3
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_util.py +9 -245
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_device.py +57 -13
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_mmcore_plus.py +20 -23
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_property.py +35 -29
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_sequencing.py +2 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_device_signal_view.py +8 -1
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/simulate/__init__.py +88 -0
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/simulate/_objects.py +670 -0
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/simulate/_render.py +510 -0
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/simulate/_sample.py +156 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/__init__.py +2 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/_device_manager.py +46 -13
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/unicore/core/_config.py +706 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/core/_unicore.py +834 -18
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_device_base.py +13 -0
- pymmcore_plus-0.17.0/src/pymmcore_plus/experimental/unicore/devices/_hub.py +50 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_stage.py +46 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_state.py +6 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/install.py +149 -18
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/_engine.py +268 -73
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_5d_writer_base.py +16 -5
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_tensorstore_handler.py +7 -1
- pymmcore_plus-0.17.0/src/pymmcore_plus/metadata/_ome.py +553 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/metadata/functions.py +2 -1
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_camera.py +1 -1
- pymmcore_plus-0.17.0/tests/00_unicore/test_pydevice_config.py +249 -0
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_slm.py +1 -1
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_state.py +35 -0
- pymmcore_plus-0.17.0/tests/00_unicore/test_unicore.py +1190 -0
- pymmcore_plus-0.17.0/tests/00_unicore/test_z_stage.py +86 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_accumulators.py +1 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_cli.py +21 -15
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_config_group_class.py +1 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_core.py +4 -4
- pymmcore_plus-0.17.0/tests/test_core_references.py +194 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_device_class.py +98 -2
- pymmcore_plus-0.17.0/tests/test_install.py +171 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_ipy_completions.py +5 -5
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_mda.py +119 -2
- pymmcore_plus-0.17.0/tests/test_metadata_to_ome.py +354 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_model.py +9 -1
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_sequencing.py +19 -1
- pymmcore_plus-0.17.0/tests/test_simulate.py +552 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_slm_image.py +1 -1
- pymmcore_plus-0.15.4/tests/unicore/test_unicore.py +0 -291
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/.gitignore +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/LICENSE +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/README.md +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_benchmark.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_build.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/_pymmcore.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_adapter.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_config.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_config_group.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_constants.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/_metadata.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_deprecated.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_norm_slot.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_prop_event_mixin.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_protocol.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_psygnal.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/core/events/_qsignals.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/_proxy.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/core/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/core/_sequence_buffer.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_camera.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_generic_device.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_properties.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_shutter.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/experimental/unicore/devices/_slm.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/_protocol.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/_runner.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/_thread_relay.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/events/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/events/_protocol.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/events/_psygnal.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/events/_qsignals.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_img_sequence_writer.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_ome_tiff_writer.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_ome_zarr_writer.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mda/handlers/_util.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/metadata/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/metadata/schema.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/metadata/serialize.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/mocks.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_config_file.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_config_group.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_core_device.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_core_link.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_device.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_microscope.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_pixel_size_config.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/model/_property.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/py.typed +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/src/pymmcore_plus/seq_tester.py +0 -0
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/conftest.py +0 -0
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_sequence_buffer.py +0 -0
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_shutter.py +0 -0
- {pymmcore_plus-0.15.4/tests/unicore → pymmcore_plus-0.17.0/tests/00_unicore}/test_xy_stage.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/__init__.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/conftest.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/io/test_image_sequence_writer.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/io/test_ome_tiff.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/io/test_zarr_writers.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/local_config.cfg +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_adapter_class.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_bench.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_events.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_metadata.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_misc.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_pixel_config_class.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_property_class.py +0 -0
- {pymmcore_plus-0.15.4 → pymmcore_plus-0.17.0}/tests/test_thread_relay.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymmcore-plus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.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
|
|
@@ -28,18 +28,19 @@ Requires-Python: >=3.9
|
|
|
28
28
|
Requires-Dist: numpy>=1.25.2
|
|
29
29
|
Requires-Dist: numpy>=1.26.0; python_version >= '3.12'
|
|
30
30
|
Requires-Dist: numpy>=2.1.0; python_version >= '3.13'
|
|
31
|
+
Requires-Dist: ome-types>=0.6.0
|
|
31
32
|
Requires-Dist: platformdirs>=3.0.0
|
|
32
33
|
Requires-Dist: psygnal>=0.10
|
|
33
|
-
Requires-Dist: pymmcore>=11.
|
|
34
|
+
Requires-Dist: pymmcore>=11.10.0.74.0
|
|
34
35
|
Requires-Dist: rich>=10.2.0
|
|
35
36
|
Requires-Dist: tensorstore!=0.1.72,>=0.1.67
|
|
36
37
|
Requires-Dist: tensorstore!=0.1.72,>=0.1.71; python_version >= '3.13'
|
|
37
|
-
Requires-Dist: typer>=0.
|
|
38
|
+
Requires-Dist: typer>=0.13.0
|
|
38
39
|
Requires-Dist: typing-extensions>=4
|
|
39
40
|
Requires-Dist: useq-schema>=0.7.2
|
|
40
41
|
Provides-Extra: cli
|
|
41
42
|
Requires-Dist: rich>=10.2.0; extra == 'cli'
|
|
42
|
-
Requires-Dist: typer>=0.
|
|
43
|
+
Requires-Dist: typer>=0.13.0; extra == 'cli'
|
|
43
44
|
Provides-Extra: io
|
|
44
45
|
Requires-Dist: tifffile>=2021.6.14; extra == 'io'
|
|
45
46
|
Requires-Dist: zarr<3,>=2.15; extra == 'io'
|
|
@@ -51,6 +52,8 @@ Provides-Extra: pyside2
|
|
|
51
52
|
Requires-Dist: pyside2>=5.15.2.1; extra == 'pyside2'
|
|
52
53
|
Provides-Extra: pyside6
|
|
53
54
|
Requires-Dist: pyside6==6.7.3; extra == 'pyside6'
|
|
55
|
+
Provides-Extra: simulate
|
|
56
|
+
Requires-Dist: pillow>=11.0; extra == 'simulate'
|
|
54
57
|
Description-Content-Type: text/markdown
|
|
55
58
|
|
|
56
59
|
# pymmcore-plus
|
|
@@ -40,27 +40,30 @@ dependencies = [
|
|
|
40
40
|
"numpy >=1.26.0; python_version >= '3.12'",
|
|
41
41
|
"numpy >=1.25.2",
|
|
42
42
|
"psygnal >=0.10",
|
|
43
|
-
"pymmcore >=11.
|
|
43
|
+
"pymmcore >=11.10.0.74.0",
|
|
44
44
|
"typing-extensions >=4", # not actually required at runtime
|
|
45
45
|
"useq-schema >=0.7.2",
|
|
46
46
|
"tensorstore >=0.1.71,!=0.1.72; python_version >= '3.13'",
|
|
47
47
|
"tensorstore >=0.1.67,!=0.1.72",
|
|
48
48
|
# cli requirements included by default for now
|
|
49
|
-
"typer >=0.
|
|
49
|
+
"typer >=0.13.0",
|
|
50
50
|
"rich >=10.2.0",
|
|
51
|
+
"ome-types >=0.6.0",
|
|
51
52
|
]
|
|
52
53
|
|
|
53
54
|
# extras
|
|
54
55
|
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies
|
|
55
56
|
[project.optional-dependencies]
|
|
56
|
-
cli = ["typer >=0.
|
|
57
|
+
cli = ["typer >=0.13.0", "rich >=10.2.0"]
|
|
57
58
|
io = ["tifffile >=2021.6.14", "zarr >=2.15,<3"]
|
|
59
|
+
simulate = ["pillow >=11.0"]
|
|
58
60
|
PySide2 = ["PySide2 >=5.15.2.1"]
|
|
59
61
|
PySide6 = ["PySide6 ==6.7.3"]
|
|
60
62
|
PyQt5 = ["PyQt5 >=5.15.4"]
|
|
61
63
|
PyQt6 = ["PyQt6 >=6.4.2"]
|
|
62
64
|
|
|
63
65
|
[dependency-groups]
|
|
66
|
+
nano = ["pymmcore-nano==11.10.0.74.0"]
|
|
64
67
|
docs = [
|
|
65
68
|
"mkdocs >=1.4",
|
|
66
69
|
"mkdocs-material>=9.5",
|
|
@@ -70,13 +73,14 @@ docs = [
|
|
|
70
73
|
"mkdocs-typer ==0.0.3",
|
|
71
74
|
]
|
|
72
75
|
test = [
|
|
73
|
-
"pymmcore-plus[io]",
|
|
76
|
+
"pymmcore-plus[io,simulate]",
|
|
74
77
|
"msgspec >= 0.19",
|
|
75
78
|
"msgpack >=1",
|
|
76
|
-
"pytest-cov >=
|
|
79
|
+
"pytest-cov >=7",
|
|
77
80
|
"ipython>=8.18.0",
|
|
78
81
|
"pytest >=8",
|
|
79
82
|
"xarray >=2024.1",
|
|
83
|
+
"lxml >=6.0",
|
|
80
84
|
]
|
|
81
85
|
test-codspeed = [{ include-group = "test" }, "pytest-codspeed >=3.2.0"]
|
|
82
86
|
test-qt = [{ include-group = 'test' }, "pytest-qt ==4.4", "qtpy >=2"]
|
|
@@ -92,6 +96,8 @@ dev = [
|
|
|
92
96
|
"pre-commit>=4.1.0",
|
|
93
97
|
"ruff>=0.9.4",
|
|
94
98
|
"pydantic >2.7.4; python_version >= '3.13'",
|
|
99
|
+
"opencv-python>=4.11.0.86",
|
|
100
|
+
"pytest-xdist>=3.8.0",
|
|
95
101
|
]
|
|
96
102
|
|
|
97
103
|
[tool.uv.sources]
|
|
@@ -173,6 +179,9 @@ filterwarnings = [
|
|
|
173
179
|
"error",
|
|
174
180
|
"ignore:Failed to disconnect::pytestqt",
|
|
175
181
|
"ignore:numpy.core.multiarray is deprecated",
|
|
182
|
+
"ignore:Focus direction is unknown",
|
|
183
|
+
"ignore:'BaseCommand' is deprecated:DeprecationWarning:typer",
|
|
184
|
+
"ignore:Failing to pass a value to the 'type_params' parameter",
|
|
176
185
|
]
|
|
177
186
|
markers = ["run_last: mark a test to run last"]
|
|
178
187
|
|
|
@@ -200,6 +209,7 @@ exclude_lines = [
|
|
|
200
209
|
"if TYPE_CHECKING:",
|
|
201
210
|
"@overload",
|
|
202
211
|
"except ImportError",
|
|
212
|
+
"except Exception as e:", # ok not to cover bare exception catches in tests
|
|
203
213
|
"raise AssertionError",
|
|
204
214
|
"\\.\\.\\.",
|
|
205
215
|
"if __name__ == .__main__.:",
|
|
@@ -222,4 +232,4 @@ ignore = [
|
|
|
222
232
|
]
|
|
223
233
|
|
|
224
234
|
[tool.typos.default]
|
|
225
|
-
extend-ignore-identifiers-re = ["(?i)nd2?.*", "(?i)ome", "anager", "ba"]
|
|
235
|
+
extend-ignore-identifiers-re = ["(?i)nd2?.*", "(?i)ome", "anager", "ba", "(?:FOVs?)"]
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from importlib.metadata import PackageNotFoundError, version
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
5
6
|
|
|
6
7
|
try:
|
|
7
8
|
__version__ = version("pymmcore-plus")
|
|
8
9
|
except PackageNotFoundError: # pragma: no cover
|
|
9
10
|
__version__ = "unknown"
|
|
10
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ._ipy_completion import install_pymmcore_ipy_completion
|
|
11
14
|
|
|
12
15
|
from ._accumulator import AbstractChangeAccumulator
|
|
16
|
+
from ._discovery import find_micromanager, use_micromanager
|
|
13
17
|
from ._logger import configure_logging
|
|
14
|
-
from ._util import find_micromanager, use_micromanager
|
|
15
18
|
from .core import (
|
|
16
19
|
ActionType,
|
|
17
20
|
CFGCommand,
|
|
@@ -63,10 +66,26 @@ __all__ = [
|
|
|
63
66
|
"__version__",
|
|
64
67
|
"configure_logging",
|
|
65
68
|
"find_micromanager",
|
|
69
|
+
"install_pymmcore_ipy_completion",
|
|
66
70
|
"use_micromanager",
|
|
67
71
|
]
|
|
68
72
|
|
|
69
73
|
|
|
74
|
+
def __getattr__(name: str) -> Any:
|
|
75
|
+
"""Lazy import for compatibility with pymmcore."""
|
|
76
|
+
if name == "install_pymmcore_ipy_completion":
|
|
77
|
+
try:
|
|
78
|
+
from ._ipy_completion import install_pymmcore_ipy_completion
|
|
79
|
+
except ImportError as e: # pragma: no cover
|
|
80
|
+
raise ImportError(
|
|
81
|
+
f"Error importing IPython completion for pymmcore-plus: {e}"
|
|
82
|
+
) from None
|
|
83
|
+
|
|
84
|
+
return install_pymmcore_ipy_completion
|
|
85
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'. ")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# install the IPython completer when imported, if running in an IPython environment
|
|
70
89
|
def _install_ipy_completer() -> None: # pragma: no cover
|
|
71
90
|
import os
|
|
72
91
|
import sys
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import abc
|
|
6
6
|
import sys
|
|
7
|
+
import weakref
|
|
7
8
|
from abc import ABC, abstractmethod
|
|
8
9
|
from collections.abc import Sequence
|
|
9
10
|
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, TypeVar
|
|
@@ -166,9 +167,10 @@ class DeviceAccumulator(abc.ABC, Generic[DT]):
|
|
|
166
167
|
mmcore: CMMCorePlus | None = None,
|
|
167
168
|
**kwargs: Any,
|
|
168
169
|
) -> None:
|
|
169
|
-
|
|
170
|
+
core = mmcore or CMMCorePlus.instance()
|
|
171
|
+
self._mmcore_ref = weakref.ref(core)
|
|
170
172
|
dev_type = self._device_type()
|
|
171
|
-
if not
|
|
173
|
+
if not core.getDeviceType(device_label) == dev_type: # pragma: no cover
|
|
172
174
|
raise ValueError(
|
|
173
175
|
f"Cannot create {self.__class__.__name__}. "
|
|
174
176
|
f"Device {device_label!r} is not a {dev_type.name}. "
|
|
@@ -177,6 +179,16 @@ class DeviceAccumulator(abc.ABC, Generic[DT]):
|
|
|
177
179
|
self._device_label = device_label
|
|
178
180
|
super().__init__(**kwargs)
|
|
179
181
|
|
|
182
|
+
@property
|
|
183
|
+
def _mmcore(self) -> CMMCorePlus:
|
|
184
|
+
"""Get the CMMCorePlus instance for this accumulator."""
|
|
185
|
+
if (mmcore := self._mmcore_ref()) is None: # pragma: no cover
|
|
186
|
+
raise RuntimeError(
|
|
187
|
+
f"The CMMCorePlus instance for this {self.__class__.__name__!r} has "
|
|
188
|
+
"been garbage collected."
|
|
189
|
+
)
|
|
190
|
+
return mmcore
|
|
191
|
+
|
|
180
192
|
def _is_busy(self) -> bool:
|
|
181
193
|
return self._mmcore.deviceBusy(self._device_label)
|
|
182
194
|
|
|
@@ -204,18 +216,24 @@ class DeviceAccumulator(abc.ABC, Generic[DT]):
|
|
|
204
216
|
device_type = mmcore.getDeviceType(device)
|
|
205
217
|
if cache_key not in DeviceAccumulator._CACHE:
|
|
206
218
|
if device_type == cls._device_type():
|
|
207
|
-
|
|
219
|
+
DeviceAccumulator._CACHE[cache_key] = cls(
|
|
220
|
+
device_label=device, mmcore=mmcore
|
|
221
|
+
)
|
|
208
222
|
else:
|
|
209
223
|
for sub in cls.__subclasses__():
|
|
210
224
|
if sub._device_type() == device_type: # noqa: SLF001
|
|
211
|
-
|
|
225
|
+
DeviceAccumulator._CACHE[cache_key] = sub(
|
|
226
|
+
device_label=device, mmcore=mmcore
|
|
227
|
+
)
|
|
212
228
|
break
|
|
213
229
|
else:
|
|
214
230
|
raise ValueError(
|
|
215
231
|
"No matching DeviceTypeMixin subclass found for device type "
|
|
216
232
|
f"{device_type.name} (for device {device!r})."
|
|
217
233
|
)
|
|
218
|
-
|
|
234
|
+
|
|
235
|
+
weakref.finalize(mmcore, DeviceAccumulator._CACHE.pop, cache_key, None)
|
|
236
|
+
obj = DeviceAccumulator._CACHE[cache_key]
|
|
219
237
|
if not isinstance(obj, cls):
|
|
220
238
|
raise TypeError(
|
|
221
239
|
f"Cannot create {cls.__name__} for {device!r}. "
|
|
@@ -7,9 +7,9 @@ import sys
|
|
|
7
7
|
import time
|
|
8
8
|
from contextlib import suppress
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from platform import system
|
|
10
11
|
from typing import Optional, Union, cast
|
|
11
12
|
|
|
12
|
-
from pymmcore_plus._util import get_device_interface_version
|
|
13
13
|
from pymmcore_plus.core._device import Device
|
|
14
14
|
from pymmcore_plus.core._mmcore_plus import CMMCorePlus
|
|
15
15
|
|
|
@@ -26,9 +26,9 @@ import pymmcore_plus
|
|
|
26
26
|
from pymmcore_plus._build import DEFAULT_PACKAGES, build
|
|
27
27
|
from pymmcore_plus._logger import configure_logging
|
|
28
28
|
from pymmcore_plus._util import USER_DATA_MM_PATH
|
|
29
|
-
from pymmcore_plus.install import PLATFORM
|
|
30
29
|
|
|
31
30
|
app = typer.Typer(name="mmcore", no_args_is_help=True)
|
|
31
|
+
PLATFORM = system()
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def _show_version_and_exit(value: bool) -> None:
|
|
@@ -103,32 +103,44 @@ def clean(
|
|
|
103
103
|
|
|
104
104
|
@app.command(name="list")
|
|
105
105
|
def _list() -> None:
|
|
106
|
-
"""Show all Micro-Manager
|
|
106
|
+
"""Show all discovered Micro-Manager installations."""
|
|
107
|
+
from pymmcore_plus import _discovery
|
|
108
|
+
|
|
107
109
|
configure_logging(stderr_level="CRITICAL")
|
|
108
|
-
found: dict[Path, list[str]] = {}
|
|
110
|
+
found: dict[Path, list[tuple[str, _discovery.DiscoveredMM]]] = {}
|
|
109
111
|
with suppress(Exception):
|
|
110
|
-
for
|
|
111
|
-
pth = Path(
|
|
112
|
-
found.setdefault(pth.parent, []).append(pth.name)
|
|
112
|
+
for dm in _discovery.discover_mm():
|
|
113
|
+
pth = Path(dm.path)
|
|
114
|
+
found.setdefault(pth.parent, []).append((pth.name, dm))
|
|
115
|
+
|
|
116
|
+
active_mm = _discovery.find_micromanager(return_first=True)
|
|
117
|
+
required_div = _discovery.PYMMCORE_DIV
|
|
113
118
|
|
|
114
119
|
if found:
|
|
115
|
-
|
|
120
|
+
print(f"[magenta]Required pymmcore device interface version: {required_div}")
|
|
116
121
|
for parent, items in found.items():
|
|
117
|
-
print(f":file_folder:[bold green] {parent}")
|
|
118
|
-
for
|
|
119
|
-
version = ""
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
print(f"\n:file_folder:[bold green] {parent}")
|
|
123
|
+
for item_name, dm in items:
|
|
124
|
+
version = f" (DIV {dm.device_interface})" if dm.device_interface else ""
|
|
125
|
+
is_active = str(dm.path) == active_mm
|
|
126
|
+
is_compatible = dm.div_compatible
|
|
127
|
+
|
|
128
|
+
# Choose bullet and status
|
|
129
|
+
bullet = " •"
|
|
130
|
+
status = ""
|
|
131
|
+
if is_active:
|
|
132
|
+
bullet = " [bold yellow]➤"
|
|
133
|
+
if is_compatible:
|
|
134
|
+
status = " [bold blue](active)[/bold blue]"
|
|
135
|
+
else:
|
|
136
|
+
status = " [bold red](active, incompatible!)[/bold red]"
|
|
137
|
+
else:
|
|
138
|
+
status = " [red](incompatible)[/red]"
|
|
139
|
+
|
|
140
|
+
print(f"{bullet} [cyan]{item_name}{version}{status}")
|
|
129
141
|
else:
|
|
130
|
-
print(":x: [bold red]
|
|
131
|
-
print("[magenta]
|
|
142
|
+
print(":x: [bold red]No Micro-Manager installations found.")
|
|
143
|
+
print("[magenta]Run `mmcore install` to install a version of Micro-Manager")
|
|
132
144
|
|
|
133
145
|
|
|
134
146
|
@app.command()
|
|
@@ -174,18 +186,24 @@ def install(
|
|
|
174
186
|
help="Do not use rich output. Useful for scripting.",
|
|
175
187
|
show_default=False,
|
|
176
188
|
),
|
|
189
|
+
test_adapters: bool = typer.Option(
|
|
190
|
+
False,
|
|
191
|
+
"--test-adapters",
|
|
192
|
+
help="Install only test adapters (e.g. DemoCamera and others for testing).",
|
|
193
|
+
),
|
|
177
194
|
) -> None:
|
|
178
195
|
"""Install Micro-Manager Device adapters from <https://download.micro-manager.org>."""
|
|
179
196
|
import pymmcore_plus.install
|
|
180
197
|
|
|
198
|
+
kwargs = {}
|
|
181
199
|
if plain_output:
|
|
182
200
|
|
|
183
201
|
def _log_msg(text: str, color: str = "", emoji: str = "") -> None:
|
|
184
202
|
print(text)
|
|
185
203
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
204
|
+
kwargs["log_msg"] = _log_msg
|
|
205
|
+
|
|
206
|
+
pymmcore_plus.install.install(dest, release, test_adapters=test_adapters, **kwargs)
|
|
189
207
|
|
|
190
208
|
|
|
191
209
|
@app.command()
|
|
@@ -414,7 +432,7 @@ def use(
|
|
|
414
432
|
),
|
|
415
433
|
) -> None:
|
|
416
434
|
"""Change the currently used Micro-manager version/path."""
|
|
417
|
-
from pymmcore_plus.
|
|
435
|
+
from pymmcore_plus._discovery import use_micromanager
|
|
418
436
|
|
|
419
437
|
_pth = Path(pattern)
|
|
420
438
|
if _pth.exists():
|