From 263a19c457e3756eefebdc9176ea502b8ab07aed Mon Sep 17 00:00:00 2001 From: Sam Evans Date: Wed, 22 Jul 2026 09:57:29 -0400 Subject: [PATCH] move imports for should-be-optional deps such imports are now inside functions instead of at top level. This will support making them optional later in pyproject.toml. This splits off of #1548 to reduce scope of that PR; see that PR for more details. --- test/io/test_structured.py | 3 +++ uxarray/core/dataarray.py | 7 +++++-- uxarray/cross_sections/sample.py | 3 ++- uxarray/grid/geometry.py | 2 -- uxarray/grid/grid.py | 8 ++++++-- uxarray/io/_healpix.py | 5 ++++- uxarray/plot/utils.py | 5 ++--- uxarray/remap/weights.py | 7 ++++++- 8 files changed, 28 insertions(+), 12 deletions(-) diff --git a/test/io/test_structured.py b/test/io/test_structured.py index b58b9818a..54e735e4b 100644 --- a/test/io/test_structured.py +++ b/test/io/test_structured.py @@ -2,6 +2,9 @@ import xarray as xr import pytest +# import pooch # not necessary to actually import, but tests would likely fail if this fails. +# (commented here for future reference, since pooch is included in uxarray[dev] +# dependencies, but not imported explicitly anywhere in uxarray.) @pytest.mark.parametrize("ds_name", ["air_temperature", "ersstv5"]) def test_read_structured_grid_from_ds(ds_name): diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index a2b55a796..177f888bb 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -5,10 +5,8 @@ from typing import TYPE_CHECKING, Any, Hashable, Literal, Mapping, Optional from warnings import warn -import cartopy.crs as ccrs import numpy as np import xarray as xr -from cartopy.mpl.geoaxes import GeoAxes from xarray.core import dtypes from xarray.core.options import OPTIONS from xarray.core.utils import UncachedAccessor @@ -37,6 +35,9 @@ from uxarray.subset import DataArraySubsetAccessor if TYPE_CHECKING: + import cartopy.crs as ccrs + from cartopy.mpl.geoaxes import GeoAxes + from uxarray.core.dataset import UxDataset @@ -462,6 +463,8 @@ def to_raster( >>> ax.imshow(raster, origin="lower", extent=ax.get_xlim() + ax.get_ylim()) """ + from cartopy.mpl.geoaxes import GeoAxes + from uxarray.constants import INT_DTYPE from uxarray.plot.matplotlib import ( _ensure_dimensions, diff --git a/uxarray/cross_sections/sample.py b/uxarray/cross_sections/sample.py index 2b34b4e44..881e5dca4 100644 --- a/uxarray/cross_sections/sample.py +++ b/uxarray/cross_sections/sample.py @@ -1,6 +1,5 @@ import numpy as np from numba import njit, prange -from pyproj import Geod @njit(parallel=True) @@ -17,6 +16,8 @@ def _fill_numba(flat_orig, face_idx, n_face, n_steps): def sample_geodesic( start: tuple[float, float], end: tuple[float, float], steps: int ) -> tuple[np.ndarray, np.ndarray]: + from pyproj import Geod + lon0, lat0 = start lon1, lat1 = end diff --git a/uxarray/grid/geometry.py b/uxarray/grid/geometry.py index 6d54ec971..f1843096f 100644 --- a/uxarray/grid/geometry.py +++ b/uxarray/grid/geometry.py @@ -397,8 +397,6 @@ def _build_corrected_polygon_shells(polygon_shells): _corrected_shells_to_original_faces : np.ndarray Original indices used to map the corrected polygon shells to their entries in face nodes """ - - # import optional dependencies import antimeridian from shapely import Polygon diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 5f61b4c50..4b9ad68dc 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import copy import os from html import escape -from typing import Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence from warnings import warn -import cartopy.crs as ccrs import numpy as np import xarray as xr from xarray.core.options import OPTIONS @@ -99,6 +100,9 @@ from uxarray.plot.accessor import GridPlotAccessor from uxarray.subset import GridSubsetAccessor +if TYPE_CHECKING: + import cartopy.crs as ccrs + class Grid: """Represents a two-dimensional unstructured grid encoded following the diff --git a/uxarray/io/_healpix.py b/uxarray/io/_healpix.py index 21fe87f7c..3e6bdfef7 100644 --- a/uxarray/io/_healpix.py +++ b/uxarray/io/_healpix.py @@ -1,6 +1,5 @@ from typing import Any -import healpix as hp import numpy as np import polars as pl import xarray as xr @@ -68,6 +67,8 @@ def pix2corner_ang( ---- This will be updated when https://github.com/ntessore/healpix/issues/66 is implemented. """ + import healpix as hp + if nest: fu = hp._chp.nest2ang_uv else: @@ -102,6 +103,8 @@ def _pixels_to_ugrid(zoom, nest): A dataset containing pixel longitude and latitude coordinates along with related attributes. """ + import healpix as hp + ds = xr.Dataset() nside = hp.order2nside(zoom) diff --git a/uxarray/plot/utils.py b/uxarray/plot/utils.py index e5c8d3f8b..17275fe25 100644 --- a/uxarray/plot/utils.py +++ b/uxarray/plot/utils.py @@ -1,6 +1,3 @@ -import holoviews as hv - - class HoloviewsBackend: """Compare and set the HoloViews plotting backend.""" @@ -9,6 +6,8 @@ def __init__(self): def assign(self, backend: str): """Assign a HoloViews backend, one of 'matplotlib', 'bokeh'.""" + import holoviews as hv + if backend not in ["bokeh", "matplotlib", None]: raise ValueError( f"Unsupported backend. Expected one of ['bokeh', 'matplotlib'], but received {backend}" diff --git a/uxarray/remap/weights.py b/uxarray/remap/weights.py index 9b0c233bf..4fdee8cdd 100644 --- a/uxarray/remap/weights.py +++ b/uxarray/remap/weights.py @@ -4,13 +4,16 @@ from dataclasses import dataclass from os import PathLike from pathlib import Path +from typing import TYPE_CHECKING import numpy as np import xarray as xr -from scipy import sparse from uxarray.core.utils import _open_dataset_with_fallback +if TYPE_CHECKING: + from scipy import sparse + # LRU-bounded cache for loaded remap operators. _WEIGHTS_CACHE_MAXSIZE = 32 _WEIGHTS_CACHE: "OrderedDict[tuple[str, int, int], RemapWeights]" = OrderedDict() @@ -83,6 +86,8 @@ class RemapWeights: @classmethod def from_file(cls, filename_or_obj: str | PathLike[str] | xr.Dataset): """Load a standard sparse remap-weight file into memory once.""" + from scipy import sparse + if isinstance(filename_or_obj, xr.Dataset): ds = filename_or_obj close_ds = False