From 1147686836c17bc90ba6c1eace14b25f14eb6ffe Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:20:21 -0400 Subject: [PATCH 1/5] add test_optional_deps files & ci commands --- .github/workflows/ci.yml | 44 ++++++++++++++++++ pyproject.toml | 3 ++ test_optional_deps/_optional_deps_helpers.py | 46 +++++++++++++++++++ test_optional_deps/test_installed_with_geo.py | 31 +++++++++++++ .../test_installed_with_no_opts.py | 32 +++++++++++++ test_optional_deps/test_installed_with_viz.py | 31 +++++++++++++ .../test_installed_with_viz_and_geo.py | 29 ++++++++++++ 7 files changed, 216 insertions(+) create mode 100644 test_optional_deps/_optional_deps_helpers.py create mode 100644 test_optional_deps/test_installed_with_geo.py create mode 100644 test_optional_deps/test_installed_with_no_opts.py create mode 100644 test_optional_deps/test_installed_with_viz.py create mode 100644 test_optional_deps/test_installed_with_viz_and_geo.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e08df536e..b1d27c684 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,3 +119,47 @@ jobs: cd docs echo 'nb_execution_mode = "off"' >> conf.py make linkcheck + + test-optional-deps: + # Ensures can install with various combinations of optional dependencies, + # and that some corresponding tests pass or crash appropriately. + # Just a single machine and single Python version should be good enough, + # the goal here is to spot-check that optional deps work as expected, + # not to run an exhaustive set of tests with each combination of deps. + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v7 + + - name: setup-python + uses: actions/setup-python@v7 + with: + python-version: "3.13" + + - name: no optional deps + run: | + python -m venv "$RUNNER_TEMP/venv-none" + source "$RUNNER_TEMP/venv-none/bin/activate" + python -m pip install "." pytest + python -m pytest test_optional_deps/test_installed_with_no_opts.py + + - name: geo only + run: | + python -m venv "$RUNNER_TEMP/venv-geo" + source "$RUNNER_TEMP/venv-geo/bin/activate" + python -m pip install ".[geo]" pytest + python -m pytest test_optional_deps/test_installed_with_geo.py + + - name: viz only + run: | + python -m venv "$RUNNER_TEMP/venv-viz" + source "$RUNNER_TEMP/venv-viz/bin/activate" + python -m pip install ".[viz]" pytest + python -m pytest test_optional_deps/test_installed_with_viz.py + + - name: viz and geo + run: | + python -m venv "$RUNNER_TEMP/venv-viz_and_geo" + source "$RUNNER_TEMP/venv-viz_and_geo/bin/activate" + python -m pip install ".[viz,geo]" pytest + python -m pytest test_optional_deps/test_installed_with_viz_and_geo.py diff --git a/pyproject.toml b/pyproject.toml index e143f4be1..f1ce5d7e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,3 +96,6 @@ known-first-party = ["uxarray"] [tool.ruff.format] docstring-code-format = true + +[tool.pytest.ini_options] +testpaths = ["tests"] # (intentionally excludes test_optional_deps) diff --git a/test_optional_deps/_optional_deps_helpers.py b/test_optional_deps/_optional_deps_helpers.py new file mode 100644 index 000000000..04d7864cc --- /dev/null +++ b/test_optional_deps/_optional_deps_helpers.py @@ -0,0 +1,46 @@ +""" +File Purpose: defines helper functions to be used for testing optional dependencies. + +The goal here is to spot-check that optional deps work as expected, +not to run an exhaustive set of tests with each combination of deps. +""" + +def check_requires_no_opts(): + """run some checks which should not require any optional dependencies""" + import uxarray as ux + uxds = ux.tutorial.open_dataset('quad-hexagon') + uxds.compute() + +def check_requires_only_viz(): + """run some checks which should require viz optional dependencies, + but not any other optional dependencies. + """ + import uxarray as ux + uxds = ux.tutorial.open_dataset('quad-hexagon') + plot_obj = uxds.plot.points() # points() doesn't need geo projection details. + + # actually try to render the plot, too: + import holoviews as hv + renderer = hv.renderer('matplotlib') + renderer.get_plot(plot_obj) + +def check_requires_only_geo(): + """run some checks which should require geo optional dependencies, + but not any other optional dependencies. + """ + import uxarray as ux + arr = ux.tutorial.open_dataset('quad-hexagon')['t2m'] + arr.to_geodataframe() + +def check_requires_viz_and_geo(): + """run some checks which should require both viz and geo optional dependencies, + but not any other optional dependencies. + """ + import uxarray as ux + arr = ux.tutorial.open_dataset('quad-hexagon')['t2m'] + plot_obj = arr.plot.polygons() # polygons() uses geo projection details. + + # actually try to render the plot, too: + import holoviews as hv + renderer = hv.renderer('matplotlib') + renderer.get_plot(plot_obj) diff --git a/test_optional_deps/test_installed_with_geo.py b/test_optional_deps/test_installed_with_geo.py new file mode 100644 index 000000000..2550200fd --- /dev/null +++ b/test_optional_deps/test_installed_with_geo.py @@ -0,0 +1,31 @@ +""" +Purpose: test expected behaviors when installed with only geo optional dependency. +Tests should all pass if and only if installed accordingly, i.e. something like: + pip install ".[geo]" +""" +import pytest + +from _optional_deps_helpers import ( + check_requires_no_opts, + check_requires_only_viz, + check_requires_only_geo, + check_requires_viz_and_geo, +) + +def test_check_requires_no_opts(): + """ensure success for checks which should not require any optional dependencies""" + check_requires_no_opts() + +def test_check_requires_only_viz(): + """ensure failure for checks which should require viz optional dependencies""" + with pytest.raises(ImportError): + check_requires_only_viz() + +def test_check_requires_only_geo(): + """ensure success for checks which should require geo optional dependencies""" + check_requires_only_geo() + +def test_check_requires_viz_and_geo(): + """ensure failure for checks which should require both viz and geo optional dependencies""" + with pytest.raises(ImportError): + check_requires_viz_and_geo() diff --git a/test_optional_deps/test_installed_with_no_opts.py b/test_optional_deps/test_installed_with_no_opts.py new file mode 100644 index 000000000..b1a9bc446 --- /dev/null +++ b/test_optional_deps/test_installed_with_no_opts.py @@ -0,0 +1,32 @@ +""" +Purpose: test expected behaviors when installed without any optional dependencies. +Tests should all pass if and only if installed accordingly, i.e. something like: + pip install "." +""" +import pytest + +from _optional_deps_helpers import ( + check_requires_no_opts, + check_requires_only_viz, + check_requires_only_geo, + check_requires_viz_and_geo, +) + +def test_check_requires_no_opts(): + """ensure success for checks which should not require any optional dependencies""" + check_requires_no_opts() + +def test_check_requires_only_viz(): + """ensure failure for checks which should require viz optional dependencies""" + with pytest.raises(ImportError): + check_requires_only_viz() + +def test_check_requires_only_geo(): + """ensure failure for checks which should require geo optional dependencies""" + with pytest.raises(ImportError): + check_requires_only_geo() + +def test_check_requires_viz_and_geo(): + """ensure failure for checks which should require both viz and geo optional dependencies""" + with pytest.raises(ImportError): + check_requires_viz_and_geo() diff --git a/test_optional_deps/test_installed_with_viz.py b/test_optional_deps/test_installed_with_viz.py new file mode 100644 index 000000000..8bcc12152 --- /dev/null +++ b/test_optional_deps/test_installed_with_viz.py @@ -0,0 +1,31 @@ +""" +Purpose: test expected behaviors when installed with only viz optional dependency. +Tests should all pass if and only if installed accordingly, i.e. something like: + pip install ".[viz]" +""" +import pytest + +from _optional_deps_helpers import ( + check_requires_no_opts, + check_requires_only_viz, + check_requires_only_geo, + check_requires_viz_and_geo, +) + +def test_check_requires_no_opts(): + """ensure success for checks which should not require any optional dependencies""" + check_requires_no_opts() + +def test_check_requires_only_viz(): + """ensure success for checks which should require viz optional dependencies""" + check_requires_only_viz() + +def test_check_requires_only_geo(): + """ensure failure for checks which should require geo optional dependencies""" + with pytest.raises(ImportError): + check_requires_only_geo() + +def test_check_requires_viz_and_geo(): + """ensure failure for checks which should require both viz and geo optional dependencies""" + with pytest.raises(ImportError): + check_requires_viz_and_geo() diff --git a/test_optional_deps/test_installed_with_viz_and_geo.py b/test_optional_deps/test_installed_with_viz_and_geo.py new file mode 100644 index 000000000..a910fdfc0 --- /dev/null +++ b/test_optional_deps/test_installed_with_viz_and_geo.py @@ -0,0 +1,29 @@ +""" +Purpose: test expected behaviors when installed with geo and viz optional dependencies. +Tests should all pass if and only if installed accordingly, i.e. something like: + pip install ".[geo,viz]" +""" +import pytest + +from _optional_deps_helpers import ( + check_requires_no_opts, + check_requires_only_viz, + check_requires_only_geo, + check_requires_viz_and_geo, +) + +def test_check_requires_no_opts(): + """ensure success for checks which should not require any optional dependencies""" + check_requires_no_opts() + +def test_check_requires_only_viz(): + """ensure success for checks which should require viz optional dependencies""" + check_requires_only_viz() + +def test_check_requires_only_geo(): + """ensure success for checks which should require geo optional dependencies""" + check_requires_only_geo() + +def test_check_requires_viz_and_geo(): + """ensure success for checks which should require both viz and geo optional dependencies""" + check_requires_viz_and_geo() From 9b3083c938d215b490eff71efae90d7fd4108576 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:57:38 -0400 Subject: [PATCH 2/5] forgot pre-commit ruff formatting --- test_optional_deps/_optional_deps_helpers.py | 22 ++++++++++++++----- test_optional_deps/test_installed_with_geo.py | 8 +++++-- .../test_installed_with_no_opts.py | 8 +++++-- test_optional_deps/test_installed_with_viz.py | 8 +++++-- .../test_installed_with_viz_and_geo.py | 8 +++++-- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/test_optional_deps/_optional_deps_helpers.py b/test_optional_deps/_optional_deps_helpers.py index 04d7864cc..a3a37e1a3 100644 --- a/test_optional_deps/_optional_deps_helpers.py +++ b/test_optional_deps/_optional_deps_helpers.py @@ -5,42 +5,52 @@ not to run an exhaustive set of tests with each combination of deps. """ + def check_requires_no_opts(): """run some checks which should not require any optional dependencies""" import uxarray as ux - uxds = ux.tutorial.open_dataset('quad-hexagon') + + uxds = ux.tutorial.open_dataset("quad-hexagon") uxds.compute() + def check_requires_only_viz(): """run some checks which should require viz optional dependencies, but not any other optional dependencies. """ import uxarray as ux - uxds = ux.tutorial.open_dataset('quad-hexagon') + + uxds = ux.tutorial.open_dataset("quad-hexagon") plot_obj = uxds.plot.points() # points() doesn't need geo projection details. # actually try to render the plot, too: import holoviews as hv - renderer = hv.renderer('matplotlib') + + renderer = hv.renderer("matplotlib") renderer.get_plot(plot_obj) + def check_requires_only_geo(): """run some checks which should require geo optional dependencies, but not any other optional dependencies. """ import uxarray as ux - arr = ux.tutorial.open_dataset('quad-hexagon')['t2m'] + + arr = ux.tutorial.open_dataset("quad-hexagon")["t2m"] arr.to_geodataframe() + def check_requires_viz_and_geo(): """run some checks which should require both viz and geo optional dependencies, but not any other optional dependencies. """ import uxarray as ux - arr = ux.tutorial.open_dataset('quad-hexagon')['t2m'] + + arr = ux.tutorial.open_dataset("quad-hexagon")["t2m"] plot_obj = arr.plot.polygons() # polygons() uses geo projection details. # actually try to render the plot, too: import holoviews as hv - renderer = hv.renderer('matplotlib') + + renderer = hv.renderer("matplotlib") renderer.get_plot(plot_obj) diff --git a/test_optional_deps/test_installed_with_geo.py b/test_optional_deps/test_installed_with_geo.py index 2550200fd..5895dbe26 100644 --- a/test_optional_deps/test_installed_with_geo.py +++ b/test_optional_deps/test_installed_with_geo.py @@ -3,28 +3,32 @@ Tests should all pass if and only if installed accordingly, i.e. something like: pip install ".[geo]" """ -import pytest +import pytest from _optional_deps_helpers import ( check_requires_no_opts, - check_requires_only_viz, check_requires_only_geo, + check_requires_only_viz, check_requires_viz_and_geo, ) + def test_check_requires_no_opts(): """ensure success for checks which should not require any optional dependencies""" check_requires_no_opts() + def test_check_requires_only_viz(): """ensure failure for checks which should require viz optional dependencies""" with pytest.raises(ImportError): check_requires_only_viz() + def test_check_requires_only_geo(): """ensure success for checks which should require geo optional dependencies""" check_requires_only_geo() + def test_check_requires_viz_and_geo(): """ensure failure for checks which should require both viz and geo optional dependencies""" with pytest.raises(ImportError): diff --git a/test_optional_deps/test_installed_with_no_opts.py b/test_optional_deps/test_installed_with_no_opts.py index b1a9bc446..4408078f6 100644 --- a/test_optional_deps/test_installed_with_no_opts.py +++ b/test_optional_deps/test_installed_with_no_opts.py @@ -3,29 +3,33 @@ Tests should all pass if and only if installed accordingly, i.e. something like: pip install "." """ -import pytest +import pytest from _optional_deps_helpers import ( check_requires_no_opts, - check_requires_only_viz, check_requires_only_geo, + check_requires_only_viz, check_requires_viz_and_geo, ) + def test_check_requires_no_opts(): """ensure success for checks which should not require any optional dependencies""" check_requires_no_opts() + def test_check_requires_only_viz(): """ensure failure for checks which should require viz optional dependencies""" with pytest.raises(ImportError): check_requires_only_viz() + def test_check_requires_only_geo(): """ensure failure for checks which should require geo optional dependencies""" with pytest.raises(ImportError): check_requires_only_geo() + def test_check_requires_viz_and_geo(): """ensure failure for checks which should require both viz and geo optional dependencies""" with pytest.raises(ImportError): diff --git a/test_optional_deps/test_installed_with_viz.py b/test_optional_deps/test_installed_with_viz.py index 8bcc12152..fd551169d 100644 --- a/test_optional_deps/test_installed_with_viz.py +++ b/test_optional_deps/test_installed_with_viz.py @@ -3,28 +3,32 @@ Tests should all pass if and only if installed accordingly, i.e. something like: pip install ".[viz]" """ -import pytest +import pytest from _optional_deps_helpers import ( check_requires_no_opts, - check_requires_only_viz, check_requires_only_geo, + check_requires_only_viz, check_requires_viz_and_geo, ) + def test_check_requires_no_opts(): """ensure success for checks which should not require any optional dependencies""" check_requires_no_opts() + def test_check_requires_only_viz(): """ensure success for checks which should require viz optional dependencies""" check_requires_only_viz() + def test_check_requires_only_geo(): """ensure failure for checks which should require geo optional dependencies""" with pytest.raises(ImportError): check_requires_only_geo() + def test_check_requires_viz_and_geo(): """ensure failure for checks which should require both viz and geo optional dependencies""" with pytest.raises(ImportError): diff --git a/test_optional_deps/test_installed_with_viz_and_geo.py b/test_optional_deps/test_installed_with_viz_and_geo.py index a910fdfc0..3ca2e9c63 100644 --- a/test_optional_deps/test_installed_with_viz_and_geo.py +++ b/test_optional_deps/test_installed_with_viz_and_geo.py @@ -3,27 +3,31 @@ Tests should all pass if and only if installed accordingly, i.e. something like: pip install ".[geo,viz]" """ -import pytest +import pytest from _optional_deps_helpers import ( check_requires_no_opts, - check_requires_only_viz, check_requires_only_geo, + check_requires_only_viz, check_requires_viz_and_geo, ) + def test_check_requires_no_opts(): """ensure success for checks which should not require any optional dependencies""" check_requires_no_opts() + def test_check_requires_only_viz(): """ensure success for checks which should require viz optional dependencies""" check_requires_only_viz() + def test_check_requires_only_geo(): """ensure success for checks which should require geo optional dependencies""" check_requires_only_geo() + def test_check_requires_viz_and_geo(): """ensure success for checks which should require both viz and geo optional dependencies""" check_requires_viz_and_geo() From 6c494abac8b6142a37c328d233e270a9432f2067 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:45:45 -0400 Subject: [PATCH 3/5] fix ruff complaint about unused import --- test_optional_deps/test_installed_with_viz_and_geo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_optional_deps/test_installed_with_viz_and_geo.py b/test_optional_deps/test_installed_with_viz_and_geo.py index 3ca2e9c63..b2cabc398 100644 --- a/test_optional_deps/test_installed_with_viz_and_geo.py +++ b/test_optional_deps/test_installed_with_viz_and_geo.py @@ -4,7 +4,6 @@ pip install ".[geo,viz]" """ -import pytest from _optional_deps_helpers import ( check_requires_no_opts, check_requires_only_geo, From b9d015b8ae9f8b1dd855d7ad2ae1cbc14d7b8b2c Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:34:49 -0400 Subject: [PATCH 4/5] add healpix-sensitive optional deps test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (maybe not necessary… but also trying to re-trigger CI jobs here, due to github actions downtime yesterday causing stalled jobs with no "rerun jobs" button available.) --- test_optional_deps/_optional_deps_helpers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_optional_deps/_optional_deps_helpers.py b/test_optional_deps/_optional_deps_helpers.py index a3a37e1a3..158b85a21 100644 --- a/test_optional_deps/_optional_deps_helpers.py +++ b/test_optional_deps/_optional_deps_helpers.py @@ -39,6 +39,8 @@ def check_requires_only_geo(): arr = ux.tutorial.open_dataset("quad-hexagon")["t2m"] arr.to_geodataframe() + ux.Grid.from_healpix(zoom=1) + def check_requires_viz_and_geo(): """run some checks which should require both viz and geo optional dependencies, From 17ac1c6276b0e1fe7d93c3db95b4c1c2184986e3 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:39:18 -0400 Subject: [PATCH 5/5] fix optional deps test: cannot plot UxDataset --- test_optional_deps/_optional_deps_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_optional_deps/_optional_deps_helpers.py b/test_optional_deps/_optional_deps_helpers.py index 158b85a21..186e2452b 100644 --- a/test_optional_deps/_optional_deps_helpers.py +++ b/test_optional_deps/_optional_deps_helpers.py @@ -20,8 +20,8 @@ def check_requires_only_viz(): """ import uxarray as ux - uxds = ux.tutorial.open_dataset("quad-hexagon") - plot_obj = uxds.plot.points() # points() doesn't need geo projection details. + arr = ux.tutorial.open_dataset("quad-hexagon")["t2m"] + plot_obj = arr.plot.points() # points() doesn't need geo projection details. # actually try to render the plot, too: import holoviews as hv