pymmcore-plus 0.9.3__tar.gz → 0.13.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.9.3 → pymmcore_plus-0.13.0}/.gitignore +2 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/PKG-INFO +22 -12
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/README.md +2 -3
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/pyproject.toml +26 -13
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/__init__.py +7 -4
- pymmcore_plus-0.13.0/src/pymmcore_plus/_benchmark.py +203 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/_build.py +6 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/_cli.py +131 -31
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/_logger.py +19 -10
- pymmcore_plus-0.13.0/src/pymmcore_plus/_pymmcore.py +12 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/_util.py +139 -32
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/__init__.py +5 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_config.py +6 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_config_group.py +4 -3
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_constants.py +135 -10
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_device.py +4 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_metadata.py +3 -3
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_mmcore_plus.py +254 -170
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_property.py +6 -6
- pymmcore_plus-0.13.0/src/pymmcore_plus/core/_sequencing.py +434 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/__init__.py +6 -6
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_device_signal_view.py +8 -6
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_norm_slot.py +2 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_prop_event_mixin.py +7 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_protocol.py +5 -2
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_psygnal.py +2 -2
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/__init__.py +14 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/_device_manager.py +173 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/_proxy.py +127 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/_unicore.py +703 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/devices/__init__.py +0 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/devices/_device.py +269 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/devices/_properties.py +400 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/unicore/devices/_stage.py +221 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/install.py +16 -11
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/_engine.py +320 -148
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/_protocol.py +6 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/_runner.py +62 -51
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/_thread_relay.py +5 -3
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/events/__init__.py +2 -2
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/events/_protocol.py +10 -2
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/events/_psygnal.py +2 -2
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/handlers/_5d_writer_base.py +106 -15
- pymmcore_plus-0.13.0/src/pymmcore_plus/mda/handlers/__init__.py +11 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/handlers/_img_sequence_writer.py +11 -6
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/handlers/_ome_tiff_writer.py +8 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/handlers/_ome_zarr_writer.py +82 -9
- pymmcore_plus-0.13.0/src/pymmcore_plus/mda/handlers/_tensorstore_handler.py +374 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/handlers/_util.py +1 -1
- pymmcore_plus-0.13.0/src/pymmcore_plus/metadata/__init__.py +36 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/metadata/functions.py +353 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/metadata/schema.py +472 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/metadata/serialize.py +120 -0
- pymmcore_plus-0.13.0/src/pymmcore_plus/mocks.py +51 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_config_file.py +5 -6
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_config_group.py +29 -2
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_core_device.py +12 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_core_link.py +2 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_device.py +39 -8
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_microscope.py +39 -3
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_pixel_size_config.py +27 -4
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/_property.py +13 -3
- pymmcore_plus-0.13.0/src/pymmcore_plus/py.typed +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/seq_tester.py +1 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/conftest.py +49 -10
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/io/test_image_sequence_writer.py +1 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/io/test_ome_tiff.py +1 -0
- pymmcore_plus-0.13.0/tests/io/test_zarr_writers.py +247 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_adapter_class.py +1 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_bench.py +15 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_cli.py +70 -20
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_config_group_class.py +1 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_core.py +51 -111
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_device_class.py +1 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_events.py +67 -115
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_mda.py +81 -22
- pymmcore_plus-0.13.0/tests/test_metadata.py +123 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_misc.py +1 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_model.py +21 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_pixel_config_class.py +1 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_property_class.py +1 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_sequencing.py +20 -23
- pymmcore_plus-0.13.0/tests/test_slm_image.py +68 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/test_thread_relay.py +1 -0
- pymmcore_plus-0.13.0/tests/unicore/test_unicore.py +286 -0
- pymmcore_plus-0.13.0/tests/unicore/test_xy_stage.py +199 -0
- pymmcore_plus-0.9.3/src/pymmcore_plus/core/_sequencing.py +0 -297
- pymmcore_plus-0.9.3/src/pymmcore_plus/core/_state.py +0 -244
- pymmcore_plus-0.9.3/src/pymmcore_plus/mda/handlers/__init__.py +0 -5
- pymmcore_plus-0.9.3/tests/io/test_ome_zarr.py +0 -109
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/LICENSE +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/_adapter.py +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/core/events/_qsignals.py +0 -0
- pymmcore_plus-0.9.3/src/pymmcore_plus/py.typed → pymmcore_plus-0.13.0/src/pymmcore_plus/experimental/__init__.py +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/__init__.py +1 -1
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/mda/events/_qsignals.py +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/src/pymmcore_plus/model/__init__.py +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/__init__.py +0 -0
- {pymmcore_plus-0.9.3 → pymmcore_plus-0.13.0}/tests/local_config.cfg +0 -0
|
@@ -139,10 +139,11 @@ cython_debug/
|
|
|
139
139
|
|
|
140
140
|
Micro-Manager-*
|
|
141
141
|
.vscode
|
|
142
|
+
.idea
|
|
142
143
|
|
|
143
144
|
docs/_includes/_cmmcore_members.md
|
|
144
145
|
docs/_includes/_cmmcore_table.md
|
|
145
146
|
docs/_includes/_cmmcoreplus_members.md
|
|
146
147
|
|
|
147
|
-
example_*.
|
|
148
|
+
example_*.some.tiff
|
|
148
149
|
example.zarr
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pymmcore-plus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.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,24 +16,24 @@ 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
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
24
|
Classifier: Topic :: System :: Hardware
|
|
25
25
|
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
26
26
|
Classifier: Topic :: Utilities
|
|
27
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
28
|
Requires-Dist: numpy>=1.17.3
|
|
29
29
|
Requires-Dist: platformdirs>=3.0.0
|
|
30
30
|
Requires-Dist: psygnal>=0.7
|
|
31
31
|
Requires-Dist: pymmcore>=10.7.0.71.0
|
|
32
32
|
Requires-Dist: rich>=10.2.0
|
|
33
|
+
Requires-Dist: tensorstore; python_version < '3.13'
|
|
33
34
|
Requires-Dist: typer>=0.4.2
|
|
34
35
|
Requires-Dist: typing-extensions
|
|
35
|
-
Requires-Dist: useq-schema>=0.
|
|
36
|
-
Requires-Dist: wrapt>=1.14
|
|
36
|
+
Requires-Dist: useq-schema>=0.6.2
|
|
37
37
|
Provides-Extra: cli
|
|
38
38
|
Requires-Dist: rich>=10.2.0; extra == 'cli'
|
|
39
39
|
Requires-Dist: typer>=0.4.2; extra == 'cli'
|
|
@@ -43,17 +43,27 @@ Requires-Dist: mypy; extra == 'dev'
|
|
|
43
43
|
Requires-Dist: pdbpp; extra == 'dev'
|
|
44
44
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
45
45
|
Requires-Dist: ruff; extra == 'dev'
|
|
46
|
+
Requires-Dist: tensorstore-stubs; extra == 'dev'
|
|
46
47
|
Provides-Extra: docs
|
|
47
48
|
Requires-Dist: mkdocs-material; extra == 'docs'
|
|
49
|
+
Requires-Dist: mkdocs-typer==0.0.3; extra == 'docs'
|
|
48
50
|
Requires-Dist: mkdocs>=1.4; extra == 'docs'
|
|
49
51
|
Requires-Dist: mkdocstrings-python==1.1.2; extra == 'docs'
|
|
50
52
|
Requires-Dist: mkdocstrings==0.22.0; extra == 'docs'
|
|
51
53
|
Provides-Extra: io
|
|
52
54
|
Requires-Dist: tifffile>=2021.6.14; extra == 'io'
|
|
53
|
-
Requires-Dist: zarr
|
|
55
|
+
Requires-Dist: zarr<3,>=2.2; extra == 'io'
|
|
56
|
+
Provides-Extra: pyqt5
|
|
57
|
+
Requires-Dist: pyqt5>=5.15.4; extra == 'pyqt5'
|
|
58
|
+
Provides-Extra: pyqt6
|
|
59
|
+
Requires-Dist: pyqt6<6.8,>=6.4.2; extra == 'pyqt6'
|
|
60
|
+
Provides-Extra: pyside2
|
|
61
|
+
Requires-Dist: pyside2>=5.15; extra == 'pyside2'
|
|
62
|
+
Provides-Extra: pyside6
|
|
63
|
+
Requires-Dist: pyside6<6.8,>=6.4.0; extra == 'pyside6'
|
|
54
64
|
Provides-Extra: test
|
|
55
65
|
Requires-Dist: msgpack; extra == 'test'
|
|
56
|
-
Requires-Dist:
|
|
66
|
+
Requires-Dist: msgspec; (python_version < '3.13') and extra == 'test'
|
|
57
67
|
Requires-Dist: pytest-cov>=4; extra == 'test'
|
|
58
68
|
Requires-Dist: pytest-qt>=4; extra == 'test'
|
|
59
69
|
Requires-Dist: pytest>=7.3.2; extra == 'test'
|
|
@@ -61,7 +71,8 @@ Requires-Dist: qtpy>=2; extra == 'test'
|
|
|
61
71
|
Requires-Dist: rich; extra == 'test'
|
|
62
72
|
Requires-Dist: tifffile>=2021.6.14; extra == 'test'
|
|
63
73
|
Requires-Dist: typer>=0.4.2; extra == 'test'
|
|
64
|
-
Requires-Dist:
|
|
74
|
+
Requires-Dist: xarray; extra == 'test'
|
|
75
|
+
Requires-Dist: zarr<3,>=2.2; extra == 'test'
|
|
65
76
|
Description-Content-Type: text/markdown
|
|
66
77
|
|
|
67
78
|
# pymmcore-plus
|
|
@@ -70,7 +81,7 @@ Description-Content-Type: text/markdown
|
|
|
70
81
|
[](https://pypi.org/project/pymmcore-plus)
|
|
71
82
|
[](https://pypi.org/project/pymmcore-plus)
|
|
72
83
|
[](https://anaconda.org/conda-forge/pymmcore-plus)
|
|
73
|
-
[](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/ci.yml)
|
|
74
85
|
[](https://pymmcore-plus.github.io/pymmcore-plus/)
|
|
75
86
|
[](https://codecov.io/gh/pymmcore-plus/pymmcore-plus)
|
|
76
87
|
[](https://codspeed.io/pymmcore-plus/pymmcore-plus)
|
|
@@ -141,8 +152,7 @@ provides a python API to control the C++ core directly, without the need for
|
|
|
141
152
|
Java in the loop. Each has its own advantages and disadvantages! With
|
|
142
153
|
pycro-manager you immediately get the entire existing micro-manager ecosystem
|
|
143
154
|
and GUI application. With pymmcore-plus you don't need to install Java, and you
|
|
144
|
-
have direct access to the memory buffers used by the C++ core
|
|
145
|
-
side of things is far less mature.
|
|
155
|
+
have direct access to the memory buffers used by the C++ core.
|
|
146
156
|
|
|
147
157
|
## Quickstart
|
|
148
158
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/pymmcore-plus)
|
|
5
5
|
[](https://pypi.org/project/pymmcore-plus)
|
|
6
6
|
[](https://anaconda.org/conda-forge/pymmcore-plus)
|
|
7
|
-
[](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/ci.yml)
|
|
8
8
|
[](https://pymmcore-plus.github.io/pymmcore-plus/)
|
|
9
9
|
[](https://codecov.io/gh/pymmcore-plus/pymmcore-plus)
|
|
10
10
|
[](https://codspeed.io/pymmcore-plus/pymmcore-plus)
|
|
@@ -75,8 +75,7 @@ provides a python API to control the C++ core directly, without the need for
|
|
|
75
75
|
Java in the loop. Each has its own advantages and disadvantages! With
|
|
76
76
|
pycro-manager you immediately get the entire existing micro-manager ecosystem
|
|
77
77
|
and GUI application. With pymmcore-plus you don't need to install Java, and you
|
|
78
|
-
have direct access to the memory buffers used by the C++ core
|
|
79
|
-
side of things is far less mature.
|
|
78
|
+
have direct access to the memory buffers used by the C++ core.
|
|
80
79
|
|
|
81
80
|
## Quickstart
|
|
82
81
|
|
|
@@ -9,7 +9,7 @@ name = "pymmcore-plus"
|
|
|
9
9
|
description = "pymmcore superset providing improved APIs, event handling, and a pure python acquisition engine"
|
|
10
10
|
keywords = ["microscope", "micro-manager", "smart-microscopy"]
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
requires-python = ">=3.
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
13
|
license = { text = "BSD 3-Clause License" }
|
|
14
14
|
authors = [
|
|
15
15
|
{ name = "Talley Lambert", email = "talley.lambert@gmail.com" },
|
|
@@ -24,11 +24,11 @@ classifiers = [
|
|
|
24
24
|
"Operating System :: OS Independent",
|
|
25
25
|
"Programming Language :: Python",
|
|
26
26
|
"Programming Language :: Python :: 3",
|
|
27
|
-
"Programming Language :: Python :: 3.8",
|
|
28
27
|
"Programming Language :: Python :: 3.9",
|
|
29
28
|
"Programming Language :: Python :: 3.10",
|
|
30
29
|
"Programming Language :: Python :: 3.11",
|
|
31
30
|
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
32
|
"Topic :: System :: Hardware",
|
|
33
33
|
"Topic :: System :: Hardware :: Hardware Drivers",
|
|
34
34
|
"Topic :: Utilities",
|
|
@@ -39,9 +39,9 @@ dependencies = [
|
|
|
39
39
|
"numpy >=1.17.3",
|
|
40
40
|
"psygnal >=0.7",
|
|
41
41
|
"pymmcore >=10.7.0.71.0",
|
|
42
|
-
"typing-extensions",
|
|
43
|
-
"useq-schema >=0.
|
|
44
|
-
"
|
|
42
|
+
"typing-extensions", # not actually required at runtime
|
|
43
|
+
"useq-schema >=0.6.2",
|
|
44
|
+
"tensorstore; python_version < '3.13'",
|
|
45
45
|
# cli requirements included by default for now
|
|
46
46
|
"typer >=0.4.2",
|
|
47
47
|
"rich >=10.2.0",
|
|
@@ -51,10 +51,14 @@ dependencies = [
|
|
|
51
51
|
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies
|
|
52
52
|
[project.optional-dependencies]
|
|
53
53
|
cli = ["typer >=0.4.2", "rich >=10.2.0"]
|
|
54
|
-
io = ["tifffile >=2021.6.14", "zarr >=2.2"]
|
|
54
|
+
io = ["tifffile >=2021.6.14", "zarr >=2.2,<3"]
|
|
55
|
+
PySide2 = ["PySide2 >=5.15"]
|
|
56
|
+
PySide6 = ["PySide6 >=6.4.0,<6.8"]
|
|
57
|
+
PyQt5 = ["PyQt5 >=5.15.4"]
|
|
58
|
+
PyQt6 = ["PyQt6 >=6.4.2,<6.8"]
|
|
55
59
|
test = [
|
|
60
|
+
"msgspec; python_version < '3.13'",
|
|
56
61
|
"msgpack",
|
|
57
|
-
"PySide6",
|
|
58
62
|
"pytest-cov >=4",
|
|
59
63
|
"pytest-qt >=4",
|
|
60
64
|
"pytest >=7.3.2",
|
|
@@ -62,14 +66,16 @@ test = [
|
|
|
62
66
|
"rich",
|
|
63
67
|
"typer >=0.4.2",
|
|
64
68
|
"tifffile >=2021.6.14",
|
|
65
|
-
"zarr >=2.2",
|
|
69
|
+
"zarr >=2.2,<3",
|
|
70
|
+
"xarray",
|
|
66
71
|
]
|
|
67
|
-
dev = ["ipython", "mypy", "pdbpp", "pre-commit", "ruff"]
|
|
72
|
+
dev = ["ipython", "mypy", "pdbpp", "pre-commit", "ruff", "tensorstore-stubs"]
|
|
68
73
|
docs = [
|
|
69
74
|
"mkdocs >=1.4",
|
|
70
75
|
"mkdocs-material",
|
|
71
76
|
"mkdocstrings ==0.22.0",
|
|
72
77
|
"mkdocstrings-python ==1.1.2",
|
|
78
|
+
"mkdocs-typer ==0.0.3",
|
|
73
79
|
# "griffe @ git+https://github.com/tlambert03/griffe@recursion"
|
|
74
80
|
]
|
|
75
81
|
|
|
@@ -96,10 +102,10 @@ include = ["/src", "/tests"]
|
|
|
96
102
|
only-include = ["src"]
|
|
97
103
|
sources = ["src"]
|
|
98
104
|
|
|
99
|
-
# https://
|
|
105
|
+
# https://docs.astral.sh/ruff/rules/
|
|
100
106
|
[tool.ruff]
|
|
101
107
|
line-length = 88
|
|
102
|
-
target-version = "
|
|
108
|
+
target-version = "py39"
|
|
103
109
|
|
|
104
110
|
[tool.ruff.lint]
|
|
105
111
|
pydocstyle = { convention = "numpy" }
|
|
@@ -115,7 +121,7 @@ select = [
|
|
|
115
121
|
"A001", # flake8-builtins
|
|
116
122
|
"RUF", # ruff-specific rules
|
|
117
123
|
"TID", # tidy
|
|
118
|
-
"
|
|
124
|
+
"TC", # typecheck
|
|
119
125
|
"SLF", # private-access
|
|
120
126
|
]
|
|
121
127
|
ignore = [
|
|
@@ -138,7 +144,8 @@ docstring-code-format = true
|
|
|
138
144
|
[tool.pytest.ini_options]
|
|
139
145
|
minversion = "6.0"
|
|
140
146
|
testpaths = ["tests"]
|
|
141
|
-
filterwarnings = ["error"]
|
|
147
|
+
filterwarnings = ["error", "ignore:Failed to disconnect::pytestqt"]
|
|
148
|
+
markers = ["run_last: mark a test to run last"]
|
|
142
149
|
|
|
143
150
|
# https://mypy.readthedocs.io/en/stable/config_file.html
|
|
144
151
|
[tool.mypy]
|
|
@@ -149,6 +156,9 @@ disallow_subclassing_any = false
|
|
|
149
156
|
show_error_codes = true
|
|
150
157
|
pretty = true
|
|
151
158
|
plugins = "pydantic.mypy"
|
|
159
|
+
# see https://github.com/python/mypy/issues/5374 and related discussions
|
|
160
|
+
# it causes more pain than it solves
|
|
161
|
+
disable_error_code = ["type-abstract"]
|
|
152
162
|
|
|
153
163
|
[[tool.mypy.overrides]]
|
|
154
164
|
module = ["tests.*"]
|
|
@@ -181,3 +191,6 @@ ignore = [
|
|
|
181
191
|
"tests/**/*",
|
|
182
192
|
"tox.ini",
|
|
183
193
|
]
|
|
194
|
+
|
|
195
|
+
[tool.typos.default]
|
|
196
|
+
extend-ignore-identifiers-re = ["(?i)nd2?.*", "(?i)ome", "anager"]
|
|
@@ -9,7 +9,7 @@ except PackageNotFoundError: # pragma: no cover
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
from ._logger import configure_logging
|
|
12
|
-
from ._util import find_micromanager
|
|
12
|
+
from ._util import find_micromanager, use_micromanager
|
|
13
13
|
from .core import (
|
|
14
14
|
CFGCommand,
|
|
15
15
|
CFGGroup,
|
|
@@ -26,6 +26,7 @@ from .core import (
|
|
|
26
26
|
FocusDirection,
|
|
27
27
|
Keyword,
|
|
28
28
|
Metadata,
|
|
29
|
+
PixelFormat,
|
|
29
30
|
PortType,
|
|
30
31
|
PropertyType,
|
|
31
32
|
)
|
|
@@ -33,7 +34,6 @@ from .core.events import CMMCoreSignaler, PCoreSignaler
|
|
|
33
34
|
from .mda._runner import GeneratorMDASequence
|
|
34
35
|
|
|
35
36
|
__all__ = [
|
|
36
|
-
"__version__",
|
|
37
37
|
"ActionType",
|
|
38
38
|
"CFGCommand",
|
|
39
39
|
"CFGGroup",
|
|
@@ -41,7 +41,6 @@ __all__ = [
|
|
|
41
41
|
"CMMCoreSignaler",
|
|
42
42
|
"ConfigGroup",
|
|
43
43
|
"Configuration",
|
|
44
|
-
"configure_logging",
|
|
45
44
|
"Device",
|
|
46
45
|
"DeviceAdapter",
|
|
47
46
|
"DeviceDetectionStatus",
|
|
@@ -49,12 +48,16 @@ __all__ = [
|
|
|
49
48
|
"DeviceNotification",
|
|
50
49
|
"DeviceProperty",
|
|
51
50
|
"DeviceType",
|
|
52
|
-
"find_micromanager",
|
|
53
51
|
"FocusDirection",
|
|
54
52
|
"GeneratorMDASequence",
|
|
55
53
|
"Keyword",
|
|
56
54
|
"Metadata",
|
|
57
55
|
"PCoreSignaler",
|
|
56
|
+
"PixelFormat",
|
|
58
57
|
"PortType",
|
|
59
58
|
"PropertyType",
|
|
59
|
+
"__version__",
|
|
60
|
+
"configure_logging",
|
|
61
|
+
"find_micromanager",
|
|
62
|
+
"use_micromanager",
|
|
60
63
|
]
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import timeit
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from pymmcore_plus import CMMCorePlus, DeviceType
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
11
|
+
|
|
12
|
+
from pymmcore_plus.core._device import Device
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Benchmark:
|
|
16
|
+
device_type = DeviceType.Camera
|
|
17
|
+
|
|
18
|
+
def __init__(self, core: CMMCorePlus, label: str = "") -> None:
|
|
19
|
+
self.core = core
|
|
20
|
+
self.label = label
|
|
21
|
+
|
|
22
|
+
def setup(self) -> None:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def device(self) -> Device | None:
|
|
26
|
+
if self.label is not None:
|
|
27
|
+
return self.core.getDeviceObject(self.label)
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def run(self, number: int) -> Iterator[tuple[str, float | str]]:
|
|
31
|
+
# get methods in the order of definition, in reverse MRO order
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
self.setup()
|
|
35
|
+
except Exception as e: # pragma: no cover
|
|
36
|
+
warnings.warn(
|
|
37
|
+
f"Setup failed on device {self.label!r}: {e}",
|
|
38
|
+
RuntimeWarning,
|
|
39
|
+
stacklevel=2,
|
|
40
|
+
)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
methods: list[str] = []
|
|
44
|
+
for base in reversed(type(self).mro()):
|
|
45
|
+
methods.extend(m for m in base.__dict__ if m.startswith("bench_"))
|
|
46
|
+
|
|
47
|
+
for method_name in methods:
|
|
48
|
+
try:
|
|
49
|
+
t = timeit.timeit(getattr(self, method_name), number=number)
|
|
50
|
+
result: float | str = round(1000 * t / number, 3)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
result = str(e)
|
|
53
|
+
yield method_name[6:], result
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CoreBenchmark(Benchmark):
|
|
57
|
+
device_type = DeviceType.Core
|
|
58
|
+
|
|
59
|
+
def bench_getDeviceAdapterNames(self) -> None:
|
|
60
|
+
self.core.getDeviceAdapterNames()
|
|
61
|
+
|
|
62
|
+
def bench_getLoadedDevices(self) -> None:
|
|
63
|
+
self.core.getLoadedDevices()
|
|
64
|
+
|
|
65
|
+
def bench_getSystemState(self) -> None:
|
|
66
|
+
self.core.getSystemState()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CameraBenchmark(Benchmark):
|
|
70
|
+
device_type = DeviceType.Camera
|
|
71
|
+
|
|
72
|
+
def setup(self) -> None:
|
|
73
|
+
self.core.setCameraDevice(self.label)
|
|
74
|
+
self.core.setExposure(self.label, 1)
|
|
75
|
+
|
|
76
|
+
def bench_getMultiROI(self) -> None:
|
|
77
|
+
self.core.getMultiROI()
|
|
78
|
+
|
|
79
|
+
def bench_getExposure(self) -> None:
|
|
80
|
+
self.core.getExposure(self.label)
|
|
81
|
+
|
|
82
|
+
def bench_snapImage(self) -> None:
|
|
83
|
+
self.core.snapImage()
|
|
84
|
+
|
|
85
|
+
def bench_getImage(self) -> None:
|
|
86
|
+
self.core.getImage()
|
|
87
|
+
|
|
88
|
+
def bench_getImageWidth(self) -> None:
|
|
89
|
+
self.core.getImageWidth()
|
|
90
|
+
|
|
91
|
+
def bench_getImageHeight(self) -> None:
|
|
92
|
+
self.core.getImageHeight()
|
|
93
|
+
|
|
94
|
+
def bench_getImageBufferSize(self) -> None:
|
|
95
|
+
self.core.getImageBufferSize()
|
|
96
|
+
|
|
97
|
+
def bench_getImageBitDepth(self) -> None:
|
|
98
|
+
self.core.getImageBitDepth()
|
|
99
|
+
|
|
100
|
+
def bench_getNumberOfComponents(self) -> None:
|
|
101
|
+
self.core.getNumberOfComponents()
|
|
102
|
+
|
|
103
|
+
def bench_getNumberOfCameraChannels(self) -> None:
|
|
104
|
+
self.core.getNumberOfCameraChannels()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class XYStageBenchmark(Benchmark):
|
|
108
|
+
device_type = DeviceType.XYStage
|
|
109
|
+
|
|
110
|
+
def setup(self) -> None:
|
|
111
|
+
self.core.setXYStageDevice(self.label)
|
|
112
|
+
self.position = self.core.getXYPosition(self.label)
|
|
113
|
+
|
|
114
|
+
def bench_getXYPosition(self) -> None:
|
|
115
|
+
self.core.getXYPosition(self.label)
|
|
116
|
+
|
|
117
|
+
def bench_getXPosition(self) -> None:
|
|
118
|
+
self.core.getXPosition(self.label)
|
|
119
|
+
|
|
120
|
+
def bench_getYPosition(self) -> None:
|
|
121
|
+
self.core.getYPosition(self.label)
|
|
122
|
+
|
|
123
|
+
def bench_setXYPosition(self) -> None:
|
|
124
|
+
self.core.setXYPosition(self.label, *self.position)
|
|
125
|
+
|
|
126
|
+
def bench_setRelativeXYPosition(self) -> None:
|
|
127
|
+
self.core.setRelativeXYPosition(self.label, 0, 0)
|
|
128
|
+
|
|
129
|
+
def bench_isXYStageSequenceable(self) -> None:
|
|
130
|
+
self.core.isXYStageSequenceable(self.label)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class StageBenchmark(Benchmark):
|
|
134
|
+
device_type = DeviceType.Stage
|
|
135
|
+
|
|
136
|
+
def setup(self) -> None:
|
|
137
|
+
self.position = self.core.getPosition(self.label)
|
|
138
|
+
|
|
139
|
+
def bench_getPosition(self) -> None:
|
|
140
|
+
self.core.getPosition(self.label)
|
|
141
|
+
|
|
142
|
+
def bench_setPosition(self) -> None:
|
|
143
|
+
self.core.setPosition(self.label, self.position)
|
|
144
|
+
|
|
145
|
+
def bench_setRelativePosition(self) -> None:
|
|
146
|
+
self.core.setRelativePosition(self.label, 0)
|
|
147
|
+
|
|
148
|
+
def bench_isStageSequenceable(self) -> None:
|
|
149
|
+
self.core.isStageSequenceable(self.label)
|
|
150
|
+
|
|
151
|
+
def bench_isStageLinearSequenceable(self) -> None:
|
|
152
|
+
self.core.isStageLinearSequenceable(self.label)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class StateBenchmark(Benchmark):
|
|
156
|
+
device_type = DeviceType.State
|
|
157
|
+
|
|
158
|
+
def setup(self) -> None:
|
|
159
|
+
self.initial_state = self.core.getState(self.label)
|
|
160
|
+
try:
|
|
161
|
+
self.labels: Sequence[str] = self.core.getStateLabels(self.label)
|
|
162
|
+
except Exception:
|
|
163
|
+
self.labels = []
|
|
164
|
+
|
|
165
|
+
def bench_getState(self) -> None:
|
|
166
|
+
self.core.getState(self.label)
|
|
167
|
+
|
|
168
|
+
def bench_setState(self) -> None:
|
|
169
|
+
self.core.setState(self.label, self.initial_state)
|
|
170
|
+
|
|
171
|
+
def bench_getNumberOfStates(self) -> None:
|
|
172
|
+
self.core.getNumberOfStates(self.label)
|
|
173
|
+
|
|
174
|
+
def bench_getStateLabel(self) -> None:
|
|
175
|
+
self.core.getStateLabel(self.label)
|
|
176
|
+
|
|
177
|
+
def bench_getStateFromLabel(self) -> None:
|
|
178
|
+
for label in self.labels:
|
|
179
|
+
self.core.getStateFromLabel(self.label, label)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def benchmark_core_and_devices(
|
|
183
|
+
core: CMMCorePlus, number: int = 100
|
|
184
|
+
) -> Iterable[Device | None | tuple[str, float | str]]:
|
|
185
|
+
"""Take an initialized core with devices and benchmark various methods.
|
|
186
|
+
|
|
187
|
+
Yields
|
|
188
|
+
------
|
|
189
|
+
Device | None | tuple[str, float | str]
|
|
190
|
+
If a `Device`, it is the device object being benchmarked.
|
|
191
|
+
If None, it is the core object being benchmarked.
|
|
192
|
+
If a tuple, it is the method name and the time taken to run it.
|
|
193
|
+
"""
|
|
194
|
+
for cls in Benchmark.__subclasses__():
|
|
195
|
+
if cls.device_type == DeviceType.Core:
|
|
196
|
+
bench = cls(core, "Core")
|
|
197
|
+
yield bench.device()
|
|
198
|
+
yield from bench.run(number)
|
|
199
|
+
else:
|
|
200
|
+
for dev in core.getLoadedDevicesOfType(cls.device_type):
|
|
201
|
+
bench = cls(core, dev)
|
|
202
|
+
yield bench.device()
|
|
203
|
+
yield from bench.run(number)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Clone the micro-manager source code from GitHub and build dev devices."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import json
|
|
4
6
|
import os
|
|
5
7
|
import platform
|
|
@@ -9,12 +11,15 @@ import subprocess
|
|
|
9
11
|
import tempfile
|
|
10
12
|
from contextlib import contextmanager
|
|
11
13
|
from pathlib import Path
|
|
12
|
-
from typing import
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
13
15
|
from urllib.request import Request, urlopen
|
|
14
16
|
|
|
15
17
|
from rich import print
|
|
16
18
|
from rich.prompt import Prompt
|
|
17
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from collections.abc import Iterator, Sequence
|
|
22
|
+
|
|
18
23
|
MM_REPO = "micro-manager/micro-manager"
|
|
19
24
|
MMCORE_AND_DEV = "micro-manager/mmCoreAndDevices"
|
|
20
25
|
MM_REPO_URL = f"https://github.com/{MM_REPO}.git"
|