From 5ec1a5ba7da9b92c3cf926e3c989c84c10687c6d Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Tue, 7 Jan 2025 14:02:39 -0500 Subject: [PATCH] Update repo (#69) * chore: update pyproject * update pyproject and linting * update readme * update tests * remove tox * install backend * Update pyproject.toml Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com> * change deploy * Update .github/workflows/ci.yml Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com> * add more explicit deps * bump setup mini * small cleanup * use miniforge * Update .github/workflows/ci.yml to use conda not mamba * Update README.md Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com> --------- Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com> --- .github/workflows/ci.yml | 96 ++++++++++++-------------- .pre-commit-config.yaml | 14 ++-- README.md | 18 ++--- pyproject.toml | 75 ++++++++++++-------- src/napari_omero/__init__.py | 5 +- src/napari_omero/plugins/_napari.py | 7 +- src/napari_omero/plugins/loaders.py | 20 +++--- src/napari_omero/plugins/masks.py | 7 +- src/napari_omero/plugins/omero.py | 30 ++++---- src/napari_omero/utils.py | 5 +- src/napari_omero/widgets/__init__.py | 2 +- src/napari_omero/widgets/gateway.py | 14 ++-- src/napari_omero/widgets/main.py | 3 +- src/napari_omero/widgets/thumb_grid.py | 4 +- src/napari_omero/widgets/tree_model.py | 7 +- tests/test_widget.py | 5 +- tox.ini | 41 ----------- 17 files changed, 159 insertions(+), 194 deletions(-) delete mode 100644 tox.ini diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c177c1b..9b2221e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,19 +2,18 @@ name: CI on: push: - branches: - - main - tags: - - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 + branches: [main] + tags: ["v*"] # Push events to matching v*, i.e. v1.0, v20.15.10 pull_request: - branches: - - main + branches: [main] workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check-manifest: - # check-manifest is a tool that checks that all files in version control are - # included in the sdist (unless explicitly excluded) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -29,72 +28,65 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10"] - backend: [pyqt5] - include: - - platform: ubuntu-latest - python-version: 3.7 - backend: pyqt5 + platform: [ubuntu-latest, macos-13, macos-latest, windows-latest] + python-version: ["3.9", "3.10"] + backend: [PyQt5] steps: - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: "latest" - channels: conda-forge - channel-priority: strict + miniforge-version: "latest" python-version: ${{ matrix.python-version }} - - uses: tlambert03/setup-qt-libs@v1 - - - name: Install Windows OpenGL - if: runner.os == 'Windows' - run: | - git clone --depth 1 https://github.com/pyvista/gl-ci-helpers.git - powershell gl-ci-helpers/appveyor/install_opengl.ps1 + - uses: pyvista/setup-headless-display-action@v3 + with: + qt: true - name: Install dependencies run: | + conda install omero-py python -m pip install --upgrade pip - python -m pip install --upgrade setuptools tox tox-conda tox-gh-actions + python -m pip install .[test] + python -m pip install ${{ matrix.backend }} - - name: Test with tox - uses: aganders3/headless-gui@v1.2 - with: - shell: bash -el {0} - run: python -m tox - env: - PLATFORM: ${{ matrix.platform }} - PYTHON: ${{ matrix.python-version }} - PYVISTA_OFF_SCREEN: True - BACKEND: ${{ matrix.backend }} + - name: Test + run: pytest --color=yes --cov=napari_omero --cov-report=xml - name: Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} deploy: # this will run when you have tagged a commit, starting with "v*" # and requires that you have put your twine API key in your needs: [test] - if: github.repository == 'tlambert03/napari-omero' && contains(github.ref, 'tags') + if: success() && startsWith(github.ref, 'refs/tags/') && github.repository == 'tlambert03/napari-omero' runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + steps: - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 + with: + fetch-depth: 0 + + - name: 🐍 Set up Python + uses: actions/setup-python@v5 with: python-version: "3.x" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U build twine - - name: Build and publish - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.pypi_password }} + + - name: 👷 Build run: | - git tag + python -m pip install build python -m build - twine check dist/* - twine upload dist/* + + - name: 🚢 Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + - uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: './dist/*' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8e2ddc8..ff7fdba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,23 +5,19 @@ ci: repos: - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.12.2 + rev: v0.23 hooks: - id: validate-pyproject - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.263 + rev: v0.8.6 hooks: - id: ruff - args: [--fix] - - - repo: https://github.com/psf/black - rev: 23.3.0 - hooks: - - id: black + args: [--fix, --unsafe-fixes] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.2.0 + rev: v1.14.1 hooks: - id: mypy files: "^src/" diff --git a/README.md b/README.md index 6086373..17b8b4b 100644 --- a/README.md +++ b/README.md @@ -83,14 +83,16 @@ omero napari view Image:1 ## installation -Requires python 3.7 - 3.10. +While this package supports anything above python 3.9, +In practice, python support is limited by `omero-py` and `zeroc-ice`, +compatibility, which is limited to python <=3.10 at the time of writing. ### from conda It's easiest to install `omero-py` from conda, so the recommended procedure is to install everything from conda, using the `conda-forge` channel -```python +```sh conda install -c conda-forge napari-omero ``` @@ -100,7 +102,7 @@ conda install -c conda-forge napari-omero `omero-py` ```sh -conda create -n omero -c conda-forge python=3.9 omero-py +conda create -n omero -c conda-forge python=3.10 omero-py conda activate omero pip install napari-omero[all] # the [all] here is the same as `napari[all]` ``` @@ -136,15 +138,15 @@ conda env create -n napari-omero python=3.10 omero-py # activate the new env conda activate napari-omero -# install in editable mode -pip install -e ".[all, dev, test]" # quotes are needed on zsh +# install in editable mode with dev dependencies +pip install -e ".[dev]" # quotes are needed on zsh ``` To maintain good code quality, this repo uses [ruff](https://github.com/astral-sh/ruff), -[mypy](https://github.com/python/mypy), and -[black](https://github.com/psf/black). To enforce code quality when you commit -code, you can install pre-commit +[mypy](https://github.com/python/mypy). + +To enforce code quality when you commit code, you can install pre-commit ```bash # install pre-commit which will run code checks prior to commits diff --git a/pyproject.toml b/pyproject.toml index 97fff91..5250324 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,8 @@ build-backend = "hatchling.build" source = "vcs" [tool.hatch.build.targets.wheel] -packages = ["src/napari_omero", "src/omero/plugins"] +only-include = ["src"] +sources = ["src"] # https://peps.python.org/pep-0621/ [project] @@ -15,15 +16,17 @@ name = "napari-omero" dynamic = ["version"] description = "napari/OMERO interoperability" readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.9" license = { text = "GPL-2.0-or-later" } authors = [ { name = "Talley Lambert", email = "talley.lambert@gmail.com" }, - { name = "Will Moore" }, + { name = "Will Moore", email = "w.moore@dundee.ac.uk" }, + { name = "Johannes Soltwedel", email = "johannes_richard.soltwedel@tu-dresden.de" }, + { name = "Peter Sobolewski" }, ] keywords = ["OMERO.CLI", "plugin", "napari", "napari-plugin"] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Environment :: X11 Applications :: Qt", "Framework :: napari", "Intended Audience :: Developers", @@ -33,34 +36,35 @@ classifiers = [ "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Natural Language :: English", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Scientific/Engineering :: Bio-Informatics", "Topic :: Scientific/Engineering :: Information Analysis", "Topic :: Scientific/Engineering :: Visualization", "Topic :: Scientific/Engineering", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities", - # "License :: OSI Approved :: BSD License", - # "Typing :: Typed", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", + "Typing :: Typed", ] # add your package dependencies here dependencies = [ "napari>=0.4.13", "omero-py", "omero-rois", - "importlib-metadata; python_version < '3.8'", + # these come with napari ... + # but are directly imported here as well so are included explicitly + "qtpy>=1.10.0", + "dask[array]>=2021.10.0", + "vispy>=0.14.1,<0.15", + "superqt>=0.6.7", ] # https://peps.python.org/pep-0621/#dependencies-optional-dependencies # "extras" (e.g. for `pip install .[test]`) [project.optional-dependencies] all = ["napari[all]"] -# add dependencies used for testing here test = [ "pytest", "pytest-cov", @@ -68,13 +72,13 @@ test = [ "pytest-regressions", "pywin32; sys_platform == 'win32'", ] -# add anything else you like to have in your dev environment here dev = [ - "black", + "napari-omero[all, test]", "ipython", "mypy", - "pdbpp", # https://github.com/pdbpp/pdbpp - "rich", # https://github.com/Textualize/rich + "pdbpp", + "pre-commit", + "rich", "ruff", ] @@ -91,34 +95,46 @@ napari-omero = "napari_omero.__main__:main" [project.entry-points."napari.manifest"] napari-omero = "napari_omero:napari.yaml" -# https://beta.ruff.rs/docs/rules/ +# https://docs.astral.sh/ruff [tool.ruff] line-length = 88 -target-version = "py37" +target-version = "py39" +src = ["src"] + +# https://docs.astral.sh/ruff/rules +[tool.ruff.lint] +pydocstyle = { convention = "numpy" } select = [ "E", # style errors "W", # style warnings "F", # flakes + "D", # pydocstyle + "D417", # Missing argument descriptions in Docstrings "I", # isort "UP", # pyupgrade + "C4", # flake8-comprehensions "B", # flake8-bugbear "A001", # flake8-builtins "RUF", # ruff-specific rules + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports ] ignore = [ - "D100", # Missing docstring in public module - "D107", # Missing docstring in __init__ - "D203", # 1 blank line required before class docstring - "D212", # Multi-line docstring summary should start at the first line - "D213", # Multi-line docstring summary should start at the second line "D401", # First line should be in imperative mood - "D413", # Missing blank line after last section - "D416", # Section name should end with a colon + "D100", # Missing docstring in public module + "D101", # Missing docstring in public class + "D102", # Missing docstring in public method + "D103", # Missing docstring in public function + "D104", # Missing docstring in public package ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "tests/*.py" = ["D", "S"] -"setup.py" = ["D"] + +# https://docs.astral.sh/ruff/formatter/ +[tool.ruff.format] +docstring-code-format = true +skip-magic-trailing-comma = false # default is false # https://mypy.readthedocs.io/en/stable/config_file.html [tool.mypy] @@ -134,7 +150,7 @@ pretty = true # https://docs.pytest.org/en/6.2.x/customize.html [tool.pytest.ini_options] -minversion = "6.0" +minversion = "7.0" testpaths = ["tests"] filterwarnings = [ "error", @@ -155,6 +171,7 @@ exclude_lines = [ "except ImportError", "\\.\\.\\.", "raise NotImplementedError()", + "pass", ] [tool.coverage.run] source = ["src"] diff --git a/src/napari_omero/__init__.py b/src/napari_omero/__init__.py index e211eef..282158c 100644 --- a/src/napari_omero/__init__.py +++ b/src/napari_omero/__init__.py @@ -1,7 +1,4 @@ -try: - from importlib.metadata import PackageNotFoundError, version -except ModuleNotFoundError: - from importlib_metadata import PackageNotFoundError, version # type: ignore +from importlib.metadata import PackageNotFoundError, version try: __version__ = version("napari-omero") diff --git a/src/napari_omero/plugins/_napari.py b/src/napari_omero/plugins/_napari.py index fc2fd1f..5820724 100644 --- a/src/napari_omero/plugins/_napari.py +++ b/src/napari_omero/plugins/_napari.py @@ -1,12 +1,13 @@ import os from functools import partial -from typing import Callable, List, Optional, Union +from typing import Callable, Optional, Union + +from napari_omero.utils import get_proxy_obj, parse_omero_url -from ..utils import get_proxy_obj, parse_omero_url from .loaders import omero_proxy_reader, omero_url_reader -def napari_get_reader(path: Union[str, List[str]]) -> Optional[Callable]: +def napari_get_reader(path: Union[str, list[str]]) -> Optional[Callable]: if isinstance(path, str): if parse_omero_url(path): return omero_url_reader diff --git a/src/napari_omero/plugins/loaders.py b/src/napari_omero/plugins/loaders.py index e2c4296..a4c3cb1 100644 --- a/src/napari_omero/plugins/loaders.py +++ b/src/napari_omero/plugins/loaders.py @@ -1,15 +1,15 @@ -from typing import Dict, List, Optional +from typing import Optional import dask.array as da from dask.delayed import delayed from napari.types import LayerData +from vispy.color import Colormap + +from napari_omero.utils import PIXEL_TYPES, lookup_obj, parse_omero_url, timer +from napari_omero.widgets import QGateWay from omero.cli import ProxyStringType from omero.gateway import BlitzGateway, ImageWrapper from omero.model import IObject -from vispy.color import Colormap - -from ..utils import PIXEL_TYPES, lookup_obj, parse_omero_url, timer -from ..widgets import QGateWay # @timer @@ -27,7 +27,7 @@ def get_gateway(path: str, host: Optional[str] = None) -> BlitzGateway: if conn: return conn - from ..widgets.login import LoginForm + from napari_omero.widgets.login import LoginForm form = LoginForm(gateway) gateway.connected.connect(form.accept) @@ -35,7 +35,7 @@ def get_gateway(path: str, host: Optional[str] = None) -> BlitzGateway: return form.gateway.conn -def omero_url_reader(path: str) -> List[LayerData]: +def omero_url_reader(path: str) -> list[LayerData]: match = parse_omero_url(path) if not match: return [] @@ -52,7 +52,7 @@ def omero_url_reader(path: str) -> List[LayerData]: # @timer def omero_proxy_reader( path: str, proxy_obj: Optional[IObject] = None -) -> List[LayerData]: +) -> list[LayerData]: gateway = get_gateway(path) if proxy_obj.__class__.__name__.startswith("Image"): @@ -62,7 +62,7 @@ def omero_proxy_reader( return [] -def load_image_wrapper(image: ImageWrapper) -> List[LayerData]: +def load_image_wrapper(image: ImageWrapper) -> list[LayerData]: data = get_data_lazy(image) meta = get_omero_metadata(image) # contrast limits range ... not accessible from plugin interface @@ -71,7 +71,7 @@ def load_image_wrapper(image: ImageWrapper) -> List[LayerData]: return [(data, meta)] -def get_omero_metadata(image: ImageWrapper) -> Dict: +def get_omero_metadata(image: ImageWrapper) -> dict: """Get metadata from OMERO as a Dict to pass to napari.""" channels = image.getChannels() diff --git a/src/napari_omero/plugins/masks.py b/src/napari_omero/plugins/masks.py index 8684c31..3c6f909 100644 --- a/src/napari_omero/plugins/masks.py +++ b/src/napari_omero/plugins/masks.py @@ -1,9 +1,8 @@ -from typing import List - import numpy as np +from omero_rois import mask_from_binary_image + from omero.gateway import ImageWrapper from omero.model import RoiI -from omero_rois import mask_from_binary_image def create_roi(image: ImageWrapper, shapes) -> RoiI: @@ -17,7 +16,7 @@ def create_roi(image: ImageWrapper, shapes) -> RoiI: return updateService.saveAndReturnObject(roi) -def save_labels(layer, image: ImageWrapper) -> List[RoiI]: +def save_labels(layer, image: ImageWrapper) -> list[RoiI]: """ Saves masks from a 5D image (no C dimension). diff --git a/src/napari_omero/plugins/omero.py b/src/napari_omero/plugins/omero.py index a14ba9c..3cf1987 100644 --- a/src/napari_omero/plugins/omero.py +++ b/src/napari_omero/plugins/omero.py @@ -3,10 +3,13 @@ import napari import numpy -import omero.clients from napari.layers.labels.labels import Labels as labels_layer from napari.layers.points.points import Points as points_layer from napari.layers.shapes.shapes import Shapes as shapes_layer +from qtpy.QtWidgets import QPushButton + +import omero.clients +from napari_omero.utils import lookup_obj, obj_to_proxy_string from omero.cli import CLI, BaseControl, ProxyStringType from omero.gateway import BlitzGateway, PixelsWrapper from omero.model import ( @@ -20,9 +23,7 @@ RoiI, ) from omero.rtypes import rdouble, rint, rstring -from qtpy.QtWidgets import QPushButton -from ..utils import lookup_obj, obj_to_proxy_string from .masks import save_labels HELP = "Connect OMERO to the napari image viewer" @@ -31,10 +32,9 @@ def gateway_required(func): - """ - Decorator which initializes a client (self.client), - a BlitzGateway (self.gateway), and makes sure that - all services of the Blitzgateway are closed again. + """Decorator which initializes a client and BlitzGateway. + + makes sure that all services of the Blitzgateway are closed again. """ @wraps(func) @@ -132,9 +132,7 @@ def get_data(img, c=0): def set_dims_labels(viewer, image): - """ - Set labels on napari viewer dims, based on - dimensions of OMERO image. + """Set labels on napari viewer dims, based on dimensions of OMERO image. :param viewer: napari viewer instance :param image: omero.gateway.ImageWrapper @@ -147,7 +145,8 @@ def set_dims_labels(viewer, image): def set_dims_defaults(viewer, image): - """ + """Set default Z/T index on napari viewer. + Set Z/T slider index on napari viewer, according to default Z/T indecies of the OMERO image. @@ -162,7 +161,8 @@ def set_dims_defaults(viewer, image): def save_rois(viewer, image): - """ + """Save napari ROIs to OMERO. + Usage: In napari, open console... >>> from napari_omero import * >>> save_rois(viewer, omero_image). @@ -170,12 +170,12 @@ def save_rois(viewer, image): conn = image._conn for layer in viewer.layers: - if type(layer) == points_layer: + if type(layer) is points_layer: for p in layer.data: point = create_omero_point(p) roi = create_roi(conn, image.id, [point]) print(f"Created ROI: {roi.id.val}") - elif type(layer) == shapes_layer: + elif type(layer) is shapes_layer: if len(layer.data) == 0 or len(layer.shape_type) == 0: continue shape_types = layer.shape_type @@ -186,7 +186,7 @@ def save_rois(viewer, image): if shape is not None: roi = create_roi(conn, image.id, [shape]) print(f"Created ROI: {roi.id.val}") - elif type(layer) == labels_layer: + elif type(layer) is labels_layer: print("Saving Labels...") save_labels(layer, image) diff --git a/src/napari_omero/utils.py b/src/napari_omero/utils.py index 496e95f..f8fedcd 100644 --- a/src/napari_omero/utils.py +++ b/src/napari_omero/utils.py @@ -2,9 +2,10 @@ import logging import re import time -from typing import Dict, Optional +from typing import Optional import numpy as np + from omero.cli import ProxyStringType from omero.gateway import BlitzGateway, BlitzObjectWrapper from omero.model import IObject @@ -66,7 +67,7 @@ def lookup_obj(conn: BlitzGateway, iobj: IObject) -> BlitzObjectWrapper: ) -def parse_omero_url(url: str) -> Optional[Dict[str, str]]: +def parse_omero_url(url: str) -> Optional[dict[str, str]]: match = omero_url_pattern.search(url) return match.groupdict() if match else None diff --git a/src/napari_omero/widgets/__init__.py b/src/napari_omero/widgets/__init__.py index 58a8cca..1dcd27b 100644 --- a/src/napari_omero/widgets/__init__.py +++ b/src/napari_omero/widgets/__init__.py @@ -2,4 +2,4 @@ from .login import LoginForm from .main import OMEROWidget -__all__ = ["OMEROWidget", "QGateWay", "LoginForm"] +__all__ = ["LoginForm", "OMEROWidget", "QGateWay"] diff --git a/src/napari_omero/widgets/gateway.py b/src/napari_omero/widgets/gateway.py index 16a76d9..6d365c9 100644 --- a/src/napari_omero/widgets/gateway.py +++ b/src/napari_omero/widgets/gateway.py @@ -1,13 +1,15 @@ import atexit -from typing import TYPE_CHECKING, Callable, Generator, Optional, Tuple +from collections.abc import Generator +from typing import TYPE_CHECKING, Callable, Optional + +from qtpy.QtCore import QObject, Signal import omero.gateway from omero.clients import BaseClient from omero.gateway import BlitzGateway, BlitzObjectWrapper, PixelsWrapper from omero.util.sessions import SessionsStore -from qtpy.QtCore import QObject, Signal -SessionStats = Tuple[BaseClient, str, int, int] +SessionStats = tuple[BaseClient, str, int, int] if TYPE_CHECKING: @@ -31,8 +33,8 @@ def __init__(self, parent=None): self.store = SessionsStore() self.destroyed.connect(self.close) atexit.register(self.close) - self.worker: Optional["WorkerBase"] = None - self._next_worker: Optional["WorkerBase"] = None + self.worker: Optional[WorkerBase] = None + self._next_worker: Optional[WorkerBase] = None @property def conn(self): @@ -87,7 +89,7 @@ def connect(self): if self.conn.connect(): self.connected.emit(self.conn) - def get_current(self) -> Tuple[str, str, str, str]: + def get_current(self) -> tuple[str, str, str, str]: return self.store.get_current() def _start_next_worker(self): diff --git a/src/napari_omero/widgets/main.py b/src/napari_omero/widgets/main.py index 55177b8..e9df938 100644 --- a/src/napari_omero/widgets/main.py +++ b/src/napari_omero/widgets/main.py @@ -1,4 +1,3 @@ -from omero.gateway import BlitzObjectWrapper, ExperimenterGroupWrapper from qtpy.QtCore import ( QCoreApplication, QItemSelection, @@ -18,6 +17,8 @@ ) from superqt.utils import signals_blocked +from omero.gateway import BlitzObjectWrapper, ExperimenterGroupWrapper + from .gateway import QGateWay from .login import LoginForm from .thumb_grid import ThumbGrid diff --git a/src/napari_omero/widgets/thumb_grid.py b/src/napari_omero/widgets/thumb_grid.py index d9ace3a..3a86d4a 100644 --- a/src/napari_omero/widgets/thumb_grid.py +++ b/src/napari_omero/widgets/thumb_grid.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Optional from qtpy.QtCore import QSize, Qt from qtpy.QtGui import QIcon, QImage, QPixmap @@ -22,7 +22,7 @@ def __init__(self, gateway: QGateWay, parent=None): self.setSpacing(4) self._current_dataset: Optional[OMEROTreeItem] = None self._current_item: Optional[OMEROTreeItem] = None - self._item_map: Dict[str, QListWidgetItem] = {} + self._item_map: dict[str, QListWidgetItem] = {} def set_item(self, item: OMEROTreeItem): if item == self._current_item: diff --git a/src/napari_omero/widgets/tree_model.py b/src/napari_omero/widgets/tree_model.py index 2f0666e..0e48271 100644 --- a/src/napari_omero/widgets/tree_model.py +++ b/src/napari_omero/widgets/tree_model.py @@ -1,10 +1,11 @@ import itertools -from typing import Any, Dict, Optional +from typing import Any, Optional -from omero.gateway import BlitzObjectWrapper, _DatasetWrapper, _ImageWrapper from qtpy.QtCore import QModelIndex, Qt from qtpy.QtGui import QStandardItem, QStandardItemModel +from omero.gateway import BlitzObjectWrapper, _DatasetWrapper, _ImageWrapper + from .gateway import QGateWay _ICON_MAP = { @@ -77,7 +78,7 @@ class OMEROTreeModel(QStandardItemModel): def __init__(self, gateway: QGateWay, parent=None): super().__init__(parent) self.gateway = gateway - self._wrapper_map: Dict[BlitzObjectWrapper, QModelIndex] = {} + self._wrapper_map: dict[BlitzObjectWrapper, QModelIndex] = {} def submit_get_projects(self, *_, owner=None, group=None): root = self.invisibleRootItem() diff --git a/tests/test_widget.py b/tests/test_widget.py index f00b627..1ba7e2c 100644 --- a/tests/test_widget.py +++ b/tests/test_widget.py @@ -1,12 +1,9 @@ from unittest.mock import patch -from napari_omero import OMEROWidget - def test_widget(make_napari_viewer): from napari_omero.widgets.gateway import QGateWay viewer = make_napari_viewer() with patch.object(QGateWay, "_try_restore_session", lambda x: None): - widget = OMEROWidget() - viewer.window.add_dock_widget(widget) + viewer.window.add_plugin_dock_widget("napari-omero", "OMERO Browser") diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 35f9e0e..0000000 --- a/tox.ini +++ /dev/null @@ -1,41 +0,0 @@ -[tox] -envlist = py{37,38,39,310}-{linux,macos,windows}-{pyqt5,pyside2} -isolated_build = true -toxworkdir=/tmp/.tox - - -[gh-actions] -python = - 3.7: py37 - 3.8: py38 - 3.9: py39 - 3.10: py310 - 3.11: py311 - -[gh-actions:env] -PLATFORM = - ubuntu-latest: linux - macos-latest: macos - windows-latest: windows -BACKEND = - pyqt5: pyqt5 - pyside2: pyside2 - -[testenv] -passenv = - CI - GITHUB_ACTIONS - DISPLAY XAUTHORITY - PYVISTA_OFF_SCREEN -setenv = CONDA_DLL_SEARCH_MODIFICATION_ENABLE=1 -conda_deps = - omero-rois - omero-py -conda_channels = conda-forge -extras = - test -deps = - pyqt5: pyqt5 - pyside2: pyside2 -commands = - python -m pytest --disable-warnings --color=yes --basetemp={envtmpdir} --cov-report=xml --cov-report=term --cov=napari_omero {posargs}