From 849058e89cb5ba9e4a19e73ad252f51711b6001f Mon Sep 17 00:00:00 2001 From: Kira Evans Date: Tue, 24 Sep 2019 09:56:37 -0700 Subject: [PATCH] starfish.display: unpin napari version, add tests, view masks separately (#1570) * display: unpin napari version, add tests, view masks separately * Separate napari tests into its own category 1. Run them as part of the napari install test that's run on master branches. 2. Move the napari and pyqt imports inside test_display() so they don't trip up tests when napari isn't installed. 3. Upgrade pytest version. Test plan: named this branch -alltest, so it should hit all the test cases. --- .travis.yml | 14 ++++++-- Makefile | 12 +++++-- REQUIREMENTS-CI.txt | 2 +- setup.py | 2 +- starfish/core/_display.py | 21 ++++++------ starfish/core/test/test_display.py | 54 ++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 starfish/core/test/test_display.py diff --git a/.travis.yml b/.travis.yml index 197a37ccf..e508941c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,9 +47,19 @@ jobs: - make install-dev slow-test after_success: - bash <(curl -s https://codecov.io/bash) - - name: Install Napari + - name: Install Napari & Test napari if: type = push and (branch = master or branch =~ /-alltest/) - script: pip install .[napari] + dist: xenial + before_install: + - sudo apt-get install -y libgl1-mesa-glx libqt5x11extras5 xvfb + - make install-dev + - pip install .[napari] + - export DISPLAY=:99 + - Xvfb $DISPLAY -ac -screen 0 1024x768x24 & + - sleep 10 + script: + - python -c "import vispy; print(vispy.sys_info())" + - make napari-test - name: Docker if: type = push and (branch = master or branch =~ /-alltest/) script: make docker diff --git a/Makefile b/Makefile index 3c4c76589..24daca43a 100644 --- a/Makefile +++ b/Makefile @@ -39,14 +39,20 @@ lint-non-init: lint-init: flake8 --ignore 'E252, E301, E302, E305, E401, F401, W503, E731, F811' --filename='*__init__.py' $(MODULES) +# note that napari tests shouldn't be run in parallel because Qt seems to intermittently fail when multiple QApplications are spawned on threads. test: - pytest -v -n 8 --cov starfish + pytest -v -n 8 --cov starfish -m 'not napari' + pytest -v --cov starfish -m 'napari' fast-test: - pytest -v -n 8 --cov starfish -m 'not slow' + pytest -v -n 8 --cov starfish -m 'not (slow or napari)' slow-test: - pytest -v -n 8 --cov starfish -m 'slow' + pytest -v -n 8 --cov starfish -m 'slow and (not napari)' + +# note that this shouldn't be run in parallel because Qt seems to intermittently fail when multiple QApplications are spawned on threads. +napari-test: + pytest -v --cov starfish -m 'napari' mypy: mypy --ignore-missing-imports $(MODULES) diff --git a/REQUIREMENTS-CI.txt b/REQUIREMENTS-CI.txt index 9bf9560e3..be6944c5f 100644 --- a/REQUIREMENTS-CI.txt +++ b/REQUIREMENTS-CI.txt @@ -5,7 +5,7 @@ mypy numpydoc nbencdec >= 0.0.5 pycodestyle==2.5.0 -pytest>=3.6.3 +pytest>=4.4.0 pytest-cov>=2.5.1 pytest-xdist recommonmark diff --git a/setup.py b/setup.py index ce7483388..0a39777c4 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ packages=setuptools.find_packages(), install_requires=install_requires, extras_require={ - 'napari': [f"napari=={napari_version}"], + 'napari': [f"napari>={napari_version}"], }, entry_points={ 'console_scripts': [ diff --git a/starfish/core/_display.py b/starfish/core/_display.py index a04472b5f..4f7bd2150 100644 --- a/starfish/core/_display.py +++ b/starfish/core/_display.py @@ -4,6 +4,7 @@ from typing import Iterable, List, Optional, Set, Tuple, Union import numpy as np +from packaging.version import parse as parse_version from starfish.core.imagestack.imagestack import ImageStack from starfish.core.intensity_table.intensity_table import IntensityTable @@ -16,7 +17,7 @@ Viewer = None -NAPARI_VERSION = "0.1.3" # when changing this, update docs in display +NAPARI_VERSION = "0.1.5" # when changing this, update docs in display INTERACTIVE = not hasattr(__main__, "__file__") @@ -127,7 +128,8 @@ def display( z_multiplier: float = 1 ): """ - Displays an image stack and/or detected spots using napari (https://github.com/napari/napari). + Display an image stack, detected spots, and masks using + `napari `. Parameters ---------- @@ -193,20 +195,19 @@ def display( Notes ----- - - To use in ipython, use the `%gui qt5` magic. + - To use in ipython, use the `%gui qt` magic. - napari axes currently cannot be labeled. Until such a time that they can, this function will order them by Round, Channel, and Z. - - Requires napari 0.1.3: install starfish using `pip install starfish[napari]` to install all - necessary requirements + - Requires at least napari 0.1.5: use `pip install starfish[napari]` + to install all necessary requirements """ - if stack is None and spots is None: - # masks without stack or spots have no context so don't check for that - raise TypeError("expected a stack and/or spots; got nothing") + if stack is None and spots is None and masks is None: + raise TypeError("expected a stack, spots, or masks; got nothing") try: import napari except ImportError: - raise ImportError(f"Requires napari {NAPARI_VERSION}. " + raise ImportError(f"Requires at least napari {NAPARI_VERSION}. " "Run `pip install starfish[napari]` " "to install the necessary requirements.") @@ -215,7 +216,7 @@ def display( except Exception as e: raise RuntimeError("Could not identify napari version") from e - if version != NAPARI_VERSION: + if parse_version(version) < parse_version(NAPARI_VERSION): raise ValueError(f"Incorrect version {version} of napari installed." "Run `pip install starfish[napari]` " "to install the necessary requirements.") diff --git a/starfish/core/test/test_display.py b/starfish/core/test/test_display.py new file mode 100644 index 000000000..e1d977f94 --- /dev/null +++ b/starfish/core/test/test_display.py @@ -0,0 +1,54 @@ +import numpy as np +import pytest + +from starfish import display, SegmentationMaskCollection +from starfish.core.test.factories import SyntheticData +from starfish.types import Coordinates + + +sd = SyntheticData( + n_ch=2, + n_round=3, + n_spots=1, + n_codes=4, + n_photons_background=0, + background_electrons=0, + camera_detection_efficiency=1.0, + gray_level=1, + ad_conversion_bits=16, + point_spread_function=(2, 2, 2), +) + +stack = sd.spots() +spots = sd.intensities() +masks = SegmentationMaskCollection.from_label_image( + np.random.rand(128, 128).astype(np.uint8), + {Coordinates.Y: np.arange(128), Coordinates.X: np.arange(128)} +) + + +@pytest.mark.napari +@pytest.mark.parametrize('masks', [masks, None], ids=['masks', ' ']) +@pytest.mark.parametrize('spots', [spots, None], ids=['spots', ' ']) +@pytest.mark.parametrize('stack', [stack, None], ids=['stack', ' ']) +def test_display(stack, spots, masks): + import napari + from qtpy.QtCore import QTimer + from qtpy.QtWidgets import QApplication + + def run(): + app = QApplication.instance() or QApplication([]) + viewer = napari.Viewer() + timer = QTimer() + timer.setInterval(500) + timer.timeout.connect(viewer.window.close) + timer.timeout.connect(app.quit) + timer.start() + display(stack, spots, masks, viewer=viewer) + app.exec_() + + if stack is None and spots is None and masks is None: + with pytest.raises(TypeError): + run() + else: + run()