diff --git a/.github/workflows/test_prereleases.yml b/.github/workflows/test_prereleases.yml index 7875aecba01..3f1b466b09a 100644 --- a/.github/workflows/test_prereleases.yml +++ b/.github/workflows/test_prereleases.yml @@ -11,6 +11,7 @@ on: pull_request: paths: - '.github/workflows/test_prereleases.yml' + - 'resources/constraints/version_denylist.txt' env: COLUMNS: 120 @@ -42,6 +43,10 @@ jobs: - uses: tlambert03/setup-qt-libs@v1 + - name: Setup Graphviz + uses: ts-graphviz/setup-graphviz@v2 + continue-on-error: true + - name: Install Windows OpenGL if: runner.os == 'Windows' run: | @@ -64,6 +69,7 @@ jobs: BACKEND: ${{ matrix.backend }} COVERAGE: "no_cov" PYVISTA_OFF_SCREEN: True # required for opengl on windows + PIP_CONSTRAINT: resources/constraints/version_denylist.txt # If something goes wrong, we can open an issue in the repo - name: Report Failures diff --git a/.github/workflows/upgrade_test_constraints.yml b/.github/workflows/upgrade_test_constraints.yml index e0e21e2ee48..cbd4a403ba4 100644 --- a/.github/workflows/upgrade_test_constraints.yml +++ b/.github/workflows/upgrade_test_constraints.yml @@ -165,7 +165,7 @@ jobs: # # --extra pyqt5 etc - names of extra sections from pyproject.toml that should be checked for the dependencies list (maybe we could create a super extra section to collect them all in) flags+=" --extra pyqt5" - flags+=" --extra pyqt6_experimental" + flags+=" --extra pyqt6" flags+=" --extra pyside2" flags+=" --extra pyside6_experimental" flags+=" --extra testing" diff --git a/CITATION.cff b/CITATION.cff index a58a177a340..0483d680998 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -103,6 +103,11 @@ authors: affiliation: Chan Zuckerberg Initiative orcid: https://orcid.org/0000-0002-3841-8344 alias: aganders3 +- given-names: Andrew + family-names: Annex + affiliation: SETI Institute/NASA ARC + orcid: https://orcid.org/0000-0002-0253-2313 + alias: AndrewAnnex - given-names: Peter family-names: Boone alias: boonepeter @@ -121,6 +126,16 @@ authors: CNRS, Palaiseau, France orcid: https://orcid.org/0000-0002-9441-9173 alias: ClementCaporal +- given-names: Jan + family-names: Eglinger + affiliation: Friedrich Miescher Institute for Biomedical Research (FMI), Basel (Switzerland) + orcid: https://orcid.org/0000-0001-7234-1435 + alias: imagejan +- given-names: Andreas + family-names: Eisenbarth + affiliation: EMBL Heidelberg, Germany + orcid: https://orcid.org/0000-0002-1113-9556 + alias: aeisenbarth - given-names: Jeremy family-names: Freeman affiliation: Chan Zuckerberg Initiative @@ -209,11 +224,21 @@ authors: family-names: Nauroth-Kreß affiliation: University Hospital Würzburg - Institute of Neuroradiology alias: Chris-N-K +- given-names: David + family-names: Palecek + affiliation: Algarve Centre of Marine Sciences (CCMAR) + orcid: https://orcid.org/0009-0003-9328-8540 + alias: palec87 - given-names: Constantin family-names: Pape affiliation: Georg-August-Universität Göttingen orcid: https://orcid.org/0000-0001-6562-7187 alias: constantinpape +- given-names: Eric + family-names: Perlman + affiliation: Yikes LLC + orcid: https://orcid.org/0000-0001-5542-1302 + alias: perlman - given-names: Kim family-names: Pevey alias: kcpevey @@ -227,6 +252,9 @@ authors: affiliation: Brown University orcid: https://orcid.org/0000-0003-4501-5428 alias: kir0ul +- given-names: David + family-names: Pinto + alias: MarchisLost - given-names: Jaime family-names: Rodríguez-Guerra affiliation: Quansight Labs @@ -242,11 +270,19 @@ authors: affiliation: European Bioinformatics Institute - European Molecular Biology Laboratory orcid: https://orcid.org/0000-0002-2447-5911 alias: ctr26 +- given-names: James + family-names: Ryan + alias: jamesyan-git - given-names: Gabriel family-names: Selzer affiliation: University of Wisconsin-Madison orcid: https://orcid.org/0009-0002-2400-1940 alias: gselzer +- given-names: MB + family-names: Smith + affiliation: AI lab for Living Technologies, University Medical Centre Utrecht (The Netherlands) + orcid: https://orcid.org/0000-0002-1405-0100 + alias: odinsbane - given-names: Paul family-names: Smith affiliation: University College London @@ -255,6 +291,11 @@ authors: - given-names: Konstantin family-names: Sofiiuk alias: ksofiyuk +- given-names: Johannes + family-names: Soltwedel + affiliation: DFG cluster of excellence 'Physics of Life', TU Dresden + orcid: https://orcid.org/0000-0003-1273-2412 + alias: jo-mueller - given-names: David family-names: Stansby affiliation: University College London @@ -269,6 +310,11 @@ authors: family-names: Wadhwa affiliation: Quansight Labs alias: ppwadhwa +- given-names: Martin + family-names: Weigert + affiliation: TU-Dresden / EPFL + orcid: https://orcid.org/0000-0002-7780-9057 + alias: maweigert - given-names: Jonas family-names: Windhager affiliation: ETH Zurich / University of Zurich diff --git a/napari/_qt/_qapp_model/_tests/test_view_menu.py b/napari/_qt/_qapp_model/_tests/test_view_menu.py index 4faffefafef..df553e47a9d 100644 --- a/napari/_qt/_qapp_model/_tests/test_view_menu.py +++ b/napari/_qt/_qapp_model/_tests/test_view_menu.py @@ -49,22 +49,28 @@ def test_toggle_axes_scale_bar_attr( @skip_local_popups -def test_toggle_fullscreen(make_napari_viewer): +def test_toggle_fullscreen(make_napari_viewer, qtbot): """Test toggle fullscreen action.""" action_id = 'napari.window.view.toggle_fullscreen' app = get_app() viewer = make_napari_viewer(show=True) # Check initial default state (no fullscreen) - assert not viewer.window._qt_window._fullscreen_flag + assert not viewer.window._qt_window.isFullScreen() # Check fullscreen state change app.commands.execute_command(action_id) - assert viewer.window._qt_window._fullscreen_flag + if sys.platform == 'darwin': + # On macOS, wait for the animation to complete + qtbot.wait(250) + assert viewer.window._qt_window.isFullScreen() # Check return to non fullscreen state app.commands.execute_command(action_id) - assert not viewer.window._qt_window._fullscreen_flag + if sys.platform == 'darwin': + # On macOS, wait for the animation to complete + qtbot.wait(250) + assert not viewer.window._qt_window.isFullScreen() @skip_local_focus diff --git a/napari/_qt/_tests/test_qt_viewer.py b/napari/_qt/_tests/test_qt_viewer.py index d3486d91afe..adff59e05c3 100644 --- a/napari/_qt/_tests/test_qt_viewer.py +++ b/napari/_qt/_tests/test_qt_viewer.py @@ -1116,3 +1116,100 @@ def test_points_2d_to_3d(make_napari_viewer): QApplication.processEvents() viewer.dims.ndisplay = 3 QApplication.processEvents() + + +@skip_local_popups +def test_scale_bar_colored(qt_viewer, qtbot): + viewer = qt_viewer.viewer + scale_bar = viewer.scale_bar + + # Add black image + data = np.zeros((2, 2)) + viewer.add_image(data) + + # Check scale bar is not visible (all the canvas is black - `[0, 0, 0, 255]`) + def check_all_black(): + screenshot = qt_viewer.screenshot(flash=False) + assert np.all(screenshot == [0, 0, 0, 255], axis=-1).all() + + qtbot.waitUntil(check_all_black) + + # Check scale bar is visible (canvas has white `[1, 1, 1, 255]` in it) + def check_white_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert not np.all(screenshot == [0, 0, 0, 255], axis=-1).all() + assert np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + + scale_bar.visible = True + qtbot.waitUntil(check_white_scale_bar) + + # Check scale bar is colored (canvas has fuchsia `[1, 0, 1, 255]` and not white in it) + def check_colored_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert not np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + assert np.all(screenshot == [1, 0, 1, 255], axis=-1).any() + + scale_bar.colored = True + qtbot.waitUntil(check_colored_scale_bar) + + # Check scale bar is still visible but not colored (canvas has white again but not fuchsia in it) + def check_only_white_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + assert not np.all(screenshot == [1, 0, 1, 255], axis=-1).any() + + scale_bar.colored = False + qtbot.waitUntil(check_only_white_scale_bar) + + +@skip_local_popups +def test_scale_bar_ticks(qt_viewer, qtbot): + viewer = qt_viewer.viewer + scale_bar = viewer.scale_bar + + # Add black image + data = np.zeros((2, 2)) + viewer.add_image(data) + + # Check scale bar is not visible (all the canvas is black - `[0, 0, 0, 255]`) + def check_all_black(): + screenshot = qt_viewer.screenshot(flash=False) + assert np.all(screenshot == [0, 0, 0, 255], axis=-1).all() + + qtbot.waitUntil(check_all_black) + + # Check scale bar is visible (canvas has white `[1, 1, 1, 255]` in it) + def check_white_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert not np.all(screenshot == [0, 0, 0, 255], axis=-1).all() + assert np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + + scale_bar.visible = True + qtbot.waitUntil(check_white_scale_bar) + + # Check scale bar has ticks active and take screenshot for later comparison + assert scale_bar.ticks + screenshot_with_ticks = qt_viewer.screenshot(flash=False) + + # Check scale bar without ticks (still white present but new screenshot differs from ticks one) + def check_no_ticks_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + npt.assert_raises( + AssertionError, + npt.assert_array_equal, + screenshot, + screenshot_with_ticks, + ) + + scale_bar.ticks = False + qtbot.waitUntil(check_no_ticks_scale_bar) + + # Check scale bar again has ticks (still white present and new screenshot corresponds with ticks one) + def check_ticks_scale_bar(): + screenshot = qt_viewer.screenshot(flash=False) + assert np.all(screenshot == [1, 1, 1, 255], axis=-1).any() + npt.assert_array_equal(screenshot, screenshot_with_ticks) + + scale_bar.ticks = True + qtbot.waitUntil(check_ticks_scale_bar) diff --git a/napari/_qt/containers/qt_layer_list.py b/napari/_qt/containers/qt_layer_list.py index f94acf6875a..af22a7fd246 100644 --- a/napari/_qt/containers/qt_layer_list.py +++ b/napari/_qt/containers/qt_layer_list.py @@ -73,6 +73,7 @@ def keyPressEvent(self, e: Optional[QKeyEvent]) -> None: e.modifiers() & Qt.KeyboardModifier.AltModifier or e.modifiers() & Qt.KeyboardModifier.ControlModifier or e.modifiers() & Qt.KeyboardModifier.MetaModifier + or e.modifiers() & Qt.KeyboardModifier.ShiftModifier ): e.ignore() elif e.key() != Qt.Key.Key_Space: diff --git a/napari/_qt/dialogs/_tests/test_reader_dialog.py b/napari/_qt/dialogs/_tests/test_reader_dialog.py index 63cdfcbeeec..3c724f7fcdd 100644 --- a/napari/_qt/dialogs/_tests/test_reader_dialog.py +++ b/napari/_qt/dialogs/_tests/test_reader_dialog.py @@ -42,7 +42,7 @@ def test_reader_defaults(reader_dialog, tmpdir): assert widg.findChild(QLabel).text().startswith('Choose reader') assert widg._get_plugin_choice() == 'p1' - assert widg.persist_checkbox.isChecked() + assert not widg.persist_checkbox.isChecked() def test_reader_with_error_message(reader_dialog): @@ -84,10 +84,10 @@ def test_get_plugin_choice(tmpdir, reader_dialog): def test_get_persist_choice(tmpdir, reader_dialog): file_pth = tmpdir.join('my_file.tif') widg = reader_dialog(pth=file_pth, readers={'p1': 'p1', 'p2': 'p2'}) - assert widg._get_persist_choice() + assert not widg._get_persist_choice() widg.persist_checkbox.toggle() - assert not widg._get_persist_choice() + assert widg._get_persist_choice() def test_prepare_dialog_options_no_readers(): diff --git a/napari/_qt/dialogs/qt_reader_dialog.py b/napari/_qt/dialogs/qt_reader_dialog.py index 61352c64abe..faabfa674d6 100644 --- a/napari/_qt/dialogs/qt_reader_dialog.py +++ b/napari/_qt/dialogs/qt_reader_dialog.py @@ -27,7 +27,7 @@ def __init__( parent: QWidget = None, readers: Optional[dict[str, str]] = None, error_message: str = '', - persist_checked: bool = True, + persist_checked: bool = False, ) -> None: if readers is None: readers = {} @@ -194,7 +194,7 @@ def handle_gui_reading( pth=_path, error_message=error_message, readers=readers, - persist_checked=not plugin_override, + persist_checked=plugin_override, ) display_name, persist = readerDialog.get_user_choices() if display_name: diff --git a/napari/_qt/qt_resources/styles/01_buttons.qss b/napari/_qt/qt_resources/styles/01_buttons.qss index db51fdbd3ab..e605dea21e3 100644 --- a/napari/_qt/qt_resources/styles/01_buttons.qss +++ b/napari/_qt/qt_resources/styles/01_buttons.qss @@ -126,7 +126,7 @@ QtModeRadioButton[mode="polygon"]::indicator { } QtModeRadioButton[mode="labels_polygon"]::indicator { - image: url("theme_{{ id }}:/polygon_lasso.svg"); + image: url("theme_{{ id }}:/polygon.svg"); } QtModeRadioButton[mode="polygon_lasso"]::indicator { diff --git a/napari/_vispy/_tests/test_vispy_image_layer.py b/napari/_vispy/_tests/test_vispy_image_layer.py index 61434ea06af..7045b4966c3 100644 --- a/napari/_vispy/_tests/test_vispy_image_layer.py +++ b/napari/_vispy/_tests/test_vispy_image_layer.py @@ -1,6 +1,7 @@ from itertools import permutations import numpy as np +import numpy.testing as npt import pytest from napari._vispy._tests.utils import vispy_image_scene_size @@ -83,3 +84,118 @@ def test_no_float32_texture_support(monkeypatch): ) image = Image(np.zeros((16, 8, 4, 2), dtype='uint8'), scale=(1, 2, 4, 8)) VispyImageLayer(image) + + +@pytest.fixture() +def im_layer() -> Image: + return Image(np.zeros((10, 10))) + + +@pytest.fixture() +def pyramid_layer() -> Image: + return Image([np.zeros((20, 20)), np.zeros((10, 10))]) + + +def test_base_create(im_layer): + VispyImageLayer(im_layer) + + +def set_translate(layer): + layer.translate = (10, 10) + + +def set_affine_translate(layer): + layer.affine.translate = (10, 10) + layer.events.affine() + + +def set_rotate(layer): + layer.rotate = 90 + + +def set_affine_rotate(layer): + layer.affine.rotate = 90 + layer.events.affine() + + +def no_op(layer): + pass + + +@pytest.mark.parametrize( + ('translate', 'exp_translate'), + [ + (set_translate, (10, 10)), + (set_affine_translate, (10, 10)), + (no_op, (0, 0)), + ], + ids=('translate', 'affine_translate', 'no_op'), +) +@pytest.mark.parametrize( + ('rotate', 'exp_rotate'), + [ + (set_rotate, ((0, -1), (1, 0))), + (set_affine_rotate, ((0, -1), (1, 0))), + (no_op, ((1, 0), (0, 1))), + ], + ids=('rotate', 'affine_rotate', 'no_op'), +) +def test_transforming_child_node( + im_layer, translate, exp_translate, rotate, exp_rotate +): + layer = VispyImageLayer(im_layer) + npt.assert_array_almost_equal( + layer.node.transform.matrix[-1][:2], (-0.5, -0.5) + ) + npt.assert_array_almost_equal( + layer.node.transform.matrix[:2, :2], ((1, 0), (0, 1)) + ) + rotate(im_layer) + translate(im_layer) + npt.assert_array_almost_equal( + layer.node.children[0].transform.matrix[:2, :2], ((1, 0), (0, 1)) + ) + npt.assert_array_almost_equal( + layer.node.children[0].transform.matrix[-1][:2], (0.5, 0.5) + ) + npt.assert_array_almost_equal( + layer.node.transform.matrix[:2, :2], exp_rotate + ) + if translate == set_translate and rotate == set_affine_rotate: + npt.assert_array_almost_equal( + layer.node.transform.matrix[-1][:2], + np.dot( + np.linalg.inv(exp_rotate), + np.array([-0.5, -0.5]) + exp_translate, + ), + ) + else: + npt.assert_array_almost_equal( + layer.node.transform.matrix[-1][:2], + np.dot(np.linalg.inv(exp_rotate), (-0.5, -0.5)) + exp_translate, + # np.dot(np.linalg.inv(im_layer.affine.rotate), exp_translate) + ) + + +def test_transforming_child_node_pyramid(pyramid_layer): + layer = VispyImageLayer(pyramid_layer) + corner_pixels_world = np.array([[0, 0], [20, 20]]) + npt.assert_array_almost_equal( + layer.node.transform.matrix[-1][:2], (-0.5, -0.5) + ) + npt.assert_array_almost_equal( + layer.node.children[0].transform.matrix[-1][:2], (0.5, 0.5) + ) + pyramid_layer.translate = (-10, -10) + pyramid_layer._update_draw( + scale_factor=1, + corner_pixels_displayed=corner_pixels_world, + shape_threshold=(10, 10), + ) + + npt.assert_array_almost_equal( + layer.node.transform.matrix[-1][:2], (-0.5, -0.5) + ) + npt.assert_array_almost_equal( + layer.node.children[0].transform.matrix[-1][:2], (-9.5, -9.5) + ) diff --git a/napari/_vispy/layers/base.py b/napari/_vispy/layers/base.py index 115ced14f7d..780e10024b8 100644 --- a/napari/_vispy/layers/base.py +++ b/napari/_vispy/layers/base.py @@ -214,7 +214,8 @@ def _on_matrix_change(self): affine_matrix[: matrix.shape[0], : matrix.shape[1]] = matrix affine_matrix[-1, : len(translate)] = translate - offset = np.zeros(len(self.layer._slice_input.displayed)) + child_offset = np.zeros(len(self.layer._slice_input.displayed)) + dims_displayed = self.layer._slice_input.displayed if self._array_like and self.layer._slice_input.ndisplay == 2: # Perform pixel offset to shift origin from top left corner @@ -230,33 +231,24 @@ def _on_matrix_change(self): affine_offset = np.eye(4) affine_offset[-1, : len(offset)] = offset[::-1] affine_matrix = affine_matrix @ affine_offset + if self.layer.multiscale: + # For performance reasons, when displaying multiscale images, + # only the part of the data that is visible on the canvas is + # sent as a texture to the GPU. This means that the texture + # gets an additional transform, to position the texture + # correctly offset from the origin of the full data. However, + # child nodes, which include overlays such as bounding boxes, + # should *not* receive this offset, so we undo it here: + child_offset = ( + np.ones(offset_matrix.shape[1]) / 2 + - self.layer.corner_pixels[0][dims_displayed][::-1] + ) + else: + child_offset = np.ones(offset_matrix.shape[1]) / 2 self._master_transform.matrix = affine_matrix - # Because of performance reason, for multiscale images - # we load only visible part of data to GPU. - # To place this part of data correctly we update transform, - # but this leads to incorrect placement of child layers. - # To fix this we need to update child layers transform. - dims_displayed = self.layer._slice_input.displayed - simplified_transform = self.layer._transforms.simplified - if simplified_transform is None: - raise ValueError( - 'simplified transform is None' - ) # pragma: no cover - translate_child = ( - self.layer.translate[dims_displayed] - + self.layer.affine.translate[dims_displayed] - )[::-1] - offset[::-1] - trans_rotate = simplified_transform.rotate[ - np.ix_(dims_displayed, dims_displayed) - ] - trans_scale = simplified_transform.scale[dims_displayed][::-1] - new_translate = ( - trans_rotate @ (translate_child - translate) / trans_scale - ) - child_matrix = np.eye(4) - child_matrix[-1, : len(translate)] = new_translate + child_matrix[-1, : len(child_offset)] = child_offset for child in self.node.children: child.transform.matrix = child_matrix diff --git a/napari/components/_viewer_key_bindings.py b/napari/components/_viewer_key_bindings.py index d378e3128ad..09a9b4049a3 100644 --- a/napari/components/_viewer_key_bindings.py +++ b/napari/components/_viewer_key_bindings.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING +from app_model.types import KeyCode, KeyMod + from napari.components.viewer_model import ViewerModel from napari.utils.action_manager import action_manager from napari.utils.theme import available_themes, get_system_theme @@ -32,6 +34,16 @@ def _inner(func): return _inner +@ViewerModel.bind_key(KeyMod.Shift | KeyCode.UpArrow, overwrite=True) +def extend_selection_to_layer_above(viewer: Viewer): + viewer.layers.select_next(shift=True) + + +@ViewerModel.bind_key(KeyMod.Shift | KeyCode.DownArrow, overwrite=True) +def extend_selection_to_layer_below(viewer: Viewer): + viewer.layers.select_previous(shift=True) + + @register_viewer_action(trans._('Reset scroll.')) def reset_scroll_progress(viewer: Viewer): # on key press diff --git a/napari/layers/labels/_tests/test_labels.py b/napari/layers/labels/_tests/test_labels.py index b30e92bcf1b..67a87017056 100644 --- a/napari/layers/labels/_tests/test_labels.py +++ b/napari/layers/labels/_tests/test_labels.py @@ -419,6 +419,26 @@ def test_custom_color_dict(): assert not (layer.get_color(1) == np.array([1.0, 1.0, 1.0, 1.0])).all() +@pytest.mark.parametrize( + 'colormap_like', + [ + ['red', 'blue'], + [[1, 0, 0, 1], [0, 0, 1, 1]], + {None: 'transparent', 1: 'red', 2: 'blue'}, + {None: [0, 0, 0, 0], 1: [1, 0, 0, 1], 2: [0, 0, 1, 1]}, + defaultdict(lambda: 'transparent', {1: 'red', 2: 'blue'}), + ], +) +def test_colormap_simple_data_types(colormap_like): + """Test that setting colormap with list or dict of colors works.""" + data = np.random.randint(20, size=(10, 15)) + # test in constructor + _ = Labels(data, colormap=colormap_like) + # test assignment + layer = Labels(data) + layer.colormap = colormap_like + + def test_metadata(): """Test setting labels metadata.""" np.random.seed(0) diff --git a/napari/layers/labels/labels.py b/napari/layers/labels/labels.py index 9254ec89d68..96c350b2064 100644 --- a/napari/layers/labels/labels.py +++ b/napari/layers/labels/labels.py @@ -53,6 +53,7 @@ from napari.utils.colormaps.colormap import ( CyclicLabelColormap, LabelColormapBase, + _normalize_label_colormap, ) from napari.utils.colormaps.colormap_utils import shuffle_and_extend_colormap from napari.utils.events import EmitterGroup, Event @@ -500,6 +501,7 @@ def colormap(self, colormap: LabelColormapBase): self._set_colormap(colormap) def _set_colormap(self, colormap): + colormap = _normalize_label_colormap(colormap) if isinstance(colormap, CyclicLabelColormap): self._random_colormap = colormap self._original_random_colormap = colormap diff --git a/napari/resources/icons/polygon.svg b/napari/resources/icons/polygon.svg index e1cd3aa3065..93b343818dd 100644 --- a/napari/resources/icons/polygon.svg +++ b/napari/resources/icons/polygon.svg @@ -1,14 +1,24 @@ - - - - - - - - - + + + + Layer 1 + + + + + + + + + + + + + + + + + + + diff --git a/napari/utils/colormaps/_accelerated_cmap.py b/napari/utils/colormaps/_accelerated_cmap.py new file mode 100644 index 00000000000..d6a7c35908d --- /dev/null +++ b/napari/utils/colormaps/_accelerated_cmap.py @@ -0,0 +1,233 @@ +""" +Colormap utility functions to be sped-up by numba JIT. + +These should stay in a separate module because they need to be reloaded during +testing, which can break instance/class relationships when done dynamically. +See https://github.com/napari/napari/pull/7025#issuecomment-2186190719. +""" + +from typing import TYPE_CHECKING + +import numpy as np + +if TYPE_CHECKING: + from numba import typed + + from napari.utils.colormaps import DirectLabelColormap + + +__all__ = ( + 'minimum_dtype_for_labels', + 'zero_preserving_modulo', + 'labels_raw_to_texture_direct', + 'zero_preserving_modulo_numpy', +) + +MAPPING_OF_UNKNOWN_VALUE = 0 +# For direct mode we map all unknown values to single value +# for simplicity of implementation we select 0 + + +def minimum_dtype_for_labels(num_colors: int) -> np.dtype: + """Return the minimum texture dtype that can hold given number of colors. + + Parameters + ---------- + num_colors : int + Number of unique colors in the data. + + Returns + ------- + np.dtype + Minimum dtype that can hold the number of colors. + """ + if num_colors <= np.iinfo(np.uint8).max: + return np.dtype(np.uint8) + if num_colors <= np.iinfo(np.uint16).max: + return np.dtype(np.uint16) + return np.dtype(np.float32) + + +def zero_preserving_modulo_numpy( + values: np.ndarray, n: int, dtype: np.dtype, to_zero: int = 0 +) -> np.ndarray: + """``(values - 1) % n + 1``, but with one specific value mapped to 0. + + This ensures (1) an output value in [0, n] (inclusive), and (2) that + no nonzero values in the input are zero in the output, other than the + ``to_zero`` value. + + Parameters + ---------- + values : np.ndarray + The dividend of the modulo operator. + n : int + The divisor. + dtype : np.dtype + The desired dtype for the output array. + to_zero : int, optional + A specific value to map to 0. (By default, 0 itself.) + + Returns + ------- + np.ndarray + The result: 0 for the ``to_zero`` value, ``values % n + 1`` + everywhere else. + """ + res = ((values - 1) % n + 1).astype(dtype) + res[values == to_zero] = 0 + return res + + +def _zero_preserving_modulo_loop( + values: np.ndarray, n: int, dtype: np.dtype, to_zero: int = 0 +) -> np.ndarray: + """``(values - 1) % n + 1``, but with one specific value mapped to 0. + + This ensures (1) an output value in [0, n] (inclusive), and (2) that + no nonzero values in the input are zero in the output, other than the + ``to_zero`` value. + + Parameters + ---------- + values : np.ndarray + The dividend of the modulo operator. + n : int + The divisor. + dtype : np.dtype + The desired dtype for the output array. + to_zero : int, optional + A specific value to map to 0. (By default, 0 itself.) + + Returns + ------- + np.ndarray + The result: 0 for the ``to_zero`` value, ``values % n + 1`` + everywhere else. + """ + result = np.empty_like(values, dtype=dtype) + # need to preallocate numpy array for asv memory benchmarks + return _zero_preserving_modulo_inner_loop(values, n, to_zero, out=result) + + +def _zero_preserving_modulo_inner_loop( + values: np.ndarray, n: int, to_zero: int, out: np.ndarray +) -> np.ndarray: + """``(values - 1) % n + 1``, but with one specific value mapped to 0. + + This ensures (1) an output value in [0, n] (inclusive), and (2) that + no nonzero values in the input are zero in the output, other than the + ``to_zero`` value. + + Parameters + ---------- + values : np.ndarray + The dividend of the modulo operator. + n : int + The divisor. + to_zero : int + A specific value to map to 0. (Usually, 0 itself.) + out : np.ndarray + Preallocated output array + + Returns + ------- + np.ndarray + The result: 0 for the ``to_zero`` value, ``values % n + 1`` + everywhere else. + """ + for i in prange(values.size): + if values.flat[i] == to_zero: + out.flat[i] = 0 + else: + out.flat[i] = (values.flat[i] - 1) % n + 1 + + return out + + +def _labels_raw_to_texture_direct_numpy( + data: np.ndarray, direct_colormap: 'DirectLabelColormap' +) -> np.ndarray: + """Convert labels data to the data type used in the texture. + + This implementation uses numpy vectorized operations. + + See `_cast_labels_data_to_texture_dtype_direct` for more details. + """ + if direct_colormap.use_selection: + return (data == direct_colormap.selection).astype(np.uint8) + mapper = direct_colormap._array_map + if any(x < 0 for x in direct_colormap.color_dict if x is not None): + half_shape = mapper.shape[0] // 2 - 1 + data = np.clip(data, -half_shape, half_shape) + else: + data = np.clip(data, 0, mapper.shape[0] - 1) + + return mapper[data] + + +def _labels_raw_to_texture_direct_loop( + data: np.ndarray, direct_colormap: 'DirectLabelColormap' +) -> np.ndarray: + """ + Cast direct labels to the minimum type. + + Parameters + ---------- + data : np.ndarray + The input data array. + direct_colormap : DirectLabelColormap + The direct colormap. + + Returns + ------- + np.ndarray + The cast data array. + """ + if direct_colormap.use_selection: + return (data == direct_colormap.selection).astype(np.uint8) + + dkt = direct_colormap._get_typed_dict_mapping(data.dtype) + target_dtype = minimum_dtype_for_labels( + direct_colormap._num_unique_colors + 2 + ) + result_array = np.full_like( + data, MAPPING_OF_UNKNOWN_VALUE, dtype=target_dtype + ) + return _labels_raw_to_texture_direct_inner_loop(data, dkt, result_array) + + +def _labels_raw_to_texture_direct_inner_loop( + data: np.ndarray, dkt: 'typed.Dict', out: np.ndarray +) -> np.ndarray: + """ + Relabel data using typed dict with mapping unknown labels to default value + """ + # The numba typed dict does not provide official Api for + # determine key and value types + for i in prange(data.size): + val = data.flat[i] + if val in dkt: + out.flat[i] = dkt[data.flat[i]] + + return out + + +try: + import numba +except ModuleNotFoundError: + zero_preserving_modulo = zero_preserving_modulo_numpy + labels_raw_to_texture_direct = _labels_raw_to_texture_direct_numpy + prange = range +else: + _zero_preserving_modulo_inner_loop = numba.njit(parallel=True, cache=True)( + _zero_preserving_modulo_inner_loop + ) + zero_preserving_modulo = _zero_preserving_modulo_loop + labels_raw_to_texture_direct = _labels_raw_to_texture_direct_loop + _labels_raw_to_texture_direct_inner_loop = numba.njit( + parallel=True, cache=True + )(_labels_raw_to_texture_direct_inner_loop) + prange = numba.prange # type: ignore [misc] + + del numba diff --git a/napari/utils/colormaps/_tests/test_colormap.py b/napari/utils/colormaps/_tests/test_colormap.py index 16ca87ac80a..6ed4f8b32a3 100644 --- a/napari/utils/colormaps/_tests/test_colormap.py +++ b/napari/utils/colormaps/_tests/test_colormap.py @@ -1,4 +1,5 @@ import importlib +from collections import defaultdict from itertools import product from unittest.mock import patch @@ -8,12 +9,17 @@ from napari._pydantic_compat import ValidationError from napari.utils.color import ColorArray -from napari.utils.colormaps import Colormap, colormap -from napari.utils.colormaps.colormap import ( +from napari.utils.colormaps import Colormap, _accelerated_cmap, colormap +from napari.utils.colormaps._accelerated_cmap import ( MAPPING_OF_UNKNOWN_VALUE, - DirectLabelColormap, _labels_raw_to_texture_direct_numpy, ) +from napari.utils.colormaps.colormap import ( + CyclicLabelColormap, + DirectLabelColormap, + LabelColormapBase, + _normalize_label_colormap, +) from napari.utils.colormaps.colormap_utils import label_colormap @@ -137,16 +143,23 @@ def test_mapped_shape(ndim): ('num', 'dtype'), [(40, np.uint8), (1000, np.uint16), (80000, np.float32)] ) def test_minimum_dtype_for_labels(num, dtype): - assert colormap.minimum_dtype_for_labels(num) == dtype + assert _accelerated_cmap.minimum_dtype_for_labels(num) == dtype @pytest.fixture() def _disable_jit(monkeypatch): + """Fixture to temporarily disable numba JIT during testing. + + This helps to measure coverage and in debugging. *However*, reloading a + module can cause issues with object instance / class relationships, so + the `_accelerated_cmap` module should be as small as possible and contain + no class definitions, only functions. + """ pytest.importorskip('numba') with patch('numba.core.config.DISABLE_JIT', True): - importlib.reload(colormap) + importlib.reload(_accelerated_cmap) yield - importlib.reload(colormap) # revert to original state + importlib.reload(_accelerated_cmap) # revert to original state @pytest.mark.parametrize(('num', 'dtype'), [(40, np.uint8), (1000, np.uint16)]) @@ -222,7 +235,9 @@ def test_direct_label_colormap_selection(direct_label_colormap): @pytest.mark.usefixtures('_disable_jit') def test_cast_direct_labels_to_minimum_type(direct_label_colormap): data = np.arange(15, dtype=np.uint32) - cast = colormap._labels_raw_to_texture_direct(data, direct_label_colormap) + cast = _accelerated_cmap.labels_raw_to_texture_direct( + data, direct_label_colormap + ) label_mapping = ( direct_label_colormap._values_mapping_to_minimum_values_set()[0] ) @@ -270,15 +285,15 @@ def test_test_cast_direct_labels_to_minimum_type_no_jit(num, dtype): cmap.color_dict[None] = np.array([1, 1, 1, 1]) data = np.arange(10, dtype=np.uint32) data[2] = 80005 - cast = colormap._labels_raw_to_texture_direct(data, cmap) + cast = _accelerated_cmap.labels_raw_to_texture_direct(data, cmap) assert cast.dtype == dtype def test_zero_preserving_modulo_naive(): pytest.importorskip('numba') data = np.arange(1000, dtype=np.uint32) - res1 = colormap._zero_preserving_modulo_numpy(data, 49, np.uint8) - res2 = colormap._zero_preserving_modulo(data, 49, np.uint8) + res1 = _accelerated_cmap.zero_preserving_modulo_numpy(data, 49, np.uint8) + res2 = _accelerated_cmap.zero_preserving_modulo(data, 49, np.uint8) npt.assert_array_equal(res1, res2) @@ -332,7 +347,9 @@ def test_label_colormap_using_cache(dtype, monkeypatch): expected = np.array([[0, 0, 0, 0], [1, 0, 0, 1], [0, 1, 0, 1]]) map1 = cmap.map(values) npt.assert_array_equal(map1, expected) - monkeypatch.setattr(colormap, '_zero_preserving_modulo_numpy', None) + monkeypatch.setattr( + _accelerated_cmap, 'zero_preserving_modulo_numpy', None + ) map2 = cmap.map(values) npt.assert_array_equal(map1, map2) @@ -341,7 +358,7 @@ def test_label_colormap_using_cache(dtype, monkeypatch): def test_cast_direct_labels_to_minimum_type_naive(size): pytest.importorskip('numba') data = np.arange(size, dtype=np.uint32) - dtype = colormap.minimum_dtype_for_labels(size) + dtype = _accelerated_cmap.minimum_dtype_for_labels(size) cmap = DirectLabelColormap( color_dict={ None: np.array([1, 1, 1, 1]), @@ -355,8 +372,8 @@ def test_cast_direct_labels_to_minimum_type_naive(size): }, ) cmap.color_dict[None] = np.array([255, 255, 255, 255]) - res1 = colormap._labels_raw_to_texture_direct(data, cmap) - res2 = colormap._labels_raw_to_texture_direct_numpy(data, cmap) + res1 = _accelerated_cmap.labels_raw_to_texture_direct(data, cmap) + res2 = _accelerated_cmap._labels_raw_to_texture_direct_numpy(data, cmap) npt.assert_array_equal(res1, res2) assert res1.dtype == dtype assert res2.dtype == dtype @@ -506,3 +523,28 @@ def test_direct_colormap_negative_values_numpy(): np.array([-1, -2, 5], dtype=np.int8), cmap ) npt.assert_array_equal(res, [0, 1, 0]) + + +@pytest.mark.parametrize( + 'colormap_like', + [ + ['red', 'blue'], + [[1, 0, 0, 1], [0, 0, 1, 1]], + {None: 'transparent', 1: 'red', 2: 'blue'}, + {None: [0, 0, 0, 0], 1: [1, 0, 0, 1], 2: [0, 0, 1, 1]}, + defaultdict(lambda: 'transparent', {1: 'red', 2: 'blue'}), + CyclicLabelColormap(['red', 'blue']), + DirectLabelColormap( + color_dict={None: 'transparent', 1: 'red', 2: 'blue'} + ), + 5, # test ValueError + ], +) +def test_normalize_label_colormap(colormap_like): + if not isinstance(colormap_like, int): + assert isinstance( + _normalize_label_colormap(colormap_like), LabelColormapBase + ) + else: + with pytest.raises(ValueError, match='Unable to interpret'): + _normalize_label_colormap(colormap_like) diff --git a/napari/utils/colormaps/colormap.py b/napari/utils/colormaps/colormap.py index ff05e02cd66..b07ccb8ff18 100644 --- a/napari/utils/colormaps/colormap.py +++ b/napari/utils/colormaps/colormap.py @@ -1,4 +1,5 @@ from collections import defaultdict +from collections.abc import MutableMapping, Sequence from functools import cached_property from typing import ( TYPE_CHECKING, @@ -15,6 +16,7 @@ from napari._pydantic_compat import Field, PrivateAttr, validator from napari.utils.color import ColorArray +from napari.utils.colormaps import _accelerated_cmap as _accel_cmap from napari.utils.colormaps.colorbars import make_colorbar from napari.utils.colormaps.standardize_color import transform_color from napari.utils.compat import StrEnum @@ -26,10 +28,6 @@ if TYPE_CHECKING: from numba import typed -MAPPING_OF_UNKNOWN_VALUE = 0 -# For direct mode we map all unknown values to single value -# for simplicity of implementation we select 0 - class ColormapInterpolationMode(StrEnum): """INTERPOLATION: Interpolation mode for colormaps. @@ -314,7 +312,7 @@ def _data_to_texture( return _cast_labels_data_to_texture_dtype_auto(values, self) def _map_without_cache(self, values) -> np.ndarray: - texture_dtype_values = _zero_preserving_modulo_numpy( + texture_dtype_values = _accel_cmap.zero_preserving_modulo_numpy( values, len(self.colors) - 1, values.dtype, @@ -494,7 +492,9 @@ def map(self, values: Union[np.ndarray, np.integer, int]) -> np.ndarray: if mapper is not None: mapped = mapper[values] else: - values_cast = _labels_raw_to_texture_direct(values, self) + values_cast = _accel_cmap.labels_raw_to_texture_direct( + values, self + ) mapped = self._map_precast(values_cast, apply_selection=True) if self.use_selection: @@ -503,7 +503,7 @@ def map(self, values: Union[np.ndarray, np.integer, int]) -> np.ndarray: def _map_without_cache(self, values: np.ndarray) -> np.ndarray: cmap = self._cmap_without_selection() - cast = _labels_raw_to_texture_direct(values, cmap) + cast = _accel_cmap.labels_raw_to_texture_direct(values, cmap) return self._map_precast(cast, apply_selection=False) def _map_precast(self, values, apply_selection) -> np.ndarray: @@ -581,10 +581,10 @@ def _label_mapping_and_color_dict( ) -> tuple[dict[Optional[int], int], dict[int, np.ndarray]]: color_to_labels: dict[tuple[int, ...], list[Optional[int]]] = {} labels_to_new_labels: dict[Optional[int], int] = { - None: MAPPING_OF_UNKNOWN_VALUE + None: _accel_cmap.MAPPING_OF_UNKNOWN_VALUE } new_color_dict: dict[int, np.ndarray] = { - MAPPING_OF_UNKNOWN_VALUE: self.default_color, + _accel_cmap.MAPPING_OF_UNKNOWN_VALUE: self.default_color, } for label, color in self.color_dict.items(): @@ -632,7 +632,9 @@ def _get_typed_dict_mapping(self, data_dtype: np.dtype) -> 'typed.Dict': from numba import typed, types # num_unique_colors + 2 because we need to map None and background - target_type = minimum_dtype_for_labels(self._num_unique_colors + 2) + target_type = _accel_cmap.minimum_dtype_for_labels( + self._num_unique_colors + 2 + ) dkt = typed.Dict.empty( key_type=getattr(types, data_dtype.name), @@ -663,7 +665,9 @@ def _array_map(self): 'Cannot use numpy implementation for large values of labels ' 'direct colormap. Please install numba.' ) - dtype = minimum_dtype_for_labels(self._num_unique_colors + 2) + dtype = _accel_cmap.minimum_dtype_for_labels( + self._num_unique_colors + 2 + ) label_mapping = self._values_mapping_to_minimum_values_set()[0] # We need 2 + the max value: one because we will be indexing with the @@ -671,7 +675,7 @@ def _array_map(self): # that index and map to the default value, rather than to the max # value in the map. mapper = np.full( - (max_value + 2), MAPPING_OF_UNKNOWN_VALUE, dtype=dtype + (max_value + 2), _accel_cmap.MAPPING_OF_UNKNOWN_VALUE, dtype=dtype ) for key, val in label_mapping.items(): if key is None: @@ -775,14 +779,14 @@ def _cast_labels_data_to_texture_dtype_auto( data_arr = np.atleast_1d(data) num_colors = len(colormap.colors) - 1 - zero_preserving_modulo_func = _zero_preserving_modulo + zero_preserving_modulo_func = _accel_cmap.zero_preserving_modulo if isinstance(data, np.integer): - zero_preserving_modulo_func = _zero_preserving_modulo_numpy + zero_preserving_modulo_func = _accel_cmap.zero_preserving_modulo_numpy - dtype = minimum_dtype_for_labels(num_colors + 1) + dtype = _accel_cmap.minimum_dtype_for_labels(num_colors + 1) if colormap.use_selection: - selection_in_texture = _zero_preserving_modulo_numpy( + selection_in_texture = _accel_cmap.zero_preserving_modulo_numpy( np.array([colormap.selection]), num_colors, dtype ) converted = np.where( @@ -799,103 +803,6 @@ def _cast_labels_data_to_texture_dtype_auto( return np.reshape(converted, original_shape) -def _zero_preserving_modulo_numpy( - values: np.ndarray, n: int, dtype: np.dtype, to_zero: int = 0 -) -> np.ndarray: - """``(values - 1) % n + 1``, but with one specific value mapped to 0. - - This ensures (1) an output value in [0, n] (inclusive), and (2) that - no nonzero values in the input are zero in the output, other than the - ``to_zero`` value. - - Parameters - ---------- - values : np.ndarray - The dividend of the modulo operator. - n : int - The divisor. - dtype : np.dtype - The desired dtype for the output array. - to_zero : int, optional - A specific value to map to 0. (By default, 0 itself.) - - Returns - ------- - np.ndarray - The result: 0 for the ``to_zero`` value, ``values % n + 1`` - everywhere else. - """ - res = ((values - 1) % n + 1).astype(dtype) - res[values == to_zero] = 0 - return res - - -def _zero_preserving_modulo_loop( - values: np.ndarray, n: int, dtype: np.dtype, to_zero: int = 0 -) -> np.ndarray: - """``(values - 1) % n + 1``, but with one specific value mapped to 0. - - This ensures (1) an output value in [0, n] (inclusive), and (2) that - no nonzero values in the input are zero in the output, other than the - ``to_zero`` value. - - Parameters - ---------- - values : np.ndarray - The dividend of the modulo operator. - n : int - The divisor. - dtype : np.dtype - The desired dtype for the output array. - to_zero : int, optional - A specific value to map to 0. (By default, 0 itself.) - - Returns - ------- - np.ndarray - The result: 0 for the ``to_zero`` value, ``values % n + 1`` - everywhere else. - """ - result = np.empty_like(values, dtype=dtype) - # need to preallocate numpy array for asv memory benchmarks - return _zero_preserving_modulo_inner_loop(values, n, to_zero, out=result) - - -def _zero_preserving_modulo_inner_loop( - values: np.ndarray, n: int, to_zero: int, out: np.ndarray -) -> np.ndarray: - """``(values - 1) % n + 1``, but with one specific value mapped to 0. - - This ensures (1) an output value in [0, n] (inclusive), and (2) that - no nonzero values in the input are zero in the output, other than the - ``to_zero`` value. - - Parameters - ---------- - values : np.ndarray - The dividend of the modulo operator. - n : int - The divisor. - to_zero : int - A specific value to map to 0. (Usually, 0 itself.) - out : np.ndarray - Preallocated output array - - Returns - ------- - np.ndarray - The result: 0 for the ``to_zero`` value, ``values % n + 1`` - everywhere else. - """ - for i in prange(values.size): - if values.flat[i] == to_zero: - out.flat[i] = 0 - else: - out.flat[i] = (values.flat[i] - 1) % n + 1 - - return out - - @overload def _cast_labels_data_to_texture_dtype_direct( data: np.ndarray, direct_colormap: DirectLabelColormap @@ -949,89 +856,21 @@ def _cast_labels_data_to_texture_dtype_direct( if isinstance(data, np.integer): mapper = direct_colormap._label_mapping_and_color_dict[0] - target_dtype = minimum_dtype_for_labels( + target_dtype = _accel_cmap.minimum_dtype_for_labels( direct_colormap._num_unique_colors + 2 ) return target_dtype.type( - mapper.get(int(data), MAPPING_OF_UNKNOWN_VALUE) + mapper.get(int(data), _accel_cmap.MAPPING_OF_UNKNOWN_VALUE) ) original_shape = np.shape(data) array_data = np.atleast_1d(data) return np.reshape( - _labels_raw_to_texture_direct(array_data, direct_colormap), + _accel_cmap.labels_raw_to_texture_direct(array_data, direct_colormap), original_shape, ) -def _labels_raw_to_texture_direct_numpy( - data: np.ndarray, direct_colormap: DirectLabelColormap -) -> np.ndarray: - """Convert labels data to the data type used in the texture. - - This implementation uses numpy vectorized operations. - - See `_cast_labels_data_to_texture_dtype_direct` for more details. - """ - if direct_colormap.use_selection: - return (data == direct_colormap.selection).astype(np.uint8) - mapper = direct_colormap._array_map - if any(x < 0 for x in direct_colormap.color_dict if x is not None): - half_shape = mapper.shape[0] // 2 - 1 - data = np.clip(data, -half_shape, half_shape) - else: - data = np.clip(data, 0, mapper.shape[0] - 1) - - return mapper[data] - - -def _labels_raw_to_texture_direct_loop( - data: np.ndarray, direct_colormap: DirectLabelColormap -) -> np.ndarray: - """ - Cast direct labels to the minimum type. - - Parameters - ---------- - data : np.ndarray - The input data array. - direct_colormap : DirectLabelColormap - The direct colormap. - - Returns - ------- - np.ndarray - The cast data array. - """ - if direct_colormap.use_selection: - return (data == direct_colormap.selection).astype(np.uint8) - - dkt = direct_colormap._get_typed_dict_mapping(data.dtype) - target_dtype = minimum_dtype_for_labels( - direct_colormap._num_unique_colors + 2 - ) - result_array = np.full_like( - data, MAPPING_OF_UNKNOWN_VALUE, dtype=target_dtype - ) - return _labels_raw_to_texture_direct_inner_loop(data, dkt, result_array) - - -def _labels_raw_to_texture_direct_inner_loop( - data: np.ndarray, dkt: 'typed.Dict', out: np.ndarray -) -> np.ndarray: - """ - Relabel data using typed dict with mapping unknown labels to default value - """ - # The numba typed dict does not provide official Api for - # determine key and value types - for i in prange(data.size): - val = data.flat[i] - if val in dkt: - out.flat[i] = dkt[data.flat[i]] - - return out - - def _texture_dtype(num_colors: int, dtype: np.dtype) -> np.dtype: """Compute VisPy texture dtype given number of colors and raw data dtype. @@ -1044,44 +883,43 @@ def _texture_dtype(num_colors: int, dtype: np.dtype) -> np.dtype: return np.dtype(np.uint8) if dtype.itemsize == 2: return np.dtype(np.uint16) - return minimum_dtype_for_labels(num_colors) + return _accel_cmap.minimum_dtype_for_labels(num_colors) + +def _normalize_label_colormap( + any_colormap_like, +) -> Union[CyclicLabelColormap, DirectLabelColormap]: + """Convenience function to convert color containers to LabelColormaps. -def minimum_dtype_for_labels(num_colors: int) -> np.dtype: - """Return the minimum texture dtype that can hold given number of colors. + A list of colors or 2D nparray of colors is interpreted as a color cycle + (`CyclicLabelColormap`), and a mapping of colors is interpreted as a direct + color map (`DirectLabelColormap`). Parameters ---------- - num_colors : int - Number of unique colors in the data. + any_colormap_like : Sequence[color], MutableMapping[int, color], ... + An object that can be interpreted as a LabelColormap, including a + LabelColormap directly. Returns ------- - np.dtype - Minimum dtype that can hold the number of colors. + CyclicLabelColormap | DirectLabelColormap + The computed LabelColormap object. """ - if num_colors <= np.iinfo(np.uint8).max: - return np.dtype(np.uint8) - if num_colors <= np.iinfo(np.uint16).max: - return np.dtype(np.uint16) - return np.dtype(np.float32) - - -try: - import numba -except ModuleNotFoundError: - _zero_preserving_modulo = _zero_preserving_modulo_numpy - _labels_raw_to_texture_direct = _labels_raw_to_texture_direct_numpy - prange = range -else: - _zero_preserving_modulo_inner_loop = numba.njit(parallel=True, cache=True)( - _zero_preserving_modulo_inner_loop + if isinstance( + any_colormap_like, (CyclicLabelColormap, DirectLabelColormap) + ): + return any_colormap_like + if isinstance(any_colormap_like, Sequence): + return CyclicLabelColormap(any_colormap_like) + if isinstance(any_colormap_like, MutableMapping): + return DirectLabelColormap(color_dict=any_colormap_like) + if ( + isinstance(any_colormap_like, np.ndarray) + and any_colormap_like.ndim == 2 + and any_colormap_like.shape[1] in (3, 4) + ): + return CyclicLabelColormap(any_colormap_like) + raise ValueError( + f'Unable to interpret as labels colormap: {any_colormap_like}' ) - _zero_preserving_modulo = _zero_preserving_modulo_loop - _labels_raw_to_texture_direct = _labels_raw_to_texture_direct_loop - _labels_raw_to_texture_direct_inner_loop = numba.njit( - parallel=True, cache=True - )(_labels_raw_to_texture_direct_inner_loop) - prange = numba.prange # type: ignore [misc] - - del numba diff --git a/napari/utils/colormaps/colormap_utils.py b/napari/utils/colormaps/colormap_utils.py index 8c53b16b3c4..a8967b5e968 100644 --- a/napari/utils/colormaps/colormap_utils.py +++ b/napari/utils/colormaps/colormap_utils.py @@ -16,13 +16,13 @@ ) from vispy.color.colormap import LUT_len +from napari.utils.colormaps._accelerated_cmap import minimum_dtype_for_labels from napari.utils.colormaps.bop_colors import bopd from napari.utils.colormaps.colormap import ( Colormap, ColormapInterpolationMode, CyclicLabelColormap, DirectLabelColormap, - minimum_dtype_for_labels, ) from napari.utils.colormaps.inverse_colormaps import inverse_cmaps from napari.utils.colormaps.standardize_color import transform_color diff --git a/napari/utils/events/_tests/test_selectable_list.py b/napari/utils/events/_tests/test_selectable_list.py index 505b7db921b..dc4472146ca 100644 --- a/napari/utils/events/_tests/test_selectable_list.py +++ b/napari/utils/events/_tests/test_selectable_list.py @@ -32,3 +32,45 @@ def test_del_discards_from_selection(): selectable_list = _make_selectable_list_and_select_first(['a', 'b', 'c']) del selectable_list[0] assert 'a' not in selectable_list.selection + + +def test_select_next(): + selectable_list = _make_selectable_list_and_select_first(['a', 'b', 'c']) + assert 'a' in selectable_list.selection + selectable_list.select_next() + assert 'a' not in selectable_list.selection + assert 'b' in selectable_list.selection + + +def test_select_previous(): + selectable_list = _make_selectable_list_and_select_first(['a', 'b', 'c']) + selectable_list.selection.active = 'c' + assert 'a' not in selectable_list.selection + assert 'c' in selectable_list.selection + selectable_list.select_previous() + assert 'c' not in selectable_list.selection + assert 'b' in selectable_list.selection + + +def test_shift_select_next_previous(): + selectable_list = _make_selectable_list_and_select_first(['a', 'b', 'c']) + assert 'a' in selectable_list.selection + selectable_list.select_next(shift=True) + assert 'a' in selectable_list.selection + assert 'b' in selectable_list.selection + selectable_list.select_previous(shift=True) + assert 'a' in selectable_list.selection + assert 'b' not in selectable_list.selection + + +def test_shift_select_previous_next(): + selectable_list = _make_selectable_list_and_select_first(['a', 'b', 'c']) + selectable_list.selection.active = 'c' + assert 'a' not in selectable_list.selection + assert 'c' in selectable_list.selection + selectable_list.select_previous(shift=True) + assert 'b' in selectable_list.selection + assert 'c' in selectable_list.selection + selectable_list.select_next(shift=True) + assert 'b' not in selectable_list.selection + assert 'c' in selectable_list.selection diff --git a/napari/utils/events/containers/_selectable_list.py b/napari/utils/events/containers/_selectable_list.py index 23d6559d909..9a7bcde7dd9 100644 --- a/napari/utils/events/containers/_selectable_list.py +++ b/napari/utils/events/containers/_selectable_list.py @@ -129,13 +129,17 @@ def move_selected(self, index: int, insert: int) -> None: def select_next(self, step: int = 1, shift: bool = False) -> None: """Selects next item from list.""" - if self.selection: + if self.selection and self.selection._current: idx = self.index(self.selection._current) + step if len(self) > idx >= 0: next_layer = self[idx] if shift: - self.selection.add(next_layer) - self.selection._current = next_layer + if next_layer in self.selection: + self.selection.remove(self.selection._current) + self.selection._current = next_layer + else: + self.selection.add(next_layer) + self.selection._current = next_layer else: self.selection.active = next_layer elif len(self) > 0: diff --git a/napari/utils/notebook_display.py b/napari/utils/notebook_display.py index fd3e27fcadb..5727bf50fb7 100644 --- a/napari/utils/notebook_display.py +++ b/napari/utils/notebook_display.py @@ -9,7 +9,7 @@ from lxml.html.clean import Cleaner lxml_unavailable = False -except ModuleNotFoundError: +except ImportError: lxml_unavailable = True from napari.utils.io import imsave_png @@ -79,9 +79,9 @@ def _clean_alt_text(self, alt_text): if alt_text is not None: if lxml_unavailable: warn( - 'The lxml library is not installed, and is required to ' - 'sanitize alt text for napari screenshots. Alt-text ' - 'will be stripped altogether without lxml.' + 'The lxml_html_clean library is not installed, and is ' + 'required to sanitize alt text for napari screenshots. ' + 'Alt Text will be stripped altogether.' ) return None # cleaner won't recognize escaped script tags, so always unescape diff --git a/pyproject.toml b/pyproject.toml index 1f3af819f39..7feb77bb0a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,8 +97,8 @@ pyside2 = [ pyside6_experimental = [ "PySide6 < 6.5 ; python_version < '3.12'" ] -pyqt6_experimental = [ - "PyQt6", +pyqt6 = [ + "PyQt6 > 6.5", "PyQt6 != 6.6.1 ; platform_system == 'Darwin'" ] pyside = [ diff --git a/resources/constraints/constraints_py3.10.txt b/resources/constraints/constraints_py3.10.txt index 4dba43f4948..72bad4ddaf8 100644 --- a/resources/constraints/constraints_py3.10.txt +++ b/resources/constraints/constraints_py3.10.txt @@ -44,13 +44,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -73,7 +73,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -95,7 +95,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -106,7 +106,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -196,7 +196,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -291,7 +291,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -310,7 +310,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -356,7 +356,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -368,7 +368,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -460,7 +460,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -508,11 +508,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -572,7 +572,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.10_docs.txt b/resources/constraints/constraints_py3.10_docs.txt index 2a46bbe2fa7..0f2177b79c1 100644 --- a/resources/constraints/constraints_py3.10_docs.txt +++ b/resources/constraints/constraints_py3.10_docs.txt @@ -57,13 +57,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -93,7 +93,7 @@ fasteners==0.19 # via zarr fastjsonschema==2.20.0 # via nbformat -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -119,7 +119,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via @@ -134,7 +134,7 @@ imageio-ffmpeg==0.5.1 # via -r docs/requirements.txt imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -269,7 +269,7 @@ nibabel==5.2.1 # via nilearn nilearn==0.10.4 # via -r resources/constraints/version_denylist_examples.txt -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -375,7 +375,7 @@ pillow==10.3.0 # sphinx-gallery pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -394,7 +394,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -412,7 +412,7 @@ pyautogui==0.9.54 # via napari (napari_repo/pyproject.toml) pyconify==0.1.6 # via superqt -pydantic==1.10.16 +pydantic==1.10.17 # via # -r napari_repo/resources/constraints/pydantic_le_2.txt # napari (napari_repo/pyproject.toml) @@ -444,7 +444,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -456,7 +456,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -553,7 +553,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scikit-learn==1.5.0 # via nilearn @@ -563,7 +563,7 @@ scipy==1.13.1 # nilearn # scikit-image # scikit-learn -setuptools==70.0.0 +setuptools==70.1.0 # via imageio-ffmpeg shellingham==1.5.4 # via typer @@ -631,7 +631,7 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx -sqlalchemy==2.0.30 +sqlalchemy==2.0.31 # via jupyter-cache stack-data==0.6.3 # via ipython @@ -648,13 +648,13 @@ tabulate==0.9.0 # via # jupyter-cache # numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) threadpoolctl==3.5.0 # via scikit-learn -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -722,7 +722,7 @@ urllib3==2.2.2 # via requests uvicorn==0.30.1 # via sphinx-autobuild -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.10_macos_arm.txt b/resources/constraints/constraints_py3.10_macos_arm.txt index 4876f7bff18..94101222e89 100644 --- a/resources/constraints/constraints_py3.10_macos_arm.txt +++ b/resources/constraints/constraints_py3.10_macos_arm.txt @@ -46,13 +46,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -75,7 +75,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -96,7 +96,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -107,7 +107,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -197,7 +197,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -260,7 +260,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -279,7 +279,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -334,7 +334,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -346,7 +346,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -434,7 +434,7 @@ rpds-py==0.18.1 # referencing rubicon-objc==0.4.9 # via mouseinfo -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -480,11 +480,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -540,7 +540,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.10_pydantic_1.txt b/resources/constraints/constraints_py3.10_pydantic_1.txt index 7d0e0585364..ef080fb4409 100644 --- a/resources/constraints/constraints_py3.10_pydantic_1.txt +++ b/resources/constraints/constraints_py3.10_pydantic_1.txt @@ -42,13 +42,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -71,7 +71,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -93,7 +93,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -104,7 +104,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -194,7 +194,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -289,7 +289,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -308,7 +308,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -326,7 +326,7 @@ pyautogui==0.9.54 # via napari (napari_repo/pyproject.toml) pyconify==0.1.6 # via superqt -pydantic==1.10.16 +pydantic==1.10.17 # via # -r napari_repo/resources/constraints/pydantic_le_2.txt # napari (napari_repo/pyproject.toml) @@ -353,7 +353,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -365,7 +365,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -457,7 +457,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -505,11 +505,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -568,7 +568,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.11.txt b/resources/constraints/constraints_py3.11.txt index 3708e12f4ff..3b959a5264c 100644 --- a/resources/constraints/constraints_py3.11.txt +++ b/resources/constraints/constraints_py3.11.txt @@ -44,13 +44,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -68,7 +68,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -90,7 +90,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -101,7 +101,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via dask in-n-out==0.2.1 # via app-model @@ -189,7 +189,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -284,7 +284,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -303,7 +303,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -349,7 +349,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -361,7 +361,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -451,7 +451,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -497,11 +497,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -553,7 +553,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.11_macos_arm.txt b/resources/constraints/constraints_py3.11_macos_arm.txt index 3f65222852d..f50ddf54947 100644 --- a/resources/constraints/constraints_py3.11_macos_arm.txt +++ b/resources/constraints/constraints_py3.11_macos_arm.txt @@ -46,13 +46,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -70,7 +70,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -91,7 +91,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -102,7 +102,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via dask in-n-out==0.2.1 # via app-model @@ -190,7 +190,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -253,7 +253,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -272,7 +272,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -327,7 +327,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -339,7 +339,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -427,7 +427,7 @@ rpds-py==0.18.1 # referencing rubicon-objc==0.4.9 # via mouseinfo -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -473,11 +473,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -525,7 +525,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.11_pydantic_1.txt b/resources/constraints/constraints_py3.11_pydantic_1.txt index 9ff3a3429fa..c90348cff49 100644 --- a/resources/constraints/constraints_py3.11_pydantic_1.txt +++ b/resources/constraints/constraints_py3.11_pydantic_1.txt @@ -42,13 +42,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -66,7 +66,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -88,7 +88,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -99,7 +99,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via dask in-n-out==0.2.1 # via app-model @@ -187,7 +187,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -282,7 +282,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -301,7 +301,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -319,7 +319,7 @@ pyautogui==0.9.54 # via napari (napari_repo/pyproject.toml) pyconify==0.1.6 # via superqt -pydantic==1.10.16 +pydantic==1.10.17 # via # -r napari_repo/resources/constraints/pydantic_le_2.txt # napari (napari_repo/pyproject.toml) @@ -346,7 +346,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -358,7 +358,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -448,7 +448,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -494,11 +494,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -549,7 +549,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.12.txt b/resources/constraints/constraints_py3.12.txt index 06b696e0084..1ef588f451c 100644 --- a/resources/constraints/constraints_py3.12.txt +++ b/resources/constraints/constraints_py3.12.txt @@ -44,13 +44,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -68,7 +68,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -89,7 +89,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -186,7 +186,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -280,7 +280,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -299,7 +299,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -345,7 +345,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -357,7 +357,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -437,7 +437,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -478,11 +478,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -531,7 +531,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.12_macos_arm.txt b/resources/constraints/constraints_py3.12_macos_arm.txt index e3960bb95b2..c760d6be303 100644 --- a/resources/constraints/constraints_py3.12_macos_arm.txt +++ b/resources/constraints/constraints_py3.12_macos_arm.txt @@ -46,13 +46,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -70,7 +70,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -91,7 +91,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -188,7 +188,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -250,7 +250,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -269,7 +269,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -324,7 +324,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -336,7 +336,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -414,7 +414,7 @@ rpds-py==0.18.1 # referencing rubicon-objc==0.4.9 # via mouseinfo -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -455,11 +455,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -506,7 +506,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.12_pydantic_1.txt b/resources/constraints/constraints_py3.12_pydantic_1.txt index ab39e15845b..3fa75aeae0f 100644 --- a/resources/constraints/constraints_py3.12_pydantic_1.txt +++ b/resources/constraints/constraints_py3.12_pydantic_1.txt @@ -42,13 +42,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -66,7 +66,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -87,7 +87,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -184,7 +184,7 @@ networkx==3.3 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -278,7 +278,7 @@ pillow==10.3.0 # scikit-image pint==0.24 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -297,7 +297,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -315,7 +315,7 @@ pyautogui==0.9.54 # via napari (napari_repo/pyproject.toml) pyconify==0.1.6 # via superqt -pydantic==1.10.16 +pydantic==1.10.17 # via # -r napari_repo/resources/constraints/pydantic_le_2.txt # napari (napari_repo/pyproject.toml) @@ -342,7 +342,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -354,7 +354,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -434,7 +434,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.23.2 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -475,11 +475,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -527,7 +527,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.9.txt b/resources/constraints/constraints_py3.9.txt index 4b090f85d14..759d720b581 100644 --- a/resources/constraints/constraints_py3.9.txt +++ b/resources/constraints/constraints_py3.9.txt @@ -43,13 +43,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -72,7 +72,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -90,7 +90,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -101,7 +101,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -195,7 +195,7 @@ networkx==3.2.1 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -290,7 +290,7 @@ pillow==10.3.0 # scikit-image pint==0.23 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -309,7 +309,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -355,7 +355,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -367,7 +367,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -459,7 +459,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.22.0 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -507,11 +507,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -569,7 +569,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.9_examples.txt b/resources/constraints/constraints_py3.9_examples.txt index 2a059cfdb04..ab4e4f47923 100644 --- a/resources/constraints/constraints_py3.9_examples.txt +++ b/resources/constraints/constraints_py3.9_examples.txt @@ -43,13 +43,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -72,7 +72,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -90,7 +90,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -101,7 +101,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -204,7 +204,7 @@ nibabel==5.2.1 # via nilearn nilearn==0.10.4 # via -r resources/constraints/version_denylist_examples.txt -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -307,7 +307,7 @@ pillow==10.3.0 # scikit-image pint==0.23 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -326,7 +326,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -372,7 +372,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -384,7 +384,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -477,7 +477,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.22.0 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scikit-learn==1.5.0 # via nilearn @@ -529,13 +529,13 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) threadpoolctl==3.5.0 # via scikit-learn -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -593,7 +593,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.9_macos_arm.txt b/resources/constraints/constraints_py3.9_macos_arm.txt index fcf7f673edb..b775c196bc9 100644 --- a/resources/constraints/constraints_py3.9_macos_arm.txt +++ b/resources/constraints/constraints_py3.9_macos_arm.txt @@ -45,13 +45,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -74,7 +74,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # virtualenv @@ -91,7 +91,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -102,7 +102,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -196,7 +196,7 @@ networkx==3.2.1 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -259,7 +259,7 @@ pillow==10.3.0 # scikit-image pint==0.23 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -278,7 +278,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -333,7 +333,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -345,7 +345,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -433,7 +433,7 @@ rpds-py==0.18.1 # referencing rubicon-objc==0.4.9 # via mouseinfo -scikit-image==0.22.0 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -479,11 +479,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -537,7 +537,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/constraints_py3.9_pydantic_1.txt b/resources/constraints/constraints_py3.9_pydantic_1.txt index eab06f39df1..23276a0e17f 100644 --- a/resources/constraints/constraints_py3.9_pydantic_1.txt +++ b/resources/constraints/constraints_py3.9_pydantic_1.txt @@ -41,13 +41,13 @@ comm==0.2.2 # via ipykernel contourpy==1.2.1 # via matplotlib -coverage==7.5.3 +coverage==7.5.4 # via # napari (napari_repo/pyproject.toml) # pytest-cov cycler==0.12.1 # via matplotlib -dask==2024.6.0 +dask==2024.6.2 # via napari (napari_repo/pyproject.toml) debugpy==1.8.1 # via ipykernel @@ -70,7 +70,7 @@ executing==2.0.1 # via stack-data fasteners==0.19 # via zarr -filelock==3.15.1 +filelock==3.15.4 # via # torch # triton @@ -88,7 +88,7 @@ heapdict==1.0.1 # via cachey hsluv==5.0.4 # via vispy -hypothesis==6.103.2 +hypothesis==6.103.4 # via napari (napari_repo/pyproject.toml) idna==3.7 # via requests @@ -99,7 +99,7 @@ imageio==2.34.1 # scikit-image imagesize==1.4.1 # via sphinx -importlib-metadata==7.1.0 +importlib-metadata==7.2.1 # via # build # dask @@ -193,7 +193,7 @@ networkx==3.2.1 # via # scikit-image # torch -npe2==0.7.5 +npe2==0.7.6 # via # napari (napari_repo/pyproject.toml) # napari-plugin-manager @@ -288,7 +288,7 @@ pillow==10.3.0 # scikit-image pint==0.23 # via napari (napari_repo/pyproject.toml) -pip==24.0 +pip==24.1 # via napari-plugin-manager platformdirs==4.2.2 # via @@ -307,7 +307,7 @@ pretend==1.0.9 # via napari (napari_repo/pyproject.toml) prompt-toolkit==3.0.47 # via ipython -psutil==5.9.8 +psutil==6.0.0 # via # napari (napari_repo/pyproject.toml) # ipykernel @@ -325,7 +325,7 @@ pyautogui==0.9.54 # via napari (napari_repo/pyproject.toml) pyconify==0.1.6 # via superqt -pydantic==1.10.16 +pydantic==1.10.17 # via # -r napari_repo/resources/constraints/pydantic_le_2.txt # napari (napari_repo/pyproject.toml) @@ -352,7 +352,7 @@ pyopengl==3.1.6 # napari (napari_repo/pyproject.toml) pyparsing==3.1.2 # via matplotlib -pyperclip==1.8.2 +pyperclip==1.9.0 # via mouseinfo pyproject-hooks==1.1.0 # via build @@ -364,7 +364,7 @@ pyqt5-sip==12.13.0 # via pyqt5 pyqt6==6.7.0 # via napari (napari_repo/pyproject.toml) -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -456,7 +456,7 @@ rpds-py==0.18.1 # via # jsonschema # referencing -scikit-image==0.22.0 +scikit-image==0.24.0 # via napari (napari_repo/pyproject.toml) scipy==1.13.1 # via @@ -504,11 +504,11 @@ sympy==1.12.1 # via torch tabulate==0.9.0 # via numpydoc -tensorstore==0.1.62 +tensorstore==0.1.63 # via # -r napari_repo/resources/constraints/version_denylist.txt # napari (napari_repo/pyproject.toml) -tifffile==2024.5.22 +tifffile==2024.6.18 # via # napari (napari_repo/pyproject.toml) # scikit-image @@ -565,7 +565,7 @@ tzdata==2024.1 # via pandas urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via napari (napari_repo/pyproject.toml) vispy==0.14.3 # via diff --git a/resources/constraints/version_denylist.txt b/resources/constraints/version_denylist.txt index 7f930fed497..f689ca503ec 100644 --- a/resources/constraints/version_denylist.txt +++ b/resources/constraints/version_denylist.txt @@ -4,3 +4,4 @@ PySide6 != 6.4.3, !=6.5.0, !=6.5.1, !=6.5.1.1, !=6.5.2, != 6.5.3, != 6.6.0, != 6 pytest-json-report pyopengl!=3.1.7 tensorstore!=0.1.38 +zarr!=3.0.0a0 diff --git a/resources/requirements_mypy.txt b/resources/requirements_mypy.txt index 13435b205f9..4e988603b84 100644 --- a/resources/requirements_mypy.txt +++ b/resources/requirements_mypy.txt @@ -26,7 +26,7 @@ mypy==1.10.0 # via -r napari_repo/resources/requirements_mypy.in mypy-extensions==1.0.0 # via mypy -npe2==0.7.5 +npe2==0.7.6 # via -r napari_repo/resources/requirements_mypy.in numpy==2.0.0 # via -r napari_repo/resources/requirements_mypy.in @@ -54,7 +54,7 @@ pyproject-hooks==1.1.0 # via build pyqt6==6.7.0 # via -r napari_repo/resources/requirements_mypy.in -pyqt6-qt6==6.7.1 +pyqt6-qt6==6.7.2 # via pyqt6 pyqt6-sip==13.6.0 # via pyqt6 @@ -81,7 +81,7 @@ typer==0.12.3 # via npe2 types-pyyaml==6.0.12.20240311 # via -r napari_repo/resources/requirements_mypy.in -types-requests==2.32.0.20240602 +types-requests==2.32.0.20240622 # via -r napari_repo/resources/requirements_mypy.in types-setuptools==70.0.0.20240524 # via -r napari_repo/resources/requirements_mypy.in diff --git a/tools/check_updated_packages.py b/tools/check_updated_packages.py index fa2cc53ab71..3d8e485af51 100644 --- a/tools/check_updated_packages.py +++ b/tools/check_updated_packages.py @@ -141,7 +141,7 @@ def calc_only_direct_updates( packages = ( metadata['dependencies'] + optional_dependencies['pyqt5'] - + optional_dependencies['pyqt6_experimental'] + + optional_dependencies['pyqt6'] + optional_dependencies['pyside2'] + optional_dependencies['pyside6_experimental'] + optional_dependencies['testing']