-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create skeleton for napari plugin with collapsible widgets #218
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## napari-dev #218 +/- ##
===========================================
Coverage 99.71% 99.72%
===========================================
Files 13 15 +2
Lines 702 726 +24
===========================================
+ Hits 700 724 +24
Misses 2 2 ☔ View full report in Codecov by Sentry. |
043d0f0
to
8e88167
Compare
tests fail on FAILED tests/test_unit/test_filtering.py::test_savgol_filter_with_nans - numpy.linalg.LinAlgError: SVD did not converge in Linear Least Squares That's perplexing because this doesn't occur on any other OS, and also not on Windows on our other branches. My only guess is that the new dependencies cause us to end up with different versions of Maybe pinning |
That's not it. I tried pinning |
In the end this problem disappeared after I switched back to a pip installation of |
6f0b424
to
78983f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have locally played with the napari
plugin and works as expected
Tests pass locally too.
Code changes look reasonable to me - one suggestion going forwards around GUI test design.
def test_hello_button(loader_widget, capsys): | ||
"""Test that the hello button works as expected.""" | ||
hello_button = loader_widget.findChildren(QPushButton)[0] | ||
assert hello_button.text() == "Say hello" | ||
hello_button.click() | ||
captured = capsys.readouterr() | ||
assert "INFO: Hello, world!" in captured.out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably overkill right now, but will become important as the plugin grows.
You've split the functionality from the GUI code well in the loader widget (which is IMO good practice), but not in the test. I would consider (at least going forwards) doing the same for the tests - so if you change just one of the two things, you only adapt the related tests.
In other words
- write a unit test that checks that if you click the button, a mock
_on_hello_clicked
is called. - write a unit test that checks that if you execute
_on_hello_clicked
, the expected message is written to sysout.
Happy to have a pair-programming session to figure out the mocking, which is always a bit tricky, if you like 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great suggestion, thanks! I'm willing to do this in this PR (even if it's a toy example) so that I establish a pattern I can keep using in the future.
I'll give it shot on my own, and will ping you to double-check. We can do pair-programming if I'm completely baffled by the mocking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've attempted to split the above test as follows:
from unittest.mock import patch
def test_hello_button_calls_on_hello_clicked(loader_widget):
"""Test that clicking the hello button calls _on_hello_clicked."""
hello_button = loader_widget.findChildren(QPushButton)[0]
with patch.object(loader_widget, "_on_hello_clicked") as mock_method:
hello_button.click()
mock_method.assert_called_once()
def test_on_hello_clicked_outputs_message(loader_widget, capsys):
"""Test that _on_hello_clicked outputs the expected message."""
loader_widget._on_hello_clicked()
captured = capsys.readouterr()
assert "INFO: Hello, world!" in captured.out
but the first test keeps failing with:
E AssertionError: Expected '_on_hello_clicked' to have been called once. Called 0 times.}
I've also tried pytest.mocker
instead of unittest
but I keep stumbling on the same problem.
Am I fundamentally misunderstanding something about mocking?
If the solution isn't obvious we can also pair program at some point next week.
FWIW, I've played around with this, and the code below works. from movement.napari._loader_widget import Loader
def test_hello_button_calls_on_hello_clicked(make_napari_viewer, mocker):
"""Test that clicking the hello button calls _on_hello_clicked."""
mock_method = mocker.patch("movement.napari._loader_widget.Loader._on_hello_clicked")
loader = Loader(make_napari_viewer)
hello_button = loader.findChildren(QPushButton)[0]
hello_button.click()
mock_method.assert_called_once() |
78983f6
to
f20bfaf
Compare
Thanks a lot @alessandrofelder! It would have taken me forever to figure this out on my own. |
Quality Gate passedIssues Measures |
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * Create skeleton for napari plugin with collapsible widgets (#218) * initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * Added loader widget for poses * update widget tests * simplify dependency on brainglobe-utils * consistent monospace formatting for movement in public docstrings * get rid of code that's only relevant for displaying Tracks * enable visibility of napari layer tooltips * renamed widget to PosesLoader * make cmap optional in set_color_by method * wrote unit tests for napari convert module * wrote unit-tests for the layer styles module * linkcheck ignore zenodo redirects * move _sample_colormap out of PointsStyle class * small refactoring in the loader widget * Expand tests for loader widget * added comments and docstrings to napari plugin tests * refactored all napari tests into separate unit test folder * added napari-video to dependencies * replaced deprecated edge_width with border_width * got rid of widget pytest fixtures * remove duplicate word from docstring * remove napari-video dependency * include napari extras in docs requirements * add test for _on_browse_clicked method * getOpenFileName returns tuple, not str * simplify poses_to_napari_tracks Co-authored-by: Chang Huan Lo <[email protected]> * [pre-commit.ci] pre-commit autoupdate (#338) updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.2](astral-sh/ruff-pre-commit@v0.6.9...v0.7.2) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.13.0](pre-commit/mirrors-mypy@v1.11.2...v1.13.0) - [github.com/mgedmin/check-manifest: 0.49 → 0.50](mgedmin/check-manifest@0.49...0.50) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Implement `compute_speed` and `compute_path_length` (#280) * implement compute_speed and compute_path_length functions * added speed to existing kinematics unit test * rewrote compute_path_length with various nan policies * unit test compute_path_length across time ranges * fixed and refactor compute_path_length and its tests * fixed docstring for compute_path_length * Accept suggestion on docstring wording Co-authored-by: Chang Huan Lo <[email protected]> * Remove print statement from test Co-authored-by: Chang Huan Lo <[email protected]> * Ensure nan report is printed Co-authored-by: Chang Huan Lo <[email protected]> * adapt warning message match in test * change 'any' to 'all' * uniform wording across path length docstrings * (mostly) leave time range validation to xarray slice * refactored parameters for test across time ranges * simplified test for path lenght with nans * replace drop policy with ffill * remove B905 ruff rule * make pre-commit happy --------- Co-authored-by: Chang Huan Lo <[email protected]> * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * avoid redefining duplicate attributes in child dataclass * modify test case to match poses_to_napari_tracks simplification * expected_log_messages should be a subset of captured messages Co-authored-by: Chang Huan Lo <[email protected]> * fix typo Co-authored-by: Chang Huan Lo <[email protected]> * use names for Qwidgets * reorganised test_valid_poses_to_napari_tracks * parametrised layer style tests * delet integration test which was reintroduced after conflict resolution * added test about file filters * deleted obsolete loader widget file (had snuck back in due to conflict merging) * combine tests for button callouts Co-authored-by: Chang Huan Lo <[email protected]> * Simplify test_layer_style_as_kwargs Co-authored-by: Chang Huan Lo <[email protected]> --------- Co-authored-by: Chang Huan Lo <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * Create skeleton for napari plugin with collapsible widgets (#218) * initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * Added loader widget for poses * update widget tests * simplify dependency on brainglobe-utils * consistent monospace formatting for movement in public docstrings * get rid of code that's only relevant for displaying Tracks * enable visibility of napari layer tooltips * renamed widget to PosesLoader * make cmap optional in set_color_by method * wrote unit tests for napari convert module * wrote unit-tests for the layer styles module * linkcheck ignore zenodo redirects * move _sample_colormap out of PointsStyle class * small refactoring in the loader widget * Expand tests for loader widget * added comments and docstrings to napari plugin tests * refactored all napari tests into separate unit test folder * added napari-video to dependencies * replaced deprecated edge_width with border_width * got rid of widget pytest fixtures * remove duplicate word from docstring * remove napari-video dependency * include napari extras in docs requirements * add test for _on_browse_clicked method * getOpenFileName returns tuple, not str * simplify poses_to_napari_tracks Co-authored-by: Chang Huan Lo <[email protected]> * [pre-commit.ci] pre-commit autoupdate (#338) updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.2](astral-sh/ruff-pre-commit@v0.6.9...v0.7.2) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.13.0](pre-commit/mirrors-mypy@v1.11.2...v1.13.0) - [github.com/mgedmin/check-manifest: 0.49 → 0.50](mgedmin/check-manifest@0.49...0.50) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Implement `compute_speed` and `compute_path_length` (#280) * implement compute_speed and compute_path_length functions * added speed to existing kinematics unit test * rewrote compute_path_length with various nan policies * unit test compute_path_length across time ranges * fixed and refactor compute_path_length and its tests * fixed docstring for compute_path_length * Accept suggestion on docstring wording Co-authored-by: Chang Huan Lo <[email protected]> * Remove print statement from test Co-authored-by: Chang Huan Lo <[email protected]> * Ensure nan report is printed Co-authored-by: Chang Huan Lo <[email protected]> * adapt warning message match in test * change 'any' to 'all' * uniform wording across path length docstrings * (mostly) leave time range validation to xarray slice * refactored parameters for test across time ranges * simplified test for path lenght with nans * replace drop policy with ffill * remove B905 ruff rule * make pre-commit happy --------- Co-authored-by: Chang Huan Lo <[email protected]> * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * avoid redefining duplicate attributes in child dataclass * modify test case to match poses_to_napari_tracks simplification * expected_log_messages should be a subset of captured messages Co-authored-by: Chang Huan Lo <[email protected]> * fix typo Co-authored-by: Chang Huan Lo <[email protected]> * use names for Qwidgets * reorganised test_valid_poses_to_napari_tracks * parametrised layer style tests * delet integration test which was reintroduced after conflict resolution * added test about file filters * deleted obsolete loader widget file (had snuck back in due to conflict merging) * combine tests for button callouts Co-authored-by: Chang Huan Lo <[email protected]> * Simplify test_layer_style_as_kwargs Co-authored-by: Chang Huan Lo <[email protected]> --------- Co-authored-by: Chang Huan Lo <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency
* initialise napari plugin development * Create skeleton for napari plugin with collapsible widgets (#218) * initialise napari plugin development * initialise napari plugin development * create skeleton for napari plugin with collapsible widgets * add basic widget smoke tests and allow headless testing * do not depend on napari from pip * include napari option in install instructions * make meta_widget module private * pin atlasapi version to avoid unnecessary dependencies * pin napari >= 0.4.19 from conda-forge * switched to pip install of napari[all] * seperation of concerns in widget tests * add pytest-mock dev dependency * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * Added loader widget for poses * update widget tests * simplify dependency on brainglobe-utils * consistent monospace formatting for movement in public docstrings * get rid of code that's only relevant for displaying Tracks * enable visibility of napari layer tooltips * renamed widget to PosesLoader * make cmap optional in set_color_by method * wrote unit tests for napari convert module * wrote unit-tests for the layer styles module * linkcheck ignore zenodo redirects * move _sample_colormap out of PointsStyle class * small refactoring in the loader widget * Expand tests for loader widget * added comments and docstrings to napari plugin tests * refactored all napari tests into separate unit test folder * added napari-video to dependencies * replaced deprecated edge_width with border_width * got rid of widget pytest fixtures * remove duplicate word from docstring * remove napari-video dependency * include napari extras in docs requirements * add test for _on_browse_clicked method * getOpenFileName returns tuple, not str * simplify poses_to_napari_tracks Co-authored-by: Chang Huan Lo <[email protected]> * [pre-commit.ci] pre-commit autoupdate (#338) updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.2](astral-sh/ruff-pre-commit@v0.6.9...v0.7.2) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.13.0](pre-commit/mirrors-mypy@v1.11.2...v1.13.0) - [github.com/mgedmin/check-manifest: 0.49 → 0.50](mgedmin/check-manifest@0.49...0.50) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Implement `compute_speed` and `compute_path_length` (#280) * implement compute_speed and compute_path_length functions * added speed to existing kinematics unit test * rewrote compute_path_length with various nan policies * unit test compute_path_length across time ranges * fixed and refactor compute_path_length and its tests * fixed docstring for compute_path_length * Accept suggestion on docstring wording Co-authored-by: Chang Huan Lo <[email protected]> * Remove print statement from test Co-authored-by: Chang Huan Lo <[email protected]> * Ensure nan report is printed Co-authored-by: Chang Huan Lo <[email protected]> * adapt warning message match in test * change 'any' to 'all' * uniform wording across path length docstrings * (mostly) leave time range validation to xarray slice * refactored parameters for test across time ranges * simplified test for path lenght with nans * replace drop policy with ffill * remove B905 ruff rule * make pre-commit happy --------- Co-authored-by: Chang Huan Lo <[email protected]> * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * initialise napari plugin development * avoid redefining duplicate attributes in child dataclass * modify test case to match poses_to_napari_tracks simplification * expected_log_messages should be a subset of captured messages Co-authored-by: Chang Huan Lo <[email protected]> * fix typo Co-authored-by: Chang Huan Lo <[email protected]> * use names for Qwidgets * reorganised test_valid_poses_to_napari_tracks * parametrised layer style tests * delet integration test which was reintroduced after conflict resolution * added test about file filters * deleted obsolete loader widget file (had snuck back in due to conflict merging) * combine tests for button callouts Co-authored-by: Chang Huan Lo <[email protected]> * Simplify test_layer_style_as_kwargs Co-authored-by: Chang Huan Lo <[email protected]> --------- Co-authored-by: Chang Huan Lo <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Description
What is this PR
Why is this PR needed?
As explained in #31, I've shifted strategy for developing the
napari
plugin. This is the first in a series of multiple PRs, in which I'll try breaking down the work-in-progress contained in #112, and debug any obstacles I encounter along the way.The PRs will be merged into the
napari-dev
branch, until the plugin becomes minimally functional for users, at which point we'll merge thenapari-dev
branch intomain
.What does this PR do?
It sets up all the necessary components for a napari plugin, including optional dependencies, the
napari.yaml
file, a simple collapsible widget (which for now just includes a simple "hello, world" button as a placeholder), and some rudimentary smoke tests.I struggled with finding the right way to install
napari
, such that it works across OSes (both locally and in CI). While debugging this, I stumbled on an issue with the brainglobe-utils.In the end,
pip install napari[all]
seems to have worked across the platforms we test. For some reason, it doesn't work on recent Ubuntu versions (like my local Ubuntu 24.04), but I think I'm going to ignore this issue for now, open an issue about it, and wait for napari to deal with it.The optional dependencies included in
movement[napari]
are :but, after brainglobe/brainglobe-utils#84 is merged and a new version of
brainglobe-utils
is released, that can change to justnapari = ["napari[all]>=0.4.19", brainglobe-utils[qt]>=0.6"]
.The installation instruction and
tox
configuration have been updated accordingly (see sections on testing and docs below).References
#31
#112
brainglobe/brainglobe-utils#84
How has this PR been tested?
Tests have been added for the napari widget. The
tox
config has been modified to pass the right env variables for QT tests to work headlessly in CI. Additionally, an extra action has been added to the test workflow to facilitate this.Is this a breaking change?
No.
Does this PR require an update to the documentation?
Yes, and the installation instructions have been updated both in the README and the more detailed installation guide in the docs.
Checklist: