diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index e8e923c..0417b9e 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -49,7 +49,7 @@ jobs: wm: herbstluftwm - name: Run tests - run: uv run --dev pytest -v --color=yes --cov=ndevio --cov-report=xml + run: uv run --dev pytest -v --color=yes -m "not network" --cov=ndevio --cov-report=xml - name: Coverage uses: codecov/codecov-action@v5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9099864..e098778 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,8 @@ repos: - id: trailing-whitespace exclude: ^\.napari-hub/.* - id: check-yaml # checks for correct yaml syntax for github actions ex. + exclude: + (?x)(^src/ndevio/ndev_settings\.yaml$) - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.8 hooks: diff --git a/pixi.lock b/pixi.lock index 73982ee..40370c1 100644 --- a/pixi.lock +++ b/pixi.lock @@ -46,11 +46,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/95/df3e74eb3f9d3b284fb9cd72ec963ee242922af600c2f205f3f3ad3be551/bermuda-0.1.6-cp313-cp313-manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a2/d6/823c893ee752ea0261c7fbea073969733f39976516fc264befac358eb6ba/bioio_ome_zarr-3.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -60,10 +60,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1d/54/a46920229d12c3a6e9f0081d1bdaeffad23c1826353ace95714faee926e5/dask-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/3a/2121294941227c548d4b5f897a8a1b5f4c44a58f5437f239e6b86511d78e/dask-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/46/ec/da78855318971c2be94d0283a41de6941a6b9f16146fb00babc74903ae01/distributed-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/45/ca760deab4de448e6c0e3860fc187bcc49216eabda379f6ce68065158843/distributed-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl @@ -111,7 +111,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -196,7 +196,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e8/13/8997d96bdc0a81f331dfd41b368935d79e8ea2917840266567e6dc40d684/vispy-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -233,11 +233,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2f/f8/2db7f92c6473715c9e4f705d5f2338c6880ebaa3e8abd193fed6ce9f729d/bermuda-0.1.6-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -298,7 +298,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -383,7 +383,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/85/9a/6664b7f0d28e0008a28111bae847a1fa3eedd409458bf01ca45a6e99da6f/vispy-0.15.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl @@ -421,11 +421,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/33/7ed7116eee36c2391e723ed9dc4fb09f682bc2b34c3fd10209baabe6ac68/bermuda-0.1.6-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -486,7 +486,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -571,7 +571,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cf/6f/b9b36f841c5ff7320764f64822e79df3fea8a2c92270cda7f3a634d9a031/vispy-0.15.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl @@ -609,11 +609,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/b1/520f6b747d05181e222d30e8eda2b09373a1b6f5b8f7f5c0082a6d791a10/bermuda-0.1.6-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -675,7 +675,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -759,7 +759,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/6e/37/abb30db1853b69aed4c32813cf312f301ec3641b8846193be9a6d892d607/vispy-0.15.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl @@ -811,11 +811,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a2/d6/823c893ee752ea0261c7fbea073969733f39976516fc264befac358eb6ba/bioio_ome_zarr-3.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -825,10 +825,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1d/54/a46920229d12c3a6e9f0081d1bdaeffad23c1826353ace95714faee926e5/dask-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/3a/2121294941227c548d4b5f897a8a1b5f4c44a58f5437f239e6b86511d78e/dask-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/46/ec/da78855318971c2be94d0283a41de6941a6b9f16146fb00babc74903ae01/distributed-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/45/ca760deab4de448e6c0e3860fc187bcc49216eabda379f6ce68065158843/distributed-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl @@ -875,7 +875,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -955,7 +955,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e8/13/8997d96bdc0a81f331dfd41b368935d79e8ea2917840266567e6dc40d684/vispy-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -991,11 +991,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -1055,7 +1055,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -1135,7 +1135,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/85/9a/6664b7f0d28e0008a28111bae847a1fa3eedd409458bf01ca45a6e99da6f/vispy-0.15.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl @@ -1172,11 +1172,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -1236,7 +1236,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -1316,7 +1316,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cf/6f/b9b36f841c5ff7320764f64822e79df3fea8a2c92270cda7f3a634d9a031/vispy-0.15.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl @@ -1353,11 +1353,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -1418,7 +1418,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -1497,7 +1497,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/6e/37/abb30db1853b69aed4c32813cf312f301ec3641b8846193be9a6d892d607/vispy-0.15.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl @@ -1550,12 +1550,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/d5/98869267ac005b7a337dcedaa327e06fc57d8493e4b4858c385e1383f717/bioio_czi-2.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a2/d6/823c893ee752ea0261c7fbea073969733f39976516fc264befac358eb6ba/bioio_ome_zarr-3.0.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -1567,10 +1567,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/a3/11/7debff1cae6d71e1b0c929af108ef6108488cf6e496d69f0c4116607e106/cmake-4.2.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/1d/54/a46920229d12c3a6e9f0081d1bdaeffad23c1826353ace95714faee926e5/dask-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/3a/2121294941227c548d4b5f897a8a1b5f4c44a58f5437f239e6b86511d78e/dask-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/46/ec/da78855318971c2be94d0283a41de6941a6b9f16146fb00babc74903ae01/distributed-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/45/ca760deab4de448e6c0e3860fc187bcc49216eabda379f6ce68065158843/distributed-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl @@ -1618,7 +1618,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -1659,7 +1659,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/d5/78/92f3c46440a83ebe22ae614bd6792e7b052bcb58ff128f677f5662015184/pyqt6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a8/07/21f7dc188e35b46631707f3b40ace5643a0e03a8e1e446854826d08a04ae/pyqt6_qt6-6.9.2-py3-none-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b0/1e/c6a28a142f14e735088534cc92951c3f48cccd77cdd4f3b10d7996be420f/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/d0/8339b888ad64a3d4e508fed8245a402b503846e1972c10ad60955883dcbb/pytest_qt-4.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl @@ -1707,7 +1707,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e8/13/8997d96bdc0a81f331dfd41b368935d79e8ea2917840266567e6dc40d684/vispy-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/20/69a0e6058bc5ea74892d089d64dfc3a62ba78917ec5e2cfa70f7c92ba3a5/xmltodict-1.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl @@ -1745,12 +1745,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/d5/98869267ac005b7a337dcedaa327e06fc57d8493e4b4858c385e1383f717/bioio_czi-2.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -1813,7 +1813,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -1851,10 +1851,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/6b/4d/b0662c259925942c5f940d1f522fba37d9ea3a9c8769070b3ed3d24bf87a/pylibczirw-5.1.1-cp313-cp313-macosx_10_15_x86_64.whl - pypi: https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/66/48/be73fb730c6f617f456ab73150db384b17a7a08394d7e2ded55f42de8a7b/pyqt6-6.10.0-cp39-abi3-macosx_10_14_universal2.whl + - pypi: https://files.pythonhosted.org/packages/c8/b6/de44a5e229a1b0e91c997e8d4083636f4c17f6cc740e12c7ae468fe223b9/pyqt6-6.10.1-cp39-abi3-macosx_10_14_universal2.whl - pypi: https://files.pythonhosted.org/packages/c3/b2/309ba75490d614530f10a5b922555374098706364ec1cc411f98068beb4d/pyqt6_qt6-6.10.0-py3-none-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a1/1e/979ea64c98ca26979d8ce11e9a36579e17d22a71f51d7366d6eec3c82c13/pyqt6_sip-13.10.2-cp313-cp313-macosx_10_13_universal2.whl - - pypi: https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/d0/8339b888ad64a3d4e508fed8245a402b503846e1972c10ad60955883dcbb/pytest_qt-4.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl @@ -1902,7 +1902,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/85/9a/6664b7f0d28e0008a28111bae847a1fa3eedd409458bf01ca45a6e99da6f/vispy-0.15.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/20/69a0e6058bc5ea74892d089d64dfc3a62ba78917ec5e2cfa70f7c92ba3a5/xmltodict-1.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl @@ -1941,12 +1941,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/d5/98869267ac005b7a337dcedaa327e06fc57d8493e4b4858c385e1383f717/bioio_czi-2.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -2009,7 +2009,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -2047,10 +2047,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/18/0a/f4f676587d0d5d0d528fd728dd0cec64000e7b0a31f915f8ffda886d7e49/pylibczirw-5.1.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/66/48/be73fb730c6f617f456ab73150db384b17a7a08394d7e2ded55f42de8a7b/pyqt6-6.10.0-cp39-abi3-macosx_10_14_universal2.whl + - pypi: https://files.pythonhosted.org/packages/c8/b6/de44a5e229a1b0e91c997e8d4083636f4c17f6cc740e12c7ae468fe223b9/pyqt6-6.10.1-cp39-abi3-macosx_10_14_universal2.whl - pypi: https://files.pythonhosted.org/packages/25/20/1de54c91f6f34be6042bdb6913c71e253a6bddf002d8aa78cb1acc67f5a8/pyqt6_qt6-6.10.0-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a1/1e/979ea64c98ca26979d8ce11e9a36579e17d22a71f51d7366d6eec3c82c13/pyqt6_sip-13.10.2-cp313-cp313-macosx_10_13_universal2.whl - - pypi: https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/d0/8339b888ad64a3d4e508fed8245a402b503846e1972c10ad60955883dcbb/pytest_qt-4.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl @@ -2098,7 +2098,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cf/6f/b9b36f841c5ff7320764f64822e79df3fea8a2c92270cda7f3a634d9a031/vispy-0.15.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/20/69a0e6058bc5ea74892d089d64dfc3a62ba78917ec5e2cfa70f7c92ba3a5/xmltodict-1.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl @@ -2137,12 +2137,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/d5/98869267ac005b7a337dcedaa327e06fc57d8493e4b4858c385e1383f717/bioio_czi-2.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/77/7d85ed141db96de2903950a678986377b1439a1c8828f7d5877897108e6c/bioio_imageio-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/88/cdfc59ac1fb3db84d59d47b8afa3bcf539b8afabc86d8b5230909b6487f9/bioio_ome_tiff-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/ed/6810b9e630b1f79c57d6f966f775bf753030980c9eafe97fd7a1136cb367/bioio_tifffile-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/d2/507fd0ee4dd574d2bdbdeac5df83f39d2cae1ffe97d4622cca6f6bab39f1/botocore-1.40.70-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl @@ -2206,7 +2206,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/ac/d8/61c29378edc7e75aa5f877b3d1b851ca357cb47b8dd4b6268d07abf7d9d3/napari-0.6.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/01/72/2067f28fd0ae87978f3b61e8ec30c1d085bbed03f64eb58e43949d526b3a/napari_console-0.1.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c9/60/eaf45290008cfb7cc1e99f6427540704fafc5c65a3a25ff0024085cd2e0d/napari_plugin_engine-0.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/ae/0eeb22806c4157a9199f65a93374e5ff5c4d2cc1411b5d25053bcd9e6b91/napari_svg-0.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/57/2aaba8ba1a918ccfb63a84424be99658f5bd33fdd5043c28446e91230bd7/nbatch-0.0.4-py3-none-any.whl @@ -2242,10 +2242,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/c8/d3/18d73a051ddc14d2eedf6e577462df7c13a23c5696b3b0c312b48d62ae3b/pylibczirw-5.1.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fb/e4/0c4745cd0b31c17bd85d5da697404679f4faa6424aa7b57112277c1e8027/pyqt6-6.10.0-cp39-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/7e/87/465ea8df9936190c133671e07370e17a0fa8fa55308c8742e544cdf3556c/pyqt6-6.10.1-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/0b/82/bbbee33205574f57b17c25059c6b8726a129961b55a1fe34f9216b4b0a70/pyqt6_qt6-6.10.0-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/89/63/e5adf350c1c3123d4865c013f164c5265512fa79f09ad464fb2fdf9f9e61/pyqt6_sip-13.10.2-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/d0/8339b888ad64a3d4e508fed8245a402b503846e1972c10ad60955883dcbb/pytest_qt-4.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl @@ -2294,7 +2294,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/6e/37/abb30db1853b69aed4c32813cf312f301ec3641b8846193be9a6d892d607/vispy-0.15.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/1a/81ac398d35be848f3655893b6260f227b29ca6acf5c2674dc5ed9af63027/xmlschema-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/20/69a0e6058bc5ea74892d089d64dfc3a62ba78917ec5e2cfa70f7c92ba3a5/xmltodict-1.0.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/10/c866e7b0fd57c92a4d5676884b81383005d81f8d7f07f1ac17e9c0ab3643/xsdata-25.7-py3-none-any.whl @@ -2767,12 +2767,12 @@ packages: requires_dist: - numpy>=1.22.2 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl name: bioio - version: 3.0.0 - sha256: c8a796d2ddf0b60fb240603c51c6b7e48fa1b3d8d2da9ade137f67d04f0707c0 + version: 3.2.0 + sha256: b25b424de47b877de184c14b0d0836f2a9c678b0c36a338eb6e526f0f74fa359 requires_dist: - - bioio-base==3.0.0 + - bioio-base~=3.2.0 - dask[array]>=2021.4.1 - numpy>=1.21.0 - ome-types[lxml]>=0.4.0 @@ -2793,15 +2793,16 @@ packages: - linkify-it-py>=2.0.0 ; extra == 'docs' - ipython>=8.4.0 ; extra == 'dev' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl name: bioio-base - version: 3.0.0 - sha256: 278f44aa79bbd50217521d8a4347716d5b07123f892ba7b7d1a6709c86021724 + version: 3.2.0 + sha256: ae37c33bfc5fe7efa89603ea678c6ba1a2e0b9c67a7e64377d4c5d1f0a75724a requires_dist: - dask[array,distributed]>=2021.4.1,!=2022.5.1 - fsspec>=2021.4.0,!=2022.7.0 - numpy>=1.21.0 - - ome-types>=0.2 + - ome-types[pint]>=0.2 + - pint - xarray>=2022.6.0 - pre-commit>=2.20.0 ; extra == 'lint' - coverage>=5.1 ; extra == 'test' @@ -2883,30 +2884,12 @@ packages: - pytest-raises>=0.11 ; extra == 'test' - types-pyyaml>=6.0.12.9 ; extra == 'test' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/a2/d6/823c893ee752ea0261c7fbea073969733f39976516fc264befac358eb6ba/bioio_ome_zarr-3.0.3-py3-none-any.whl - name: bioio-ome-zarr - version: 3.0.3 - sha256: 26a2953e365f22583c70309e34b100bc91289b145094ee56c00d1c1aef956fcf - requires_dist: - - bioio-base>=1.4.0 - - fsspec[http]>=2022.8.0 - - s3fs>=2023.9.0 - - scikit-image!=0.23.0 - - xarray>=0.16.1 - - zarr>=3.1.0 - - pre-commit>=2.20.0 ; extra == 'lint' - - ngff-zarr[validate]>=0.8.2 ; extra == 'test' - - coverage>=5.1 ; extra == 'test' - - pytest>=5.4.3 ; extra == 'test' - - pytest-cov>=2.9.0 ; extra == 'test' - - pytest-raises>=0.11 ; extra == 'test' - requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/21/f3/4c7db4d6fe5711f232358329450b84fdfd5fbc90aeb21b46b1b490a57898/bioio_ome_zarr-3.1.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/29/4e/cacd4aeebf6334e088072b5b97ccc78d20f53eefad1b78fb579017ec1387/bioio_ome_zarr-3.2.0-py3-none-any.whl name: bioio-ome-zarr - version: 3.1.0 - sha256: e4fdd867f85810b6168cc03acf3baeaa8410cb5baf15a754f078b15136ffe59c + version: 3.2.0 + sha256: 1aa2670726f54960d8276a55862d433ae1a2601a3d5d58c7b2a15ea7572a1ab0 requires_dist: - - bioio-base>=1.4.0 + - bioio-base>=3.1.0 - dask!=2025.11.0 - fsspec[http]>=2022.8.0 - s3fs>=2023.9.0 @@ -3152,10 +3135,10 @@ packages: - pytest-xdist ; extra == 'test' - pre-commit ; extra == 'test' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/1d/54/a46920229d12c3a6e9f0081d1bdaeffad23c1826353ace95714faee926e5/dask-2025.11.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/6f/3a/2121294941227c548d4b5f897a8a1b5f4c44a58f5437f239e6b86511d78e/dask-2025.12.0-py3-none-any.whl name: dask - version: 2025.11.0 - sha256: 08c35a8146c05c93b34f83cf651009129c42ee71762da7ca452fb7308641c2b8 + version: 2025.12.0 + sha256: 4213ce9c5d51d6d89337cff69de35d902aa0bf6abdb8a25c942a4d0281f3a598 requires_dist: - click>=8.1 - cloudpickle>=3.0.0 @@ -3163,13 +3146,13 @@ packages: - packaging>=20.0 - partd>=1.4.0 - pyyaml>=5.3.1 - - toolz>=0.10.0 + - toolz>=0.12.0 - importlib-metadata>=4.13.0 ; python_full_version < '3.12' - numpy>=1.24 ; extra == 'array' - dask[array] ; extra == 'dataframe' - pandas>=2.0 ; extra == 'dataframe' - pyarrow>=14.0.1 ; extra == 'dataframe' - - distributed==2025.11.0 ; extra == 'distributed' + - distributed>=2025.12.0,<2025.12.1 ; extra == 'distributed' - bokeh>=3.1.0 ; extra == 'diagnostics' - jinja2>=2.10.3 ; extra == 'diagnostics' - dask[array,dataframe,diagnostics,distributed] ; extra == 'complete' @@ -3220,14 +3203,14 @@ packages: - urllib3>=1.26.5 - zict>=3.0.0 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/46/ec/da78855318971c2be94d0283a41de6941a6b9f16146fb00babc74903ae01/distributed-2025.11.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/87/45/ca760deab4de448e6c0e3860fc187bcc49216eabda379f6ce68065158843/distributed-2025.12.0-py3-none-any.whl name: distributed - version: 2025.11.0 - sha256: 1794ff25b19ba347ccce563fb1dd5898c3bb30f500b15f8c20ad373f6281b30f + version: 2025.12.0 + sha256: 35d18449002ea191e97f7e04a33e16f90c2243486be52d4d0f991072ea06b48a requires_dist: - click>=8.0 - cloudpickle>=3.0.0 - - dask==2025.11.0 + - dask>=2025.12.0,<2025.12.1 - jinja2>=2.10.3 - locket>=1.0.0 - msgpack>=1.0.2 @@ -3235,8 +3218,8 @@ packages: - psutil>=5.8.0 - pyyaml>=5.4.1 - sortedcontainers>=2.0.5 - - tblib>=1.6.0 - - toolz>=0.11.2 + - tblib>=1.6.0,!=3.2.0,!=3.2.1 + - toolz>=0.12.0 - tornado>=6.2.0 - urllib3>=1.26.5 - zict>=3.0.0 @@ -4825,10 +4808,10 @@ packages: - pytest ; extra == 'test' - pytest-cov ; extra == 'test' requires_python: '>=3.6' -- pypi: https://files.pythonhosted.org/packages/67/88/5cda361a3ebd680809f6ef2f52c54e415525a56e4dc9b05b1100d4d36441/napari_plugin_manager-0.1.8-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/76/20/346995cc3d780ad783411ea07a7e3747643878005bb7b1acd17123128932/napari_plugin_manager-0.1.9-py3-none-any.whl name: napari-plugin-manager - version: 0.1.8 - sha256: 0d303fb1c218973c3b6826b3f4b9504b638c6774e7b99ecab2ef75e505dec61e + version: 0.1.9 + sha256: 11556096b881a6f66a11178b0e98438444cec94fbe0d56f4bb38e968cda2d7b4 requires_dist: - npe2 - qtpy @@ -4842,7 +4825,7 @@ packages: - coverage ; extra == 'testing' - flaky ; extra == 'testing' - pytest ; extra == 'testing' - - pytest-qt<4.5 ; extra == 'testing' + - pytest-qt ; extra == 'testing' - virtualenv ; extra == 'testing' - sphinx>6 ; extra == 'docs' - sphinx-autobuild ; extra == 'docs' @@ -4925,15 +4908,15 @@ packages: requires_python: '>=3.10' - pypi: ./ name: ndevio - version: 0.6.1.dev2+g3b37a3718.d20251210 - sha256: 51f84d28b03da8af6312c90e5d121a2fd208a31382b40a6db800234b545e8cf2 + version: 0.6.1.dev8+gbac3698ff + sha256: 9d8b8a38e885c1d111655b743fe5d8c05f5b01fb4b1e821a5da54ed02f0d5916 requires_dist: - bioio-base - bioio-imageio - bioio-ome-tiff>=1.2.0 - bioio-ome-zarr>=3 - bioio-tifffile - - bioio>=2 + - bioio>=3.2.0 - magic-class - magicgui - napari @@ -6351,18 +6334,18 @@ packages: - pyqt6-sip>=13.8,<14 - pyqt6-qt6>=6.9.0,<6.10.0 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/66/48/be73fb730c6f617f456ab73150db384b17a7a08394d7e2ded55f42de8a7b/pyqt6-6.10.0-cp39-abi3-macosx_10_14_universal2.whl +- pypi: https://files.pythonhosted.org/packages/7e/87/465ea8df9936190c133671e07370e17a0fa8fa55308c8742e544cdf3556c/pyqt6-6.10.1-cp39-abi3-win_amd64.whl name: pyqt6 - version: 6.10.0 - sha256: 0eb82f152a83a8ae39f7d3ba580829ff7c0e8179d19d70f396853c10c8ddc5ac + version: 6.10.1 + sha256: 9cc63abb4136f9c71b39381874ca37ba2b8b920085828497176f3ef50fb72ac2 requires_dist: - pyqt6-sip>=13.8,<14 - pyqt6-qt6>=6.10.0,<6.11.0 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/fb/e4/0c4745cd0b31c17bd85d5da697404679f4faa6424aa7b57112277c1e8027/pyqt6-6.10.0-cp39-abi3-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/c8/b6/de44a5e229a1b0e91c997e8d4083636f4c17f6cc740e12c7ae468fe223b9/pyqt6-6.10.1-cp39-abi3-macosx_10_14_universal2.whl name: pyqt6 - version: 6.10.0 - sha256: 8b5e4ea573733017a76bd12ea1b53351fd7f6dc57f8abf4329c4a41fea6dde04 + version: 6.10.1 + sha256: 3c32d738c3fe7434e9008c6aed2897742952a0634383fe5fabaf390139a7726e requires_dist: - pyqt6-sip>=13.8,<14 - pyqt6-qt6>=6.10.0,<6.11.0 @@ -6398,10 +6381,10 @@ packages: version: 13.10.2 sha256: 3dde8024d055f496eba7d44061c5a1ba4eb72fc95e5a9d7a0dbc908317e0888b requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl name: pytest - version: 9.0.1 - sha256: 67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad + version: 9.0.2 + sha256: 711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b requires_dist: - colorama>=0.4 ; sys_platform == 'win32' - exceptiongroup>=1 ; python_full_version < '3.11' @@ -7820,10 +7803,10 @@ packages: version: 1.17.3 sha256: 1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/86/b4/cfa7aa56807dd2d9db0576c3440b3acd51bae6207338ec5610d4878e5c9b/xarray-2025.11.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/d5/e4/62a677feefde05b12a70a4fc9bdc8558010182a801fbcab68cb56c2b0986/xarray-2025.12.0-py3-none-any.whl name: xarray - version: 2025.11.0 - sha256: 986893b995de4a948429356a3897d78e634243c1cac242bd59d03832b9d72dd1 + version: 2025.12.0 + sha256: 9e77e820474dbbe4c6c2954d0da6342aa484e33adaa96ab916b15a786181e970 requires_dist: - numpy>=1.26 - packaging>=24.1 diff --git a/pyproject.toml b/pyproject.toml index 4e70aac..b4d19e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "magic-class", "xarray", "bioio-base", - "bioio>=2", # migrates writers to plugins + "bioio>=3.2.0", # migrates writers to plugins "bioio-ome-zarr>=3", # Pin to v3.x for zarr 3.x compatibility "bioio-ome-tiff>=1.2.0", # minimum for migrated writer "bioio-tifffile", # a backup tifffile reader with better performance than imageio diff --git a/scripts/generate_napari_patterns.py b/scripts/generate_napari_patterns.py new file mode 100644 index 0000000..5b50adf --- /dev/null +++ b/scripts/generate_napari_patterns.py @@ -0,0 +1,46 @@ +import sys +from pathlib import Path + +from ndevio._bioio_plugin_utils import BIOIO_PLUGINS + +# Ensure src is on path so we can import the package in editable installs +repo_root = Path(__file__).resolve().parents[1] +src_path = repo_root / 'src' +out_dir = Path(__file__).resolve().parent +if str(src_path) not in sys.path: + sys.path.insert(0, str(src_path)) + + +def normalize_ext(e: str) -> str: + e = e.strip().lower() + if e.startswith('.'): + e = e[1:] + return e + + +def main(): + exts = set() + for info in BIOIO_PLUGINS.values(): + for e in info.get('extensions', []): + ne = normalize_ext(e) + if ne: + exts.add(ne) + + sorted_exts = sorted(exts) + + # Produce napari YAML snippet (pretty, one-per-line quoted) + patterns = [f"'*.{ext}'" for ext in sorted_exts] + # Break into lines of ~10 entries for readability + lines = [] + chunk = 10 + for i in range(0, len(patterns), chunk): + lines.append(' ' + ', '.join(patterns[i : i + chunk]) + ',') + + snippet_file = out_dir / 'napari_patterns_snippet.txt' + snippet_text = 'filename_patterns: [\n' + '\n'.join(lines) + '\n]\n' + snippet_file.write_text(snippet_text) + print(f'Wrote napari snippet to {snippet_file}') + + +if __name__ == '__main__': + main() diff --git a/scripts/napari_patterns_snippet.txt b/scripts/napari_patterns_snippet.txt new file mode 100644 index 0000000..16bd16d --- /dev/null +++ b/scripts/napari_patterns_snippet.txt @@ -0,0 +1,23 @@ +filename_patterns: [ + '*.1sc', '*.264', '*.265', '*.2fl', '*.3fr', '*.3g2', '*.a64', '*.acff', '*.adp', '*.afi', + '*.afm', '*.aim', '*.al3d', '*.am', '*.amiramesh', '*.amr', '*.amv', '*.apl', '*.apng', '*.arf', + '*.arw', '*.asf', '*.avc', '*.avi', '*.avs', '*.avs2', '*.bay', '*.bif', '*.bin', '*.bip', + '*.bmp', '*.btf', '*.c01', '*.cdg', '*.cfg', '*.cgi', '*.ch5', '*.cif', '*.cr2', '*.crw', + '*.csv', '*.ct', '*.cxd', '*.czi', '*.dat', '*.dcm', '*.dcr', '*.dib', '*.dip', '*.dir', + '*.dm2', '*.dm3', '*.dm4', '*.dng', '*.dnxhd', '*.dti', '*.dv', '*.dvd', '*.eps', '*.erf', + '*.exp', '*.exr', '*.fdf', '*.fff', '*.ffr', '*.fits', '*.flex', '*.fli', '*.frm', '*.gel', + '*.gif', '*.grey', '*.hdf', '*.hdr', '*.hed', '*.his', '*.htd', '*.html', '*.hx', '*.i2i', + '*.icb', '*.ics', '*.ids', '*.if', '*.iiq', '*.im3', '*.img', '*.ims', '*.imt', '*.inr', + '*.ipl', '*.ipm', '*.ipw', '*.ism', '*.jfif', '*.jif', '*.jng', '*.jp2', '*.jpg', '*.jpk', + '*.jpx', '*.l2d', '*.labels', '*.lei', '*.lif', '*.liff', '*.lim', '*.lms', '*.lsm', '*.mcidas', + '*.mdb', '*.mnc', '*.mng', '*.mod', '*.mov', '*.mp4', '*.mpo', '*.mrc', '*.mrw', '*.msp', + '*.msr', '*.mtb', '*.mvd2', '*.naf', '*.nd', '*.nd2', '*.ndpi', '*.ndpis', '*.nef', '*.nhdr', + '*.nii', '*.nii.gz', '*.nrrd', '*.obf', '*.obsep', '*.oib', '*.oif', '*.oir', '*.ome', '*.ome.btf', + '*.ome.tf2', '*.ome.tf8', '*.ome.tif', '*.ome.tiff', '*.ome.xml', '*.par', '*.pbm', '*.pcoraw', '*.pcx', '*.pdf', + '*.pds', '*.pgm', '*.pic', '*.pict', '*.png', '*.pnl', '*.ppm', '*.pr3', '*.ps', '*.psd', + '*.qptiff', '*.r3d', '*.raw', '*.rcpnl', '*.rec', '*.scn', '*.sdt', '*.seq', '*.sif', '*.sld', + '*.sldy', '*.sm2', '*.sm3', '*.spc', '*.spe', '*.spi', '*.spider', '*.stk', '*.stp', '*.svs', + '*.sxm', '*.tf2', '*.tf8', '*.tfr', '*.tga', '*.tif', '*.tiff', '*.tiles.ome.tif', '*.tnb', '*.top', + '*.txt', '*.v', '*.vms', '*.vsi', '*.vws', '*.wat', '*.wlz', '*.xdce', '*.xml', '*.xqd', + '*.xqf', '*.xv', '*.xvthumb', '*.xys', '*.zarr', '*.zfp', '*.zfr', '*.zif', '*.zvi', +] diff --git a/src/ndevio/_bioio_extensions.py b/src/ndevio/_bioio_extensions.py new file mode 100644 index 0000000..dc72671 --- /dev/null +++ b/src/ndevio/_bioio_extensions.py @@ -0,0 +1,224 @@ +BIOIO_IMAGEIO_EXTENSIONS = [ + '264', + '265', + '3fr', + '3g2', + 'A64', + 'IMT', + 'MCIDAS', + 'PCX', + 'SPIDER', + 'XVTHUMB', + 'a64', + 'adp', + 'amr', + 'amv', + 'apng', + 'arw', + 'asf', + 'avc', + 'avi', + 'avs', + 'avs2', + 'bay', + 'bif', + 'bmp', + 'cdg', + 'cgi', + 'cif', + 'ct', + 'dcr', + 'dib', + 'dip', + 'dng', + 'dnxhd', + 'dv', + 'dvd', + 'erf', + 'exr', + 'fff', + 'gif', + 'icb', + 'if', + 'iiq', + 'ism', + 'jif', + 'jfif', + 'jng', + 'jp2', + 'jpg', + 'mov', + 'mp4', + 'mpo', + 'msp', + 'pdf', + 'png', + 'ppm', + 'ps', + 'zif', +] + +BIOIO_BIOFORMATS_EXTENSIONS = [ + '.1sc', + '.2fl', + '.acff', + '.afi', + '.afm', + '.aim', + '.al3d', + '.am', + '.amiramesh', + '.apl', + '.arf', + '.avi', + '.bin', + '.bip', + '.bmp', + '.btf', + '.c01', + '.cfg', + '.ch5', + '.cif', + '.cr2', + '.crw', + '.csv', + '.cxd', + '.czi', + '.dat', + '.dcm', + '.dib', + '.dm2', + '.dm3', + '.dm4', + '.dti', + '.dv', + '.eps ', + '.exp', + '.fdf', + '.fff', + '.ffr', + '.fits', + '.flex', + '.fli', + '.frm', + '.gel', + '.gif', + '.grey', + '.hdf', + '.hdr', + '.hed', + '.his', + '.htd', + '.html', + '.hx', + '.i2i', + '.ics', + '.ids', + '.im3', + '.img', + '.ims', + '.inr', + '.ipl', + '.ipm', + '.ipw', + '.jp2', + '.jpg', + '.jpk', + '.jpx', + '.l2d', + '.labels', + '.lei', + '.lif', + '.liff', + '.lim', + '.lms', + '.lsm', + '.mdb', + '.mnc', + '.mng', + '.mod', + '.mov', + '.mrc', + '.mrw', + '.msr', + '.mtb', + '.mvd2', + '.naf', + '.nd', + '.nd2', + '.ndpi', + '.ndpis', + '.nef', + '.nhdr', + '.nii', + '.nii.gz', + '.nrrd', + '.obf', + '.obsep', + '.oib', + '.oif', + '.oir', + '.ome', + '.ome.btf', + '.ome.tf2', + '.ome.tf8', + '.ome.tif', + '.ome.tiff', + '.ome.xml', + '.par', + '.pbm', + '.pcoraw', + '.pcx', + '.pds', + '.pgm', + '.pic', + '.pict', + '.png', + '.pnl', + '.ppm', + '.pr3', + '.psd', + '.qptiff', + '.r3d', + '.raw', + '.rcpnl', + '.rec', + '.scn', + '.sdt', + '.seq', + '.sif', + '.sld', + '.sm2', + '.sm3', + '.spc', + '.spe', + '.spi', + '.stk', + '.stp', + '.svs', + '.sxm', + '.tf2', + '.tf8', + '.tfr', + '.tga', + '.tif', + '.tiff', + '.tnb', + '.top', + '.txt', + '.v', + '.vms', + '.vsi', + '.vws', + '.wat', + '.wlz', + '.xdce', + '.xml', + '.xqd', + '.xqf', + '.xv', + '.xys', + '.zfp', + '.zfr', + '.zvi', +] diff --git a/src/ndevio/_bioio_plugin_utils.py b/src/ndevio/_bioio_plugin_utils.py index bc7dba7..8049dc1 100644 --- a/src/ndevio/_bioio_plugin_utils.py +++ b/src/ndevio/_bioio_plugin_utils.py @@ -34,6 +34,11 @@ import logging from typing import TYPE_CHECKING +from ._bioio_extensions import ( + BIOIO_BIOFORMATS_EXTENSIONS, + BIOIO_IMAGEIO_EXTENSIONS, +) + if TYPE_CHECKING: from pathlib import Path @@ -44,14 +49,15 @@ # Bioio plugins and their supported extensions # Source: https://github.com/bioio-devs/bioio # -# ORDERING MATTERS: Plugins are listed in priority order (highest priority first). -# This order is used by ReaderPluginManager when selecting readers. -# Priority is based on: -# 1. Metadata preservation quality (OME formats preserve most metadata) -# 2. Reliability and performance for specific formats -# 3. Known issues or limitations +# NOTE: This registry is used for: +# - Installation suggestions (which plugins to install for a file type) +# - Plugin metadata (descriptions, repository links, notes) +# - Identifying core plugins (bundled with bioio) +# +# Reader PRIORITY/ORDERING is handled by bioio itself (see bioio#162). +# bioio uses a deterministic ordering based on extension specificity. BIOIO_PLUGINS = { - # Highest priority: OME formats with excellent metadata preservation + # OME formats with excellent metadata preservation 'bioio-ome-zarr': { 'extensions': ['.zarr'], 'description': 'OME-Zarr files', @@ -69,7 +75,7 @@ 'description': 'Tiled OME-TIFF files', 'repository': 'https://github.com/bioio-devs/bioio-ome-tiled-tiff', }, - # High priority: Format-specific readers with good metadata support + # Format-specific readers with good metadata support 'bioio-tifffile': { 'extensions': ['.tif', '.tiff'], 'description': 'TIFF files (including those without OME metadata)', @@ -101,21 +107,21 @@ 'description': '3i SlideBook files', 'repository': 'https://github.com/bioio-devs/bioio-sldy', }, - # Lower priority: Generic/fallback readers + # Generic/fallback readers 'bioio-imageio': { - 'extensions': ['.bmp', '.gif', '.jpg', '.jpeg', '.png'], + 'extensions': BIOIO_IMAGEIO_EXTENSIONS, 'description': 'Generic image formats (PNG, JPG, etc.)', 'repository': 'https://github.com/bioio-devs/bioio-imageio', 'core': True, }, 'bioio-tiff-glob': { - 'extensions': ['.tiff'], + 'extensions': ['.tif', '.tiff'], 'description': 'TIFF sequences (glob patterns)', 'repository': 'https://github.com/bioio-devs/bioio-tiff-glob', }, - # Lowest priority: Requires external dependencies (Java) + # Requires external dependencies (Java) 'bioio-bioformats': { - 'extensions': ['.oib', '.oif', '.vsi', '.ims', '.lsm', '.stk'], + 'extensions': BIOIO_BIOFORMATS_EXTENSIONS, 'description': 'Proprietary microscopy formats (requires Java)', 'repository': 'https://github.com/bioio-devs/bioio-bioformats', 'note': 'Requires Java Runtime Environment', @@ -131,7 +137,24 @@ _EXTENSION_TO_PLUGIN[ext].append(plugin_name) -def get_reader_by_name(reader_name: str) -> Reader: +def get_installed_plugins() -> set[str]: + """Get names of installed bioio reader plugins. + + Uses importlib.metadata entry_points which is fast and doesn't + require loading any plugins. + + Returns + ------- + set of str + Set of installed plugin names (e.g., {'bioio-ome-tiff', 'bioio-czi'}). + """ + from importlib.metadata import entry_points + + eps = entry_points(group='bioio.readers') + return {ep.name for ep in eps} + + +def get_reader_by_name(reader_name: str) -> type[Reader]: """Import and return Reader class from plugin name. Converts plugin name (e.g., 'bioio-czi') to module name (e.g., 'bioio_czi') @@ -144,7 +167,7 @@ def get_reader_by_name(reader_name: str) -> Reader: Returns ------- - Reader + type[Reader] The Reader class from the plugin module Raises @@ -202,7 +225,7 @@ def format_plugin_installation_message( # No plugins found for this extension if not suggested_plugins: return ( - f"\n\nNo bioio plugins found for '{filename}'.\n" + f"\n\nNo bioio plugins known for reading '{filename}'.\n" 'See https://github.com/bioio-devs/bioio for available plugins.' ) diff --git a/src/ndevio/_napari_reader.py b/src/ndevio/_napari_reader.py index 6cbb310..e7f144f 100644 --- a/src/ndevio/_napari_reader.py +++ b/src/ndevio/_napari_reader.py @@ -1,14 +1,12 @@ from __future__ import annotations import logging -from collections.abc import Callable from functools import partial from typing import TYPE_CHECKING -from bioio_base.exceptions import UnsupportedFileFormatError from ndev_settings import get_settings -from .nimage import determine_reader_plugin, nImage +from .nimage import nImage if TYPE_CHECKING: from napari.types import LayerDataTuple, PathLike, ReaderFunction @@ -25,6 +23,10 @@ def napari_get_reader( """ Get the appropriate reader function for a single given path. + Uses a quick extension check to determine if any bioio plugin might + support this file. If so, returns a reader function that will use + bioio's priority system to try each installed reader. + Parameters ---------- path : PathLike @@ -42,10 +44,25 @@ def napari_get_reader( Returns ------- - ReaderFunction - The reader function for the given path + ReaderFunction or None + The reader function for the given path, or None if extension + is not recognized by any bioio plugin. """ + from ._bioio_plugin_utils import suggest_plugins_for_path + + if isinstance(path, list): + logger.info('Bioio: Expected a single path, got a list of paths.') + return None + + # Quick extension check - if no plugins recognize this extension, return None + # This allows other napari readers to try the file + # TODO: This is probably legacy cruft before starting to autopopulate the filename_patterns in napari.yaml + suggested = suggest_plugins_for_path(path) + if not suggested: + logger.debug('ndevio: No bioio plugins for extension: %s', path) + return None + settings = get_settings() open_first_scene_only = ( @@ -60,51 +77,32 @@ def napari_get_reader( else settings.ndevio_reader.scene_handling == 'View All Scenes' # type: ignore ) or False - if isinstance(path, list): - logger.info('Bioio: Expected a single path, got a list of paths.') - return None - - try: - reader = determine_reader_plugin(path) - return partial( - napari_reader_function, - reader=reader, # type: ignore - in_memory=in_memory, - open_first_scene_only=open_first_scene_only, - open_all_scenes=open_all_scenes, - ) - - except UnsupportedFileFormatError as e: - # determine_reader_plugin() already enhanced the error message - logger.error('ndevio: Unsupported file format: %s', path) - # Show plugin installer widget if enabled in settings - if settings.ndevio_reader.suggest_reader_plugins: # type: ignore - _open_plugin_installer(path, e) - - # Return None per napari reader spec - don't raise exception - return None - - except Exception as e: # noqa: BLE001 - logger.warning('ndevio: Error reading file: %s', e) - return None + # Extension is recognized - return a reader function + # The actual reading happens in napari_reader_function + return partial( + napari_reader_function, + in_memory=in_memory, + open_first_scene_only=open_first_scene_only, + open_all_scenes=open_all_scenes, + ) def napari_reader_function( path: PathLike, - reader: Callable, in_memory: bool | None = None, open_first_scene_only: bool = False, open_all_scenes: bool = False, ) -> list[LayerDataTuple] | None: """ - Read a file using the given reader function. + Read a file using bioio. + + nImage handles reader selection: if a preferred_reader is set in settings, + it's tried first with automatic fallback to bioio's default plugin ordering. Parameters ---------- path : PathLike Path to the file to be read - reader : Callable - Bioio Reader class to be used to read the file. in_memory : bool, optional Whether to read the file in memory, by default None. open_first_scene_only : bool, optional @@ -120,7 +118,16 @@ def napari_reader_function( List containing image data, metadata, and layer type """ - img = nImage(path, reader=reader) + from bioio_base.exceptions import UnsupportedFileFormatError + + try: + img = nImage(path) # nImage handles preferred reader and fallback + except UnsupportedFileFormatError: + # Try to open plugin installer widget + # If no viewer available, this will re-raise + _open_plugin_installer(path) + return None + logger.info('Bioio: Reading file with %d scenes', len(img.scenes)) # open first scene only @@ -137,11 +144,11 @@ def napari_reader_function( # else: open scene widget _open_scene_container(path=path, img=img, in_memory=in_memory) - return [(None,)] + return [(None,)] # type: ignore[return-value] def _open_scene_container( - path: PathLike, img: nImage, in_memory: bool + path: PathLike, img: nImage, in_memory: bool | None ) -> None: from pathlib import Path @@ -157,20 +164,24 @@ def _open_scene_container( ) -def _open_plugin_installer( - path: PathLike, error: UnsupportedFileFormatError -) -> None: +def _open_plugin_installer(path: PathLike) -> None: """Open the plugin installer widget for an unsupported file. + If no napari viewer is available, re-raises the UnsupportedFileFormatError + with installation suggestions so programmatic users get a helpful message. + Parameters ---------- path : PathLike Path to the file that couldn't be read - error : UnsupportedFileFormatError - The error that was raised - """ + Raises + ------ + UnsupportedFileFormatError + If no napari viewer is available (programmatic usage) + """ import napari + from bioio_base.exceptions import UnsupportedFileFormatError from ._plugin_manager import ReaderPluginManager from .widgets import PluginInstallerWidget @@ -178,12 +189,17 @@ def _open_plugin_installer( # Get viewer, handle case where no viewer available viewer = napari.current_viewer() - # Don't try to open widget if no viewer available (e.g., in tests) + # If no viewer, re-raise with helpful message for programmatic users if viewer is None: - logger.warning( - 'Cannot open plugin installer widget: No napari viewer available' + logger.debug( + 'No napari viewer available, raising exception with suggestions' + ) + manager = ReaderPluginManager(path) + raise UnsupportedFileFormatError( + reader_name='ndevio', + path=str(path), + msg_extra=manager.get_installation_message(), ) - return # Create plugin manager for this file manager = ReaderPluginManager(path) diff --git a/src/ndevio/_plugin_manager.py b/src/ndevio/_plugin_manager.py index f663b4f..2c82018 100644 --- a/src/ndevio/_plugin_manager.py +++ b/src/ndevio/_plugin_manager.py @@ -1,15 +1,16 @@ """Centralized manager for bioio reader plugin detection and recommendations. -This module provides a unified interface for managing three categories of plugins: -1. Available plugins - All known bioio plugins (from BIOIO_PLUGINS) -2. Installed plugins - Plugins currently installed in the environment -3. Suggested/installable plugins - Plugins that could read a specific file +This module provides a unified interface for: +1. Suggesting plugins based on file extension +2. Detecting which plugins are installed vs need to be installed +3. Generating helpful installation messages -The ReaderPluginManager class eliminates code duplication and provides a clean -API for both core functionality and widgets. +bioio handles reader priority and fallback internally (bioio#162). +This module focuses on user-friendly installation suggestions. Public API: ReaderPluginManager - Main class for plugin management + get_installed_plugins - Fast detection of installed bioio plugins Example: >>> from ndevio._plugin_manager import ReaderPluginManager @@ -17,126 +18,62 @@ >>> # Create manager for a specific file >>> manager = ReaderPluginManager("image.czi") >>> - >>> # Check what plugins could read this file - >>> print(manager.suggested_plugins) - >>> - >>> # Check what's installed - >>> print(manager.installed_plugins) - >>> - >>> # Get plugins that need to be installed + >>> # Check what plugins could be installed >>> print(manager.installable_plugins) - >>> - >>> # Try to get a working reader - >>> reader = manager.get_working_reader(preferred_reader="bioio-czi") + >>> print(manager.get_installation_message()) """ from __future__ import annotations import logging -from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING +from ._bioio_plugin_utils import get_installed_plugins + if TYPE_CHECKING: - from bioio.plugins import PluginSupport - from bioio_base.reader import Reader from napari.types import PathLike logger = logging.getLogger(__name__) class ReaderPluginManager: - """Centralized manager for bioio reader plugin detection and recommendations. - - This class handles three plugin categories: - 1. Available plugins - All known bioio plugins (from BIOIO_PLUGINS) - 2. Installed plugins - Plugins currently installed in environment - 3. Suggested plugins - Plugins that could read a specific file + """Manager for bioio reader plugin detection and installation suggestions. - The manager caches expensive operations (like plugin_feasibility_report) - and provides a clean API for widgets and core logic. + bioio handles reader priority and fallback internally (see bioio#162). + This class focuses on: + - Detecting which plugins are installed + - Suggesting plugins to install based on file extension + - Generating helpful installation messages Parameters ---------- path : PathLike, optional Path to the file for which to manage plugins. If None, manager - operates in standalone mode (e.g., for browsing all available plugins). - - Attributes - ---------- - path : Path or None - Path to the file being managed + operates in standalone mode. Examples -------- - >>> # For a specific file >>> manager = ReaderPluginManager("image.czi") - >>> if manager.installed_plugins: - ... reader = manager.get_working_reader() - >>> else: + >>> if manager.installable_plugins: ... print(manager.get_installation_message()) - >>> - >>> # Standalone mode (no file) - >>> manager = ReaderPluginManager() - >>> all_plugins = manager.available_plugins """ def __init__(self, path: PathLike | None = None): - """Initialize manager, optionally for a specific file path. - - Parameters - ---------- - path : PathLike, optional - Path to the file for plugin detection. If None, operates in - standalone mode. - """ self.path = Path(path) if path is not None else None - @property - def known_plugins(self) -> list[str]: - """Get all known bioio plugin names from BIOIO_PLUGINS. - - Returns - ------- - list of str - List of plugin names (e.g., ['bioio-czi', 'bioio-ome-tiff', ...]). - """ - from ._bioio_plugin_utils import BIOIO_PLUGINS - - return list(BIOIO_PLUGINS.keys()) - - @cached_property - def feasibility_report(self) -> dict[str, PluginSupport]: - """Get cached feasibility report for current path. - - The feasibility report from bioio.plugin_feasibility_report() shows - which installed plugins can read the file. This property caches the - result to avoid expensive repeated calls. - - Returns - ------- - dict - Mapping of plugin names to PluginSupport objects. Empty dict if - no path is set. - """ - if not self.path: - return {} - - from bioio import plugin_feasibility_report - - return plugin_feasibility_report(self.path) - @property def installed_plugins(self) -> set[str]: """Get names of installed bioio plugins. + Uses entry_points for fast lookup without loading plugins. + Returns ------- set of str - Set of installed plugin names (excludes "ArrayLike"). + Set of installed plugin names. """ - report = self.feasibility_report - return {name for name in report if name != 'ArrayLike'} + return get_installed_plugins() @property def suggested_plugins(self) -> list[str]: @@ -183,81 +120,6 @@ def installable_plugins(self) -> list[str]: and plugin_name not in installed ] - def get_working_reader( - self, preferred_reader: str | None = None - ) -> Reader | None: - """Get a reader that can actually read this file. - - Tries readers in priority order: - 1. Preferred reader if it's installed and supported - 2. Readers from BIOIO_PLUGINS dict order (highest priority first) - 3. Any other installed reader that supports the file - 4. None if no working reader found - - Parameters - ---------- - preferred_reader : str, optional - Name of preferred reader to try first (e.g., "bioio-ome-tiff") - - Returns - ------- - Reader or None - Reader class that can read the file, or None if no suitable - reader is installed. - - Notes - ----- - The priority order is determined by the ordering of BIOIO_PLUGINS - in _bioio_plugin_utils.py, which prioritizes readers based on - metadata preservation quality, reliability, and known issues. - """ - from ._bioio_plugin_utils import get_reader_by_name - - if not self.path: - logger.warning( - 'Cannot get working reader without a path. ' - 'Initialize ReaderPluginManager with a file path.' - ) - return None - - report = self.feasibility_report - - # Try preferred reader first - if ( - preferred_reader - and preferred_reader in report - and report[preferred_reader].supported - ): - logger.info( - 'Using preferred reader: %s for %s', - preferred_reader, - self.path, - ) - return get_reader_by_name(preferred_reader) - - # Try readers in priority order from BIOIO_PLUGINS - for reader_name in self.known_plugins: - if reader_name in report and report[reader_name].supported: - logger.info( - 'Using reader: %s for %s (from priority list)', - reader_name, - self.path, - ) - return get_reader_by_name(reader_name) - - # Try any other installed reader that supports the file - for name, support in report.items(): - if name != 'ArrayLike' and support.supported: - logger.info( - 'Using reader: %s for %s (from installed plugins)', - name, - self.path, - ) - return get_reader_by_name(name) - - logger.warning('No working reader found for: %s', self.path) - return None - def get_installation_message(self) -> str: """Generate helpful message for missing plugins. diff --git a/src/ndevio/napari.yaml b/src/ndevio/napari.yaml index 1ccbe41..b8b97ef 100644 --- a/src/ndevio/napari.yaml +++ b/src/ndevio/napari.yaml @@ -37,39 +37,27 @@ contributions: - command: ndevio.get_reader accepts_directories: false filename_patterns: [ - '*.1sc', '*.2fl', '*.3fr', '*.acff', '*.acqp', '*.afi', '*.afm', '*.aim', '*.al3d', - '*.ali', '*.am', '*.amiramesh', '*.ano', '*.apl', '*.arf', '*.array-like', '*.arw', - '*.avi', '*.bay', '*.bif', '*.bin', '*.bip', '*.bmp', '*.bmq', '*.bsdf', '*.bufr', - '*.bw', '*.c01', '*.cap', '*.cat', '*.cfg', '*.ch5', '*.cif', '*.cine', '*.cr2', - '*.crw', '*.cs1', '*.csv', '*.ct', '*.ct.img', '*.cur', '*.cut', '*.cxd', '*.czi', - '*.dat', '*.db', '*.dc2', '*.dcm', '*.dcr', '*.dcx', '*.dds', '*.df3', '*.dicom', - '*.dm2', '*.dm3', '*.dng', '*.drf', '*.dsc', '*.dti', '*.dv', '*.ecw', '*.emf', - '*.eps', '*.epsi', '*.erf', '*.exp', '*.exr', '*.fake', '*.fdf', '*.fff', '*.ffr', - '*.fid', '*.fit', '*.fits', '*.flc', '*.flex', '*.fli', '*.fpx', '*.frm', '*.ftc', - '*.fts', '*.ftu', '*.fz', '*.g3', '*.gbr', '*.gdcm', '*.gel', '*.gif', '*.gipl', - '*.grey', '*.grib', '*.h5', '*.hdf', '*.hdf5', '*.hdp', '*.hdr', '*.hed', '*.his', - '*.htd', '*.htm', '*.html', '*.hx', '*.i2i', '*.ia', '*.icns', '*.ico', '*.ics', - '*.ids', '*.iff', '*.iim', '*.iiq', '*.im', '*.im3', '*.img', '*.imggz', '*.ims', - '*.inf', '*.inr', '*.ipl', '*.ipm', '*.ipw', '*.j2c', '*.j2k', '*.jfif', '*.jif', - '*.jng', '*.jp2', '*.jpc', '*.jpe', '*.jpeg', '*.jpf', '*.jpg', '*.jpk', '*.jpx', - '*.jxr', '*.k25', '*.kc2', '*.kdc', '*.klb', '*.koa', '*.l2d', '*.labels', '*.lbm', - '*.lei', '*.lfp', '*.lfr', '*.lif', '*.liff', '*.lim', '*.lms', '*.lsm', '*.mdb', - '*.mdc', '*.mef', '*.mgh', '*.mha', '*.mhd', '*.mic', '*.mkv', '*.mnc', '*.mnc2', - '*.mng', '*.mod', '*.mos', '*.mov', '*.mp4', '*.mpeg', '*.mpg', '*.mpo', '*.mrc', - '*.mri', '*.mrw', '*.msp', '*.msr', '*.mtb', '*.mvd2', '*.naf', '*.nd', '*.nd2', - '*.ndpi', '*.ndpis', '*.nef', '*.nhdr', '*.nia', '*.nii', '*.nii.gz', '*.niigz', - '*.npz', '*.nrrd', '*.nrw', '*.obf', '*.oib', '*.oif', '*.oir', '*.ome', '*.ome.tif', - '*.ome.tiff', '*.orf', '*.par', '*.pbm', '*.pcd', '*.pcoraw', '*.pct', '*.pcx', - '*.pef', '*.pfm', '*.pgm', '*.pic', '*.pict', '*.png', '*.pnl', '*.ppm', '*.pr3', - '*.ps', '*.psd', '*.ptx', '*.pxn', '*.pxr', '*.qptiff', '*.qtk', '*.r3d', '*.raf', - '*.ras', '*.raw', '*.rcpnl', '*.rdc', '*.rec', '*.rgb', '*.rgba', '*.rw2', '*.rwl', - '*.rwz', '*.scan', '*.scn', '*.sdt', '*.seq', '*.sif', '*.sld', '*.sm2', '*.sm3', - '*.spc', '*.spe', '*.spi', '*.sr2', '*.srf', '*.srw', '*.st', '*.sti', '*.stk', - '*.stp', '*.svs', '*.swf', '*.sxm', '*.targa', '*.tfr', '*.tga', '*.thm', '*.tif', - '*.tiff', '*.tim', '*.tnb', '*.top', '*.txt', '*.v', '*.vff', '*.vms', '*.vsi', - '*.vtk', '*.vws', '*.wap', '*.wat', '*.wav', '*.wbm', '*.wbmp', '*.wdp', '*.webp', - '*.wlz', '*.wmf', '*.wmv', '*.wpi', '*.xbm', '*.xdce', '*.xml', '*.xpm', '*.xqd', - '*.xqf', '*.xv', '*.xys', '*.zfp', '*.zfr', '*.zip', '*.zpo', '*.zvi' + '*.1sc', '*.264', '*.265', '*.2fl', '*.3fr', '*.3g2', '*.a64', '*.acff', '*.adp', '*.afi', + '*.afm', '*.aim', '*.al3d', '*.am', '*.amiramesh', '*.amr', '*.amv', '*.apl', '*.apng', '*.arf', + '*.arw', '*.asf', '*.avc', '*.avi', '*.avs', '*.avs2', '*.bay', '*.bif', '*.bin', '*.bip', + '*.bmp', '*.btf', '*.c01', '*.cdg', '*.cfg', '*.cgi', '*.ch5', '*.cif', '*.cr2', '*.crw', + '*.csv', '*.ct', '*.cxd', '*.czi', '*.dat', '*.dcm', '*.dcr', '*.dib', '*.dip', '*.dir', + '*.dm2', '*.dm3', '*.dm4', '*.dng', '*.dnxhd', '*.dti', '*.dv', '*.dvd', '*.eps', '*.erf', + '*.exp', '*.exr', '*.fdf', '*.fff', '*.ffr', '*.fits', '*.flex', '*.fli', '*.frm', '*.gel', + '*.gif', '*.grey', '*.hdf', '*.hdr', '*.hed', '*.his', '*.htd', '*.html', '*.hx', '*.i2i', + '*.icb', '*.ics', '*.ids', '*.if', '*.iiq', '*.im3', '*.img', '*.ims', '*.imt', '*.inr', + '*.ipl', '*.ipm', '*.ipw', '*.ism', '*.jfif', '*.jif', '*.jng', '*.jp2', '*.jpg', '*.jpk', + '*.jpx', '*.l2d', '*.labels', '*.lei', '*.lif', '*.liff', '*.lim', '*.lms', '*.lsm', '*.mcidas', + '*.mdb', '*.mnc', '*.mng', '*.mod', '*.mov', '*.mp4', '*.mpo', '*.mrc', '*.mrw', '*.msp', + '*.msr', '*.mtb', '*.mvd2', '*.naf', '*.nd', '*.nd2', '*.ndpi', '*.ndpis', '*.nef', '*.nhdr', + '*.nii', '*.nii.gz', '*.nrrd', '*.obf', '*.obsep', '*.oib', '*.oif', '*.oir', '*.ome', '*.ome.btf', + '*.ome.tf2', '*.ome.tf8', '*.ome.tif', '*.ome.tiff', '*.ome.xml', '*.par', '*.pbm', '*.pcoraw', '*.pcx', '*.pdf', + '*.pds', '*.pgm', '*.pic', '*.pict', '*.png', '*.pnl', '*.ppm', '*.pr3', '*.ps', '*.psd', + '*.qptiff', '*.r3d', '*.raw', '*.rcpnl', '*.rec', '*.scn', '*.sdt', '*.seq', '*.sif', '*.sld', + '*.sldy', '*.sm2', '*.sm3', '*.spc', '*.spe', '*.spi', '*.spider', '*.stk', '*.stp', '*.svs', + '*.sxm', '*.tf2', '*.tf8', '*.tfr', '*.tga', '*.tif', '*.tiff', '*.tiles.ome.tif', '*.tnb', '*.top', + '*.txt', '*.v', '*.vms', '*.vsi', '*.vws', '*.wat', '*.wlz', '*.xdce', '*.xml', '*.xqd', + '*.xqf', '*.xv', '*.xvthumb', '*.xys', '*.zarr', '*.zfp', '*.zfr', '*.zif', '*.zvi', ] widgets: - command: ndevio.make_plugin_installer_widget diff --git a/src/ndevio/ndev_settings.yaml b/src/ndevio/ndev_settings.yaml index 310b4ae..b4c567f 100644 --- a/src/ndevio/ndev_settings.yaml +++ b/src/ndevio/ndev_settings.yaml @@ -4,12 +4,12 @@ ndevio_reader: tooltip: Whether to suggest plugins to install when no reader can be found value: true preferred_reader: - default: bioio-ome-tiff + default: null dynamic_choices: fallback_message: No readers found provider: bioio.readers - tooltip: Preferred reader to use when opening images - value: bioio-ome-tiff + tooltip: Preferred reader to try first when opening images. + value: null scene_handling: choices: - Open Scene Widget diff --git a/src/ndevio/nimage.py b/src/ndevio/nimage.py index 1925de5..0de8f9e 100644 --- a/src/ndevio/nimage.py +++ b/src/ndevio/nimage.py @@ -3,6 +3,7 @@ from __future__ import annotations import logging +from collections.abc import Sequence from pathlib import Path from typing import TYPE_CHECKING @@ -10,7 +11,7 @@ from bioio import BioImage from bioio_base.dimensions import DimensionNames from bioio_base.reader import Reader -from bioio_base.types import ImageLike +from bioio_base.types import ImageLike, PathLike if TYPE_CHECKING: from napari.types import LayerDataTuple @@ -23,71 +24,6 @@ LABEL_KEYWORDS = ['label', 'mask', 'segmentation', 'seg', 'roi'] -def determine_reader_plugin( - image: ImageLike, preferred_reader: str | None = None -) -> Reader: - """ - Determine the reader plugin to use for loading an image. - - Convenience wrapper that integrates ReaderPluginManager with ndevio settings. - For file paths, uses the priority system (DEFAULT_READER_PRIORITY). - For arrays, uses bioio's default plugin detection. - - Parameters - ---------- - image : ImageLike - Image to be loaded (file path, numpy array, or xarray DataArray). - preferred_reader : str, optional - Preferred reader name. If None, uses ndevio_reader.preferred_reader - from settings. - - Returns - ------- - Reader - Reader class to use for loading the image. - - Raises - ------ - UnsupportedFileFormatError - If no suitable reader can be found. Error message includes - installation suggestions for missing plugins. - - """ - from bioio_base.exceptions import UnsupportedFileFormatError - from ndev_settings import get_settings - - from ._plugin_manager import ReaderPluginManager - - # For file paths: use ReaderPluginManager - if isinstance(image, str | Path): - settings = get_settings() - - # Get preferred reader from settings if not provided - if preferred_reader is None: - preferred_reader = settings.ndevio_reader.preferred_reader # type: ignore - - manager = ReaderPluginManager(image) - reader = manager.get_working_reader(preferred_reader) - - if reader: - return reader - - # No reader found - raise error with installation suggestions - if settings.ndevio_reader.suggest_reader_plugins: # type: ignore - msg_extra = manager.get_installation_message() - else: - msg_extra = None - - raise UnsupportedFileFormatError( - reader_name='ndevio', - path=str(image), - msg_extra=msg_extra, - ) - - # For arrays: use bioio's built-in plugin determination - return nImage.determine_plugin(image).metadata.get_reader() - - class nImage(BioImage): """ An nImage is a BioImage with additional functionality for napari. @@ -97,9 +33,13 @@ class nImage(BioImage): image : ImageLike Image to be loaded. Can be a path to an image file, a numpy array, or an xarray DataArray. - reader : Reader, optional - Reader to be used to load the image. If not provided, a reader will be - determined based on the image type. + reader : type[Reader] or Sequence[type[Reader]], optional + Reader class or priority list of readers. If not provided, checks + settings for preferred_reader and tries that first. If the preferred + reader fails, falls back to bioio's default priority. If no preferred + reader is set, uses bioio's deterministic plugin ordering directly. + **kwargs + Additional arguments passed to BioImage. Attributes ---------- @@ -108,42 +48,92 @@ class nImage(BioImage): See BioImage for inherited attributes. """ - def __init__(self, image: ImageLike, reader: Reader | None = None) -> None: - """ - Initialize an nImage with an image, and optionally a reader. - - If a reader is not provided, a reader will be determined by bioio. - However, if the image is supported by the preferred reader, the reader - will be set to preferred_reader.Reader to override the softer decision - made by bioio.BioImage.determine_plugin(). - - Note: The issue here is that bioio.BioImage.determine_plugin() will - sort by install time and choose the first plugin that supports the - image. This is not always the desired behavior, because bioio-tifffile - can take precedence over bioio-ome-tiff, even if the image was saved - as an OME-TIFF via bioio.writers.OmeTiffWriter (which is the case - for napari-ndev). - - Note: If no suitable reader can be found, an UnsupportedFileFormatError - will be raised, with installation suggestions for missing plugins. - """ + def __init__( + self, + image: ImageLike, + reader: type[Reader] | Sequence[type[Reader]] | None = None, + **kwargs, + ) -> None: + """Initialize an nImage with an image, and optionally a reader.""" + from bioio_base.exceptions import UnsupportedFileFormatError from ndev_settings import get_settings self.settings = get_settings() - if reader is None: - reader = determine_reader_plugin(image) + # If no explicit reader and we have a file path, check for preferred + if reader is None and isinstance(image, (str | Path)): + reader = self._get_preferred_reader_list() + + try: + if reader is not None: + # Try preferred/explicit reader first + try: + super().__init__(image=image, reader=reader, **kwargs) + except UnsupportedFileFormatError: + # Preferred reader failed, fall back to bioio's default + # errors in this will propagate to outer except, bubbling + # up the suggestion error message + super().__init__(image=image, reader=None, **kwargs) + else: + # No preferred reader, use bioio's default priority + super().__init__(image=image, reader=None, **kwargs) + except UnsupportedFileFormatError: + # Only add installation suggestions for file paths + # For arrays/other types, just let bioio's error propagate + if isinstance(image, (str | Path)): + self._raise_with_suggestions(image) + raise - super().__init__(image=image, reader=reader) # type: ignore self.napari_layer_data: xr.DataArray | None = None self.layer_data_tuples: list[tuple] | None = None - self.path = image if isinstance(image, str | Path) else None + self.path = image if isinstance(image, (str | Path)) else None + + def _get_preferred_reader_list(self) -> list[type[Reader]] | None: + """Get preferred reader as a list (for fallback) or None. + + Returns the Reader class if set and installed, else None. + When returned, __init__ will try this reader first and fall back + to bioio's default priority if it fails. + """ + from ._bioio_plugin_utils import ( + get_installed_plugins, + get_reader_by_name, + ) + + pref = self.settings.ndevio_reader.preferred_reader # type: ignore + if not pref: + return None + + # in case a legacy settings file exists with a plugin that is not present in a new environment + if pref not in get_installed_plugins(): + logger.debug('Preferred reader %s not installed', pref) + return None + + return [get_reader_by_name(pref)] + + def _raise_with_suggestions(self, path: PathLike) -> None: + """Raise UnsupportedFileFormatError with installation suggestions.""" + from bioio_base.exceptions import UnsupportedFileFormatError + + from ._plugin_manager import ReaderPluginManager + + manager = ReaderPluginManager(path) + if self.settings.ndevio_reader.suggest_reader_plugins: # type: ignore + msg_extra = manager.get_installation_message() + else: + msg_extra = None + + raise UnsupportedFileFormatError( + reader_name='ndevio', + path=str(path), + msg_extra=msg_extra, + ) from None def _determine_in_memory( self, path=None, - max_in_mem_bytes: int = 4e9, - max_in_mem_percent: int = 0.3, + max_in_mem_bytes: float = 4e9, + max_in_mem_percent: float = 0.3, ) -> bool: """ Determine whether the image should be loaded into memory or not. diff --git a/src/ndevio/widgets/_plugin_install_widget.py b/src/ndevio/widgets/_plugin_install_widget.py index 7443cc9..c76ad69 100644 --- a/src/ndevio/widgets/_plugin_install_widget.py +++ b/src/ndevio/widgets/_plugin_install_widget.py @@ -13,6 +13,8 @@ from magicgui.widgets import ComboBox, Container, Label, PushButton +from .._bioio_plugin_utils import BIOIO_PLUGINS + if TYPE_CHECKING: from .._plugin_manager import ReaderPluginManager @@ -93,8 +95,7 @@ def _init_widgets(self): self._info_label = Label(value='Select a plugin to install:') self.append(self._info_label) - # Get all available plugin names from manager - plugin_names = self.manager.known_plugins + plugin_names = list(BIOIO_PLUGINS.keys()) self._plugin_select = ComboBox( label='Plugin', diff --git a/tests/test_bioio_plugin_utils.py b/tests/test_bioio_plugin_utils.py index 06d8cc9..f11d466 100644 --- a/tests/test_bioio_plugin_utils.py +++ b/tests/test_bioio_plugin_utils.py @@ -15,10 +15,10 @@ class TestSuggestPluginsForPath: @pytest.mark.parametrize( ('filename', 'expected_plugins'), [ - ('test.czi', ['bioio-czi']), - ('test.lif', ['bioio-lif']), - ('test.nd2', ['bioio-nd2']), - ('test.dv', ['bioio-dv']), + ('test.czi', ['bioio-czi', 'bioio-bioformats']), + ('test.lif', ['bioio-lif', 'bioio-bioformats']), + ('test.nd2', ['bioio-nd2', 'bioio-bioformats']), + ('test.dv', ['bioio-dv', 'bioio-bioformats']), ('test.xyz', []), # Unsupported returns empty ], ) diff --git a/tests/test_napari_reader.py b/tests/test_napari_reader.py index 3688b47..400a5ca 100644 --- a/tests/test_napari_reader.py +++ b/tests/test_napari_reader.py @@ -1,7 +1,6 @@ from __future__ import annotations from pathlib import Path -from unittest.mock import patch import dask.array as da @@ -199,36 +198,13 @@ def test_napari_get_reader_ome_override(resources_dir: Path) -> None: def test_napari_get_reader_unsupported(resources_dir: Path): """Test that unsupported file extension returns None per napari reader spec.""" - # Mock the widget opener since we don't have a viewer in this test - with patch('ndevio._napari_reader._open_plugin_installer') as mock_opener: - reader = napari_get_reader( - str(resources_dir / 'measure_props_Labels.abcdefg'), - ) - - # Should return None for unsupported formats (per napari spec) - assert reader is None - - # Plugin installer widget should have been called - assert mock_opener.called - # Check the error message contains the extension - error_arg = mock_opener.call_args[0][1] - error_msg = str(error_arg) - assert '.abcdefg' in error_msg or 'abcdefg' in error_msg - - -def test_napari_get_reader_general_exception(caplog): - """Test that general exceptions in determine_reader_plugin are handled correctly.""" - test_path = 'non_existent_file.xyz' - - # Mock determine_reader_plugin to raise an exception - with patch('ndevio._napari_reader.determine_reader_plugin') as mock_reader: - mock_reader.side_effect = Exception('Test exception') - - reader = napari_get_reader(test_path) - assert reader is None + reader = napari_get_reader( + str(resources_dir / 'measure_props_Labels.abcdefg'), + ) - assert 'ndevio: Error reading file' in caplog.text - assert 'Test exception' in caplog.text + # Should return None for unsupported formats (per napari spec) + # Unknown extensions are quickly rejected via suggest_plugins_for_path() + assert reader is None def test_napari_get_reader_supported_formats_work(resources_dir: Path): @@ -257,26 +233,24 @@ def test_napari_get_reader_supported_formats_work(resources_dir: Path): (ND2_FILE, 'bioio-nd2'), # ND2 needs bioio-nd2 ], ) -def test_napari_get_reader_unsupported_formats_helpful_errors( +def test_napari_reader_missing_plugin_error( resources_dir: Path, filename: str, expected_plugin_in_error: str ): - """Test that unsupported formats return None per napari reader spec. + """Test that reading a file without the required plugin raises helpful error. - napari readers should return None (not raise) when they can't read a file. - The plugin installer widget should be shown via settings if enabled. + When extension is recognized but required plugin isn't installed, + nImage raises UnsupportedFileFormatError with installation suggestions. """ - # Mock the widget opener since we don't have a viewer in this test - with patch('ndevio._napari_reader._open_plugin_installer') as mock_opener: - reader = napari_get_reader(str(resources_dir / filename)) - - # Should return None for unsupported formats (per napari spec) - assert reader is None - - # Plugin installer widget should have been called with error info - assert mock_opener.called - call_args = mock_opener.call_args - # Check the UnsupportedFileFormatError message contains plugin suggestion - error_arg = call_args[0][1] # Second argument is the exception - error_msg = str(error_arg) - assert expected_plugin_in_error in error_msg - assert 'pip install' in error_msg or 'conda install' in error_msg + from bioio_base.exceptions import UnsupportedFileFormatError + + # napari_get_reader returns a reader function since extension is known + reader = napari_get_reader(str(resources_dir / filename)) + assert callable(reader) + + # But actually reading fails with helpful error message + with pytest.raises(UnsupportedFileFormatError) as exc_info: + reader(str(resources_dir / filename)) + + error_msg = str(exc_info.value) + assert expected_plugin_in_error in error_msg + assert 'pip install' in error_msg or 'conda install' in error_msg diff --git a/tests/test_nimage.py b/tests/test_nimage.py index 8aa7122..2f223c6 100644 --- a/tests/test_nimage.py +++ b/tests/test_nimage.py @@ -4,11 +4,7 @@ from pathlib import Path from unittest import mock - -try: - import bioio_tifffile -except ImportError: # pragma: no cover - optional test dependency - bioio_tifffile = None +from unittest.mock import MagicMock, patch import pytest from bioio_base.exceptions import UnsupportedFileFormatError @@ -51,28 +47,12 @@ def test_nImage_ome_reader(resources_dir: Path): img_path = resources_dir / CELLS3D2CH_OME_TIFF nimg = nImage(img_path) - assert nimg.settings.ndevio_reader.preferred_reader == 'bioio-ome-tiff' + # assert nimg.settings.ndevio_reader.preferred_reader == 'bioio-ome-tiff' # this was the old methodology before bioio#162 + assert nimg.reader.name == 'bioio_ome_tiff' # the below only exists if 'bioio-ome-tiff' is used assert hasattr(nimg, 'ome_metadata') assert nimg.channel_names == ['membrane', 'nuclei'] - # Additional check that the reader override works when bioio_tifffile is - # available. The project does not require bioio_tifffile as a test - # dependency, so skip this part when it's missing. - if bioio_tifffile is None: # pragma: no cover - optional - pytest.skip( - 'bioio_tifffile not installed; skipping reader-override checks' - ) - - nimg = nImage(img_path, reader=bioio_tifffile.Reader) - - # check that despite preferred reader, the reader is still bioio_tifffile - # because there is no ome_metadata - assert nimg.settings.ndevio_reader.preferred_reader == 'bioio-ome-tiff' - # check that calling nimg.ome_metadata raises NotImplementedError - with pytest.raises(NotImplementedError): - _ = nimg.ome_metadata - def test_nImage_save_read(resources_dir: Path, tmp_path: Path): """ @@ -291,7 +271,7 @@ def test_nimage_init_with_various_formats( ): """Test nImage initialization with various file formats. - This tests the complete workflow: file → determine_reader_plugin → nImage init + This tests the complete workflow: file → get_reader_priority → nImage init """ if should_work is True: # Must successfully initialize @@ -602,3 +582,228 @@ def test_channel_kwargs_override_metadata(self, resources_dir: Path): assert ( layer_tuples[1][1]['colormap'] == 'green' ) # default for 2-channel + + +class TestPreferredReaderFallback: + """Tests for preferred reader fallback logic in nImage.__init__.""" + + def test_preferred_reader_success(self, resources_dir: Path): + """Test that preferred reader is used when it works.""" + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + # Mock returning a valid reader + from bioio_tifffile import Reader + + mock_get.return_value = [Reader] + + img = nImage(str(resources_dir / 'cells3d2ch_legacy.tiff')) + + # Verify preferred reader was attempted + mock_get.assert_called_once() + assert img is not None + assert img.reader.name == 'bioio_tifffile' + + def test_preferred_reader_fallback(self, resources_dir: Path): + """Test that failed preferred reader will fallback""" + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + # Mock returning a valid reader + from bioio_czi import Reader + + mock_get.return_value = [Reader] + + img = nImage(str(resources_dir / 'cells3d2ch_legacy.tiff')) + + # Verify preferred reader was attempted + mock_get.assert_called_once() + assert img is not None + assert img.reader.name == 'bioio_ome_tiff' + + def test_no_preferred_reader_uses_default(self, resources_dir: Path): + """Test that no preferred reader uses bioio's default priority.""" + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + mock_get.return_value = None # No preferred reader + + img = nImage(str(resources_dir / 'cells3d2ch_legacy.tiff')) + assert img is not None + mock_get.assert_called_once() + assert img.reader.name == 'bioio_ome_tiff' + + +class TestGetPreferredReaderList: + """Tests for _get_preferred_reader_list method.""" + + def test_returns_none_when_no_preferred_reader(self, resources_dir: Path): + """Test returns None when preferred_reader is not set.""" + with patch('ndev_settings.get_settings') as mock_settings: + mock_settings.return_value.ndevio_reader.preferred_reader = None + + img = nImage.__new__(nImage) + img.settings = mock_settings.return_value + + result = img._get_preferred_reader_list() + assert result is None + + def test_returns_none_when_preferred_not_installed( + self, resources_dir: Path + ): + """Test returns None when preferred reader is not installed.""" + with ( + patch('ndev_settings.get_settings') as mock_settings, + patch( + 'ndevio._bioio_plugin_utils.get_installed_plugins', + return_value={'bioio-ome-tiff', 'bioio-tifffile'}, + ), + ): + mock_settings.return_value.ndevio_reader.preferred_reader = ( + 'bioio-czi' + ) + + img = nImage.__new__(nImage) + img.settings = mock_settings.return_value + + result = img._get_preferred_reader_list() + assert result is None + + def test_returns_reader_when_preferred_installed( + self, resources_dir: Path + ): + """Test returns reader class when preferred reader is installed.""" + with ( + patch('ndev_settings.get_settings') as mock_settings, + patch( + 'ndevio._bioio_plugin_utils.get_installed_plugins', + return_value={'bioio-ome-tiff'}, + ), + patch( + 'ndevio._bioio_plugin_utils.get_reader_by_name' + ) as mock_get_reader, + ): + from bioio_ome_tiff import Reader as OmeTiffReader + + mock_get_reader.return_value = OmeTiffReader + mock_settings.return_value.ndevio_reader.preferred_reader = ( + 'bioio-ome-tiff' + ) + + img = nImage.__new__(nImage) + img.settings = mock_settings.return_value + + result = img._get_preferred_reader_list() + assert result == [OmeTiffReader] + mock_get_reader.assert_called_once_with('bioio-ome-tiff') + + +class TestRaiseWithSuggestions: + """Tests for _raise_with_suggestions method.""" + + def test_raises_with_suggestions_enabled(self): + """Test error message includes suggestions when enabled.""" + with patch('ndev_settings.get_settings') as mock_settings: + mock_settings.return_value.ndevio_reader.suggest_reader_plugins = ( + True + ) + + img = nImage.__new__(nImage) + img.settings = mock_settings.return_value + + with pytest.raises(UnsupportedFileFormatError) as exc_info: + img._raise_with_suggestions('test.czi') + + error_msg = str(exc_info.value) + assert 'bioio-czi' in error_msg + + def test_raises_without_suggestions_when_disabled(self): + """Test error message has no suggestions when disabled.""" + with patch('ndev_settings.get_settings') as mock_settings: + mock_settings.return_value.ndevio_reader.suggest_reader_plugins = ( + False + ) + + img = nImage.__new__(nImage) + img.settings = mock_settings.return_value + + with pytest.raises(UnsupportedFileFormatError) as exc_info: + img._raise_with_suggestions('test.czi') + + error_msg = str(exc_info.value) + # Should still mention the file but not include installation message + assert 'test.czi' in error_msg + + +class TestNonPathImageHandling: + """Tests for handling non-path inputs (arrays).""" + + def test_array_input_no_preferred_reader_check(self): + """Test that arrays don't trigger preferred reader logic.""" + import numpy as np + + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + # Create a simple array + arr = np.zeros((10, 10), dtype=np.uint8) + + # This should work without calling _get_preferred_reader_list + img = nImage(arr) + assert img is not None + # Should not call _get_preferred_reader_list for arrays + mock_get.assert_not_called() + + def test_unsupported_array_raises_without_suggestions(self): + """Test that unsupported arrays raise error without plugin suggestions.""" + # Create something that will fail + with pytest.raises(UnsupportedFileFormatError) as exc_info: + # Pass an invalid object + nImage('this_is_not_a_valid_input.fake') + + # Error should be raised but without custom suggestions since it's not a path + error_msg = str(exc_info.value) + assert ( + 'fake' in error_msg.lower() or 'unsupported' in error_msg.lower() + ) + + +class TestExplicitReaderParameter: + """Tests for when reader is explicitly provided.""" + + def test_explicit_reader_bypasses_preferred(self, resources_dir: Path): + """Test that explicit reader parameter bypasses preferred reader.""" + from bioio_tifffile import Reader as TifffileReader + + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + # Explicit reader should bypass preferred + img = nImage( + str(resources_dir / 'cells3d2ch_legacy.tiff'), + reader=TifffileReader, + ) + + assert img is not None + # Should not check for preferred reader when explicit reader provided + mock_get.assert_not_called() + + def test_explicit_reader_fails_falls_back(self, resources_dir: Path): + """Test explicit reader that fails falls back to default.""" + mock_reader = MagicMock() + mock_reader.side_effect = UnsupportedFileFormatError( + reader_name='mock', path='test' + ) + + with patch( + 'ndevio.nimage.nImage._get_preferred_reader_list' + ) as mock_get: + # Even with explicit reader that fails, should fall back + img = nImage( + str(resources_dir / 'cells3d2ch_legacy.tiff'), + reader=mock_reader, + ) + + assert img is not None + # Should not check preferred when explicit reader provided + mock_get.assert_not_called() diff --git a/tests/test_plugin_installer_widget.py b/tests/test_plugin_installer_widget.py index f0b2a0c..2d999ab 100644 --- a/tests/test_plugin_installer_widget.py +++ b/tests/test_plugin_installer_widget.py @@ -6,7 +6,7 @@ """ from pathlib import Path -from unittest.mock import Mock, patch +from unittest.mock import patch import pytest @@ -15,31 +15,36 @@ class TestPluginInstallerWidget: """Tests for PluginInstallerWidget behavior.""" def test_standalone_mode(self): - """Test widget in standalone mode - no path, shows all plugins.""" - from ndevio._bioio_plugin_utils import BIOIO_PLUGINS + """Test widget in standalone mode - no path, shows generic title.""" from ndevio.widgets import PluginInstallerWidget widget = PluginInstallerWidget() - # Standalone mode: no path, generic title, all plugins available + # Standalone mode: no path, generic title assert widget.manager.path is None assert 'Install BioIO Reader Plugin' in widget._title_label.value - assert set(widget.manager.known_plugins) == set(BIOIO_PLUGINS.keys()) def test_error_mode_with_path(self): """Test widget in error mode - has path, preselects suggested plugin.""" from ndevio._plugin_manager import ReaderPluginManager from ndevio.widgets import PluginInstallerWidget - with patch('bioio.plugin_feasibility_report') as mock_report: - mock_report.return_value = {'ArrayLike': Mock(supported=False)} + # Mock installed plugins to NOT include bioio-czi + # This simulates the error case where file can't be read + with patch( + 'ndevio._plugin_manager.get_installed_plugins', + return_value={'bioio-ome-tiff'}, + ): manager = ReaderPluginManager('test.czi') widget = PluginInstallerWidget(plugin_manager=manager) - # Error mode: has path, shows filename, preselects installable plugin - assert 'test.czi' in widget._title_label.value - assert 'bioio-czi' in widget.manager.installable_plugins - assert widget._plugin_select.value == 'bioio-czi' + # Error mode: has path, shows filename, preselects installable plugin + assert 'test.czi' in widget._title_label.value + assert 'bioio-czi' in manager.suggested_plugins + # bioio-czi should be in installable since it's not installed + assert 'bioio-czi' in manager.installable_plugins + # Value should be set to first installable plugin + assert widget._plugin_select.value is not None def test_install_button_behavior(self): """Test install button: queues installation and updates status.""" @@ -73,19 +78,12 @@ class TestOpenPluginInstallerIntegration: @pytest.fixture def viewer_with_plugin_installer(self, make_napari_viewer): """Fixture that creates viewer and opens plugin installer for .czi.""" - from bioio_base.exceptions import UnsupportedFileFormatError - import ndevio._napari_reader as reader_module viewer = make_napari_viewer() test_path = Path('path/to/test.czi') - error = UnsupportedFileFormatError( - reader_name='test', path=str(test_path), msg_extra='' - ) - with patch('bioio.plugin_feasibility_report') as mock_report: - mock_report.return_value = {'ArrayLike': Mock(supported=False)} - reader_module._open_plugin_installer(test_path, error) + reader_module._open_plugin_installer(test_path) # Find the widget widget = None @@ -109,4 +107,4 @@ def test_docks_widget_with_correct_state( # Widget has correct path and suggestions assert widget.manager.path == test_path assert test_path.name in widget._title_label.value - assert 'bioio-czi' in widget.manager.installable_plugins + assert 'bioio-czi' in widget.manager.suggested_plugins diff --git a/tests/test_plugin_manager.py b/tests/test_plugin_manager.py index 3d9757b..10583e8 100644 --- a/tests/test_plugin_manager.py +++ b/tests/test_plugin_manager.py @@ -4,96 +4,123 @@ via TestSuggestPluginsForPath. We trust those unit tests and don't duplicate here. """ -import logging -from unittest.mock import Mock, patch +from unittest.mock import patch + + +class TestGetInstalledPlugins: + """Tests for get_installed_plugins module-level function.""" + + def test_returns_set_of_strings(self): + """Test that get_installed_plugins returns a set of strings.""" + from ndevio._bioio_plugin_utils import get_installed_plugins + + result = get_installed_plugins() + + assert isinstance(result, set) + for item in result: + assert isinstance(item, str) + + def test_includes_core_plugins(self): + """Test that installed plugins include core bioio plugins.""" + from ndevio._bioio_plugin_utils import get_installed_plugins + + result = get_installed_plugins() + + # At minimum, one core plugin should be present + core_plugins = {'bioio-ome-tiff', 'bioio-ome-zarr', 'bioio-tifffile'} + assert len(result & core_plugins) > 0 class TestReaderPluginManager: """Tests for ReaderPluginManager properties and methods.""" - def test_known_plugins_matches_bioio_plugins_registry(self): - """Test that known_plugins returns all plugins from BIOIO_PLUGINS.""" - from ndevio._bioio_plugin_utils import BIOIO_PLUGINS + def test_installed_plugins_returns_set(self): + """Test that installed_plugins returns a set of plugin names.""" from ndevio._plugin_manager import ReaderPluginManager - manager = ReaderPluginManager() + manager = ReaderPluginManager('test.tiff') + + assert isinstance(manager.installed_plugins, set) + assert len(manager.installed_plugins) > 0 + + def test_installed_plugins_matches_module_function(self): + """Test installed_plugins matches get_installed_plugins().""" + from ndevio._bioio_plugin_utils import get_installed_plugins + from ndevio._plugin_manager import ReaderPluginManager + + manager = ReaderPluginManager('test.tiff') + + assert manager.installed_plugins == get_installed_plugins() - assert set(manager.known_plugins) == set(BIOIO_PLUGINS.keys()) + def test_suggested_plugins_for_tiff(self): + """Test suggested_plugins returns relevant plugins for tiff.""" + from ndevio._plugin_manager import ReaderPluginManager + + manager = ReaderPluginManager('test.tiff') + suggested = manager.suggested_plugins - def test_installed_plugins_from_feasibility_report(self): - """Test that installed_plugins filters to bioio plugins from report.""" + assert 'bioio-ome-tiff' in suggested + assert 'bioio-tifffile' in suggested + + def test_suggested_plugins_for_czi(self): + """Test suggested_plugins returns bioio-czi for .czi files.""" from ndevio._plugin_manager import ReaderPluginManager - with patch('bioio.plugin_feasibility_report') as mock_report: - mock_report.return_value = { - 'bioio-czi': Mock(supported=False), - 'bioio-ome-tiff': Mock(supported=True), - 'ArrayLike': Mock(supported=False), # Not a bioio plugin - } - manager = ReaderPluginManager('test.czi') + manager = ReaderPluginManager('test.czi') + suggested = manager.suggested_plugins - # Only bioio-* plugins should be included - assert 'bioio-czi' in manager.installed_plugins - assert 'bioio-ome-tiff' in manager.installed_plugins - assert 'ArrayLike' not in manager.installed_plugins + assert 'bioio-czi' in suggested def test_installable_excludes_installed_and_core(self): """Test installable_plugins excludes installed and core plugins.""" from ndevio._plugin_manager import ReaderPluginManager - with patch('bioio.plugin_feasibility_report') as mock_report: - # Simulate: bioio-ome-tiff installed, nothing else - mock_report.return_value = { - 'bioio-ome-tiff': Mock(supported=True), - 'ArrayLike': Mock(supported=False), - } - manager = ReaderPluginManager('test.tiff') - installable = manager.installable_plugins - - # Core plugins never in installable (even if "suggested" by extension) - assert 'bioio-ome-tiff' not in installable - assert 'bioio-tifffile' not in installable - # Non-core tiff plugin should be installable - assert 'bioio-tiff-glob' in installable - - def test_feasibility_report_cached(self): - """Test that feasibility_report is cached via @cached_property.""" - from ndevio._plugin_manager import ReaderPluginManager + manager = ReaderPluginManager('test.tiff') + installable = manager.installable_plugins - with patch('bioio.plugin_feasibility_report') as mock_report: - mock_report.return_value = {'ArrayLike': Mock(supported=False)} - manager = ReaderPluginManager('test.czi') + # Core plugins never in installable + assert 'bioio-ome-tiff' not in installable + assert 'bioio-tifffile' not in installable + assert 'bioio-imageio' not in installable + assert 'bioio-tiff-glob' in installable + + # Already installed plugins should not be in installable + installed = manager.installed_plugins + for plugin in installable: + assert plugin not in installed + + def test_get_installation_message_with_installable(self): + """Test installation message is generated for installable plugins.""" + from ndevio._plugin_manager import ReaderPluginManager - # Access multiple times - _ = manager.feasibility_report - _ = manager.feasibility_report - _ = manager.installed_plugins # Also uses feasibility_report + # Mock installed plugins to NOT include bioio-nd2 + with patch( + 'ndevio._plugin_manager.get_installed_plugins', + return_value={'bioio-ome-tiff', 'bioio-tifffile'}, + ): + manager = ReaderPluginManager('test.nd2') + message = manager.get_installation_message() - # Should only call bioio once - assert mock_report.call_count == 1 + assert 'bioio-nd2' in message + assert 'pip install' in message class TestReaderPluginManagerNoPath: """Tests for ReaderPluginManager edge cases when no path provided.""" - def test_feasibility_report_empty_without_path(self): - """Test that feasibility_report returns {} without path.""" + def test_suggested_plugins_empty_without_path(self): + """Test suggested_plugins returns [] without path.""" from ndevio._plugin_manager import ReaderPluginManager manager = ReaderPluginManager() - assert manager.feasibility_report == {} + assert manager.suggested_plugins == [] - def test_get_working_reader_returns_none_with_warning(self, caplog): - """Test get_working_reader returns None and logs warning without path.""" + def test_installable_plugins_empty_without_path(self): + """Test installable_plugins returns [] without path.""" from ndevio._plugin_manager import ReaderPluginManager manager = ReaderPluginManager() - - with caplog.at_level(logging.WARNING): - result = manager.get_working_reader() - - assert result is None - assert 'Cannot get working reader without a path' in caplog.text + assert manager.installable_plugins == [] def test_get_installation_message_returns_empty(self): """Test get_installation_message returns '' without path.""" @@ -106,12 +133,23 @@ def test_get_installation_message_returns_empty(self): class TestReaderPluginManagerWithRealFiles: """Tests using real files to verify end-to-end behavior.""" - def test_get_working_reader_for_ome_tiff(self, resources_dir): - """Test get_working_reader returns correct reader for OME-TIFF.""" + def test_suggested_plugins_for_real_tiff(self, resources_dir): + """Test suggested_plugins with a real TIFF file.""" + from ndevio._plugin_manager import ReaderPluginManager + + manager = ReaderPluginManager(resources_dir / 'cells3d2ch_legacy.tiff') + suggested = manager.suggested_plugins + + assert 'bioio-ome-tiff' in suggested + assert 'bioio-tifffile' in suggested + + def test_installed_plugins_includes_core(self, resources_dir): + """Test installed_plugins includes core plugins.""" from ndevio._plugin_manager import ReaderPluginManager manager = ReaderPluginManager(resources_dir / 'cells3d2ch_legacy.tiff') - reader = manager.get_working_reader() + installed = manager.installed_plugins - assert reader is not None - assert 'ome_tiff' in reader.__module__ + # At least one core plugin should be installed + core_plugins = {'bioio-ome-tiff', 'bioio-ome-zarr', 'bioio-tifffile'} + assert len(installed & core_plugins) > 2 diff --git a/uv.lock b/uv.lock index 38625c3..e59efba 100644 --- a/uv.lock +++ b/uv.lock @@ -329,7 +329,7 @@ wheels = [ [[package]] name = "bioio" -version = "3.0.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bioio-base" }, @@ -338,25 +338,26 @@ dependencies = [ { name = "ome-types", extra = ["lxml"] }, { name = "semver" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/95/0f9c15d0e329da74d63ad8495ddb6563d53ee27bc4eea9dc471f59648015/bioio-3.0.0.tar.gz", hash = "sha256:8be25df70b611e62d1f1c3b11b47e039f3626e08e17e0dc792d824be3e4aeb89", size = 59699, upload-time = "2025-07-03T17:55:48.525Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/29/0534e35d8bcbe24b7626625352acb2d606fb3f290c2e39fa0e1085f08b67/bioio-3.2.0.tar.gz", hash = "sha256:731211b43b3c5438cbf597f915a98dcd1be29814b0a67e5a1e89ab38fe746538", size = 60876, upload-time = "2025-12-22T20:02:40.715Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/02/c6713b1d38fdf6b705265e8def466ed22f62c0684b8ee030809ad396924e/bioio-3.0.0-py3-none-any.whl", hash = "sha256:c8a796d2ddf0b60fb240603c51c6b7e48fa1b3d8d2da9ade137f67d04f0707c0", size = 56626, upload-time = "2025-07-03T17:55:47.088Z" }, + { url = "https://files.pythonhosted.org/packages/ae/5f/96a446d850b19d1bdb5bbb0d49eec2efa6b396cd5efb6d1e5d84cf03cad9/bioio-3.2.0-py3-none-any.whl", hash = "sha256:b25b424de47b877de184c14b0d0836f2a9c678b0c36a338eb6e526f0f74fa359", size = 58721, upload-time = "2025-12-22T20:02:39.245Z" }, ] [[package]] name = "bioio-base" -version = "3.0.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dask", extra = ["array", "distributed"] }, { name = "fsspec" }, { name = "numpy" }, - { name = "ome-types" }, + { name = "ome-types", extra = ["pint"] }, + { name = "pint" }, { name = "xarray" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/fd/5fc014f5ca15579bbeaf3bebb15b49efc9e743a4877d078bcc4ba500479b/bioio_base-3.0.0.tar.gz", hash = "sha256:47ba5aa50b9c068116b0b505f8c5816c346fc7ad1c65116052c6a734e662d680", size = 95694, upload-time = "2025-06-27T21:30:46.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/64a855e25b89384b703255e10c12f0ccaa9862e58f1f73b642d8fbdb696e/bioio_base-3.2.0.tar.gz", hash = "sha256:f935e19ae243e9cdb1c46ae9c5ac2a19343345f6be7a74255e292c7188a37c68", size = 96797, upload-time = "2025-12-08T22:35:38.617Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/63/68e0e98001f5572c8734c7c99950a803aeeba72249f180ef1d5796bb0141/bioio_base-3.0.0-py3-none-any.whl", hash = "sha256:278f44aa79bbd50217521d8a4347716d5b07123f892ba7b7d1a6709c86021724", size = 99966, upload-time = "2025-06-27T21:30:44.875Z" }, + { url = "https://files.pythonhosted.org/packages/c4/44/178363ad21c93bb94ac5d9abf03c39fb3a43d5849c97d6a213efbb77f318/bioio_base-3.2.0-py3-none-any.whl", hash = "sha256:ae37c33bfc5fe7efa89603ea678c6ba1a2e0b9c67a7e64377d4c5d1f0a75724a", size = 100971, upload-time = "2025-12-08T22:35:37.076Z" }, ] [[package]] @@ -1224,7 +1225,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.13'" }, + { name = "zipp", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [ @@ -2136,6 +2137,7 @@ dependencies = [ { name = "natsort" }, { name = "nbatch" }, { name = "ndev-settings" }, + { name = "pooch" }, { name = "xarray" }, { name = "zarr" }, ] @@ -2157,7 +2159,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "bioio", specifier = ">=2" }, + { name = "bioio", specifier = ">=3.2.0" }, { name = "bioio-base" }, { name = "bioio-imageio" }, { name = "bioio-ome-tiff", specifier = ">=1.2.0" }, @@ -2171,6 +2173,7 @@ requires-dist = [ { name = "natsort" }, { name = "nbatch", specifier = ">=0.0.4" }, { name = "ndev-settings", specifier = ">=0.4.0" }, + { name = "pooch" }, { name = "xarray" }, { name = "zarr", specifier = ">=3.1.3" }, ] @@ -2394,6 +2397,9 @@ wheels = [ lxml = [ { name = "lxml" }, ] +pint = [ + { name = "pint" }, +] [[package]] name = "packaging"