From c9ce1dd7450c0b93df69a4676769af7be793f269 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 3 Mar 2026 14:30:15 +1100 Subject: [PATCH 1/4] Defer most ShocSimple coordinate detection to CFGrid2D The lat/lon coordinate detection was a subset of the CFGrid2D detection logic, but was less good. Fixes #217 --- src/emsarray/conventions/shoc.py | 49 +++++++++++--------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/emsarray/conventions/shoc.py b/src/emsarray/conventions/shoc.py index 3c52a426..a5052fd6 100644 --- a/src/emsarray/conventions/shoc.py +++ b/src/emsarray/conventions/shoc.py @@ -24,7 +24,7 @@ from ._base import Specificity from .arakawa_c import ArakawaC, ArakawaCGridKind -from .grid import CFGrid2D, CFGrid2DTopology +from .grid import CFGrid2D logger = logging.getLogger(__name__) @@ -87,25 +87,6 @@ class ShocSimple(CFGrid2D): """ _dimensions: tuple[Hashable, Hashable] = ('j', 'i') - @cached_property - def topology(self) -> CFGrid2DTopology: - y_dimension, x_dimension = self._dimensions - try: - latitude = next( - name for name, variable in self.dataset.variables.items() - if variable.dims == self._dimensions - and variable.attrs.get("standard_name", None) == "latitude" - ) - longitude = next( - name for name, variable in self.dataset.variables.items() - if variable.dims == self._dimensions - and variable.attrs.get("standard_name", None) == "longitude" - ) - except StopIteration: - raise ValueError("Could not find the necessary coordinate variables") - - return CFGrid2DTopology(self.dataset, latitude=latitude, longitude=longitude) - @classmethod def check_dataset(cls, dataset: xarray.Dataset) -> int | None: if 'ems_version' not in dataset.attrs: @@ -114,27 +95,29 @@ def check_dataset(cls, dataset: xarray.Dataset) -> int | None: return None return Specificity.HIGH - @cached_property - def depth_coordinate(self) -> xarray.DataArray: - name = 'zc' - try: - return self.dataset[name] - except KeyError: - raise NoSuchCoordinateError( - f"SHOC dataset did not have expected depth coordinate {name!r}") - @cached_property def depth_coordinates(self) -> tuple[xarray.DataArray, ...]: + # SHOC depth coordinates have fixed names. + # Prioritise those, even if they don't have the correct attributes. names = ['zc', 'zcsed'] - return tuple( + shoc_depth_coordinates = tuple( self.dataset[name] for name in names if name in self.dataset.variables) + other_depth_coordinates = tuple( + coordinate for coordinate in super().depth_coordinates + if coordinate.name not in names) + return shoc_depth_coordinates + other_depth_coordinates @cached_property def time_coordinate(self) -> xarray.DataArray: - name = 'time' try: - return self.dataset[name] - except KeyError: + # Try detecting a time coordinate + return super().time_coordinate + except NoSuchCoordinateError: + # Fall back to a hard coded 'time' name in case this dataset + # doesn't have the right metadata + name = 'time' + if name in self.dataset: + return self.dataset[name] raise NoSuchCoordinateError( f"SHOC dataset did not have expected time coordinate {name!r}") From 4388065b74b4462f28833d40b3d4d8c43b32b59e Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 3 Mar 2026 15:08:25 +1100 Subject: [PATCH 2/4] Bump pinned dependencies --- continuous-integration/requirements-3.12.txt | 70 +++++++++---------- continuous-integration/requirements-3.13.txt | 70 +++++++++---------- continuous-integration/requirements-3.14.txt | 70 +++++++++---------- .../requirements-minimum.txt | 46 ++++++------ 4 files changed, 119 insertions(+), 137 deletions(-) diff --git a/continuous-integration/requirements-3.12.txt b/continuous-integration/requirements-3.12.txt index e5ad7c9e..6642357f 100644 --- a/continuous-integration/requirements-3.12.txt +++ b/continuous-integration/requirements-3.12.txt @@ -4,7 +4,7 @@ # # pip-compile --extra=testing --output-file=./continuous-integration/requirements-3.12.txt --unsafe-package=emsarray pyproject.toml # -bokeh==3.8.1 +bokeh==3.8.2 # via dask bottleneck==1.6.0 # via @@ -12,7 +12,7 @@ bottleneck==1.6.0 # emsarray (pyproject.toml) cartopy==0.25.0 # via emsarray -certifi==2025.11.12 +certifi==2026.2.25 # via # netcdf4 # pyproj @@ -25,7 +25,7 @@ cfunits==3.3.7 # via emsarray charset-normalizer==3.4.4 # via requests -click==8.3.0 +click==8.3.1 # via # dask # distributed @@ -37,23 +37,23 @@ contourpy==1.3.3 # via # bokeh # matplotlib -coverage[toml]==7.11.3 +coverage[toml]==7.13.4 # via pytest-cov cycler==0.12.1 # via matplotlib -dask[array,complete,dataframe,diagnostics,distributed]==2025.11.0 +dask[array,complete,dataframe,diagnostics,distributed]==2026.1.2 # via # distributed # xarray -distributed==2025.11.0 +distributed==2026.1.2 # via dask flake8==7.3.0 # via emsarray (pyproject.toml) -fonttools==4.60.1 +fonttools==4.61.1 # via matplotlib freezegun==1.5.5 # via emsarray (pyproject.toml) -fsspec==2025.10.0 +fsspec==2026.2.0 # via dask geojson==3.2.0 # via @@ -63,7 +63,7 @@ idna==3.11 # via requests iniconfig==2.3.0 # via pytest -isort==7.0.0 +isort==8.0.1 # via emsarray (pyproject.toml) jinja2==3.1.6 # via @@ -73,6 +73,8 @@ jinja2==3.1.6 # pytest-mpl kiwisolver==1.4.9 # via matplotlib +librt==0.8.1 + # via mypy locket==1.0.0 # via # distributed @@ -81,7 +83,7 @@ lz4==4.4.5 # via dask markupsafe==3.0.3 # via jinja2 -matplotlib==3.10.7 +matplotlib==3.10.8 # via # cartopy # emsarray @@ -90,17 +92,17 @@ mccabe==0.7.0 # via flake8 msgpack==1.1.2 # via distributed -mypy==1.18.2 +mypy==1.19.1 # via emsarray (pyproject.toml) mypy-extensions==1.1.0 # via mypy -narwhals==2.11.0 +narwhals==2.17.0 # via bokeh -netcdf4==1.7.3 +netcdf4==1.7.4 # via # emsarray # emsarray (pyproject.toml) -numpy==2.3.4 +numpy==2.4.2 # via # bokeh # bottleneck @@ -118,7 +120,7 @@ numpy==2.3.4 # pykdtree # shapely # xarray -packaging==25.0 +packaging==26.0 # via # bokeh # cartopy @@ -132,33 +134,33 @@ packaging==25.0 # pytest # pytest-mpl # xarray -pandas==2.3.3 +pandas==3.0.1 # via # bokeh # dask # xarray -pandas-stubs==2.3.2.250926 +pandas-stubs==3.0.0.260204 # via emsarray (pyproject.toml) partd==1.4.2 # via dask -pathspec==0.12.1 +pathspec==1.0.4 # via mypy -pillow==12.0.0 +pillow==12.1.1 # via # bokeh # matplotlib # pytest-mpl -platformdirs==4.5.0 +platformdirs==4.9.2 # via pooch pluggy==1.6.0 # via # pytest # pytest-cov -pooch==1.8.2 +pooch==1.9.0 # via emsarray -psutil==7.1.3 +psutil==7.2.2 # via distributed -pyarrow==22.0.0 +pyarrow==23.0.1 # via dask pycodestyle==2.14.0 # via flake8 @@ -168,16 +170,16 @@ pygments==2.19.2 # via pytest pykdtree==1.4.3 # via emsarray -pyparsing==3.2.5 +pyparsing==3.3.2 # via matplotlib pyproj==3.7.2 # via cartopy -pyshp==3.0.2.post1 +pyshp==3.0.3 # via # cartopy # emsarray # emsarray (pyproject.toml) -pytest==9.0.1 +pytest==9.0.2 # via # emsarray (pyproject.toml) # pytest-cov @@ -191,8 +193,6 @@ python-dateutil==2.9.0.post0 # freezegun # matplotlib # pandas -pytz==2025.2 - # via pandas pyyaml==6.0.3 # via # bokeh @@ -216,27 +216,23 @@ toolz==1.1.0 # dask # distributed # partd -tornado==6.5.2 +tornado==6.5.4 # via # bokeh # distributed types-pytz==2025.2.0.20251108 - # via - # emsarray (pyproject.toml) - # pandas-stubs + # via emsarray (pyproject.toml) typing-extensions==4.15.0 # via mypy -tzdata==2025.2 - # via pandas -urllib3==2.5.0 +urllib3==2.6.3 # via # distributed # requests -xarray[parallel]==2025.10.1 +xarray[parallel]==2026.2.0 # via # emsarray # emsarray (pyproject.toml) -xyzservices==2025.10.0 +xyzservices==2025.11.0 # via bokeh zict==3.0.0 # via distributed diff --git a/continuous-integration/requirements-3.13.txt b/continuous-integration/requirements-3.13.txt index 587558e5..a29a4825 100644 --- a/continuous-integration/requirements-3.13.txt +++ b/continuous-integration/requirements-3.13.txt @@ -4,7 +4,7 @@ # # pip-compile --extra=testing --output-file=./continuous-integration/requirements-3.13.txt --unsafe-package=emsarray pyproject.toml # -bokeh==3.8.1 +bokeh==3.8.2 # via dask bottleneck==1.6.0 # via @@ -12,7 +12,7 @@ bottleneck==1.6.0 # emsarray (pyproject.toml) cartopy==0.25.0 # via emsarray -certifi==2025.11.12 +certifi==2026.2.25 # via # netcdf4 # pyproj @@ -25,7 +25,7 @@ cfunits==3.3.7 # via emsarray charset-normalizer==3.4.4 # via requests -click==8.3.0 +click==8.3.1 # via # dask # distributed @@ -37,23 +37,23 @@ contourpy==1.3.3 # via # bokeh # matplotlib -coverage[toml]==7.11.3 +coverage[toml]==7.13.4 # via pytest-cov cycler==0.12.1 # via matplotlib -dask[array,complete,dataframe,diagnostics,distributed]==2025.11.0 +dask[array,complete,dataframe,diagnostics,distributed]==2026.1.2 # via # distributed # xarray -distributed==2025.11.0 +distributed==2026.1.2 # via dask flake8==7.3.0 # via emsarray (pyproject.toml) -fonttools==4.60.1 +fonttools==4.61.1 # via matplotlib freezegun==1.5.5 # via emsarray (pyproject.toml) -fsspec==2025.10.0 +fsspec==2026.2.0 # via dask geojson==3.2.0 # via @@ -63,7 +63,7 @@ idna==3.11 # via requests iniconfig==2.3.0 # via pytest -isort==7.0.0 +isort==8.0.1 # via emsarray (pyproject.toml) jinja2==3.1.6 # via @@ -73,6 +73,8 @@ jinja2==3.1.6 # pytest-mpl kiwisolver==1.4.9 # via matplotlib +librt==0.8.1 + # via mypy locket==1.0.0 # via # distributed @@ -81,7 +83,7 @@ lz4==4.4.5 # via dask markupsafe==3.0.3 # via jinja2 -matplotlib==3.10.7 +matplotlib==3.10.8 # via # cartopy # emsarray @@ -90,17 +92,17 @@ mccabe==0.7.0 # via flake8 msgpack==1.1.2 # via distributed -mypy==1.18.2 +mypy==1.19.1 # via emsarray (pyproject.toml) mypy-extensions==1.1.0 # via mypy -narwhals==2.11.0 +narwhals==2.17.0 # via bokeh -netcdf4==1.7.3 +netcdf4==1.7.4 # via # emsarray # emsarray (pyproject.toml) -numpy==2.3.4 +numpy==2.4.2 # via # bokeh # bottleneck @@ -118,7 +120,7 @@ numpy==2.3.4 # pykdtree # shapely # xarray -packaging==25.0 +packaging==26.0 # via # bokeh # cartopy @@ -132,33 +134,33 @@ packaging==25.0 # pytest # pytest-mpl # xarray -pandas==2.3.3 +pandas==3.0.1 # via # bokeh # dask # xarray -pandas-stubs==2.3.2.250926 +pandas-stubs==3.0.0.260204 # via emsarray (pyproject.toml) partd==1.4.2 # via dask -pathspec==0.12.1 +pathspec==1.0.4 # via mypy -pillow==12.0.0 +pillow==12.1.1 # via # bokeh # matplotlib # pytest-mpl -platformdirs==4.5.0 +platformdirs==4.9.2 # via pooch pluggy==1.6.0 # via # pytest # pytest-cov -pooch==1.8.2 +pooch==1.9.0 # via emsarray -psutil==7.1.3 +psutil==7.2.2 # via distributed -pyarrow==22.0.0 +pyarrow==23.0.1 # via dask pycodestyle==2.14.0 # via flake8 @@ -168,16 +170,16 @@ pygments==2.19.2 # via pytest pykdtree==1.4.3 # via emsarray -pyparsing==3.2.5 +pyparsing==3.3.2 # via matplotlib pyproj==3.7.2 # via cartopy -pyshp==3.0.2.post1 +pyshp==3.0.3 # via # cartopy # emsarray # emsarray (pyproject.toml) -pytest==9.0.1 +pytest==9.0.2 # via # emsarray (pyproject.toml) # pytest-cov @@ -191,8 +193,6 @@ python-dateutil==2.9.0.post0 # freezegun # matplotlib # pandas -pytz==2025.2 - # via pandas pyyaml==6.0.3 # via # bokeh @@ -216,27 +216,23 @@ toolz==1.1.0 # dask # distributed # partd -tornado==6.5.2 +tornado==6.5.4 # via # bokeh # distributed types-pytz==2025.2.0.20251108 - # via - # emsarray (pyproject.toml) - # pandas-stubs + # via emsarray (pyproject.toml) typing-extensions==4.15.0 # via mypy -tzdata==2025.2 - # via pandas -urllib3==2.5.0 +urllib3==2.6.3 # via # distributed # requests -xarray[parallel]==2025.10.1 +xarray[parallel]==2026.2.0 # via # emsarray # emsarray (pyproject.toml) -xyzservices==2025.10.0 +xyzservices==2025.11.0 # via bokeh zict==3.0.0 # via distributed diff --git a/continuous-integration/requirements-3.14.txt b/continuous-integration/requirements-3.14.txt index f1ff285c..94832990 100644 --- a/continuous-integration/requirements-3.14.txt +++ b/continuous-integration/requirements-3.14.txt @@ -4,7 +4,7 @@ # # pip-compile --extra=testing --output-file=./continuous-integration/requirements-3.14.txt --unsafe-package=emsarray pyproject.toml # -bokeh==3.8.1 +bokeh==3.8.2 # via dask bottleneck==1.6.0 # via @@ -12,7 +12,7 @@ bottleneck==1.6.0 # emsarray (pyproject.toml) cartopy==0.25.0 # via emsarray -certifi==2025.11.12 +certifi==2026.2.25 # via # netcdf4 # pyproj @@ -25,7 +25,7 @@ cfunits==3.3.7 # via emsarray charset-normalizer==3.4.4 # via requests -click==8.3.0 +click==8.3.1 # via # dask # distributed @@ -37,23 +37,23 @@ contourpy==1.3.3 # via # bokeh # matplotlib -coverage[toml]==7.11.3 +coverage[toml]==7.13.4 # via pytest-cov cycler==0.12.1 # via matplotlib -dask[array,complete,dataframe,diagnostics,distributed]==2025.11.0 +dask[array,complete,dataframe,diagnostics,distributed]==2026.1.2 # via # distributed # xarray -distributed==2025.11.0 +distributed==2026.1.2 # via dask flake8==7.3.0 # via emsarray (pyproject.toml) -fonttools==4.60.1 +fonttools==4.61.1 # via matplotlib freezegun==1.5.5 # via emsarray (pyproject.toml) -fsspec==2025.10.0 +fsspec==2026.2.0 # via dask geojson==3.2.0 # via @@ -63,7 +63,7 @@ idna==3.11 # via requests iniconfig==2.3.0 # via pytest -isort==7.0.0 +isort==8.0.1 # via emsarray (pyproject.toml) jinja2==3.1.6 # via @@ -73,6 +73,8 @@ jinja2==3.1.6 # pytest-mpl kiwisolver==1.4.9 # via matplotlib +librt==0.8.1 + # via mypy locket==1.0.0 # via # distributed @@ -81,7 +83,7 @@ lz4==4.4.5 # via dask markupsafe==3.0.3 # via jinja2 -matplotlib==3.10.7 +matplotlib==3.10.8 # via # cartopy # emsarray @@ -90,17 +92,17 @@ mccabe==0.7.0 # via flake8 msgpack==1.1.2 # via distributed -mypy==1.18.2 +mypy==1.19.1 # via emsarray (pyproject.toml) mypy-extensions==1.1.0 # via mypy -narwhals==2.11.0 +narwhals==2.17.0 # via bokeh -netcdf4==1.7.3 +netcdf4==1.7.4 # via # emsarray # emsarray (pyproject.toml) -numpy==2.3.4 +numpy==2.4.2 # via # bokeh # bottleneck @@ -118,7 +120,7 @@ numpy==2.3.4 # pykdtree # shapely # xarray -packaging==25.0 +packaging==26.0 # via # bokeh # cartopy @@ -132,33 +134,33 @@ packaging==25.0 # pytest # pytest-mpl # xarray -pandas==2.3.3 +pandas==3.0.1 # via # bokeh # dask # xarray -pandas-stubs==2.3.2.250926 +pandas-stubs==3.0.0.260204 # via emsarray (pyproject.toml) partd==1.4.2 # via dask -pathspec==0.12.1 +pathspec==1.0.4 # via mypy -pillow==12.0.0 +pillow==12.1.1 # via # bokeh # matplotlib # pytest-mpl -platformdirs==4.5.0 +platformdirs==4.9.2 # via pooch pluggy==1.6.0 # via # pytest # pytest-cov -pooch==1.8.2 +pooch==1.9.0 # via emsarray -psutil==7.1.3 +psutil==7.2.2 # via distributed -pyarrow==22.0.0 +pyarrow==23.0.1 # via dask pycodestyle==2.14.0 # via flake8 @@ -168,16 +170,16 @@ pygments==2.19.2 # via pytest pykdtree==1.4.3 # via emsarray -pyparsing==3.2.5 +pyparsing==3.3.2 # via matplotlib pyproj==3.7.2 # via cartopy -pyshp==3.0.2.post1 +pyshp==3.0.3 # via # cartopy # emsarray # emsarray (pyproject.toml) -pytest==9.0.1 +pytest==9.0.2 # via # emsarray (pyproject.toml) # pytest-cov @@ -191,8 +193,6 @@ python-dateutil==2.9.0.post0 # freezegun # matplotlib # pandas -pytz==2025.2 - # via pandas pyyaml==6.0.3 # via # bokeh @@ -216,27 +216,23 @@ toolz==1.1.0 # dask # distributed # partd -tornado==6.5.2 +tornado==6.5.4 # via # bokeh # distributed types-pytz==2025.2.0.20251108 - # via - # emsarray (pyproject.toml) - # pandas-stubs + # via emsarray (pyproject.toml) typing-extensions==4.15.0 # via mypy -tzdata==2025.2 - # via pandas -urllib3==2.5.0 +urllib3==2.6.3 # via # distributed # requests -xarray[parallel]==2025.10.1 +xarray[parallel]==2026.2.0 # via # emsarray # emsarray (pyproject.toml) -xyzservices==2025.10.0 +xyzservices==2025.11.0 # via bokeh zict==3.0.0 # via distributed diff --git a/continuous-integration/requirements-minimum.txt b/continuous-integration/requirements-minimum.txt index 97b04cf1..fcf16690 100644 --- a/continuous-integration/requirements-minimum.txt +++ b/continuous-integration/requirements-minimum.txt @@ -34,20 +34,17 @@ contourpy~=1.3.3 # matplotlib cycler~=0.12.1 # via matplotlib -dask~=2024.12.1 +dask~=2025.2.0 # via - # dask-expr # distributed # xarray -dask-expr~=1.1.21 +distributed~=2025.2.0 # via dask -distributed~=2024.12.1 - # via dask -fonttools~=4.54.1 +fonttools~=4.56.0 # via matplotlib -fsspec~=2024.10.0 +fsspec~=2025.2.0 # via dask -geojson~=3.1.0 +geojson~=3.2.0 # via # emsarray # emsarray (pyproject.toml) @@ -64,23 +61,23 @@ locket~=1.0.0 # via # distributed # partd -lz4~=4.3.3 +lz4~=4.4.5 # via dask markupsafe~=3.0.3 # via jinja2 -matplotlib~=3.10.7 +matplotlib~=3.10.8 # via # cartopy # emsarray msgpack~=1.1.2 # via distributed -narwhals~=2.11.0 +narwhals~=1.28.0 # via bokeh -netcdf4~=1.7.3 +netcdf4~=1.7.4 # via # emsarray # emsarray (pyproject.toml) -numpy~=1.26.4 +numpy~=2.1.3 # via # bokeh # bottleneck @@ -113,11 +110,10 @@ pandas~=2.2.3 # via # bokeh # dask - # dask-expr # xarray partd~=1.4.2 # via dask -pillow~=11.0.0 +pillow~=11.1.0 # via # bokeh # matplotlib @@ -125,13 +121,11 @@ platformdirs~=4.3.8 # via pooch pooch~=1.8.2 # via emsarray -psutil~=6.1.1 +psutil~=7.0.0 # via distributed -pyarrow~=18.0.0 - # via - # dask - # dask-expr -pykdtree~=1.3.13 +pyarrow~=19.0.1 + # via dask +pykdtree~=1.4.3 # via emsarray pyparsing~=3.2.5 # via matplotlib @@ -158,7 +152,7 @@ shapely~=2.0.7 # cartopy # emsarray # emsarray (pyproject.toml) -six~=1.16.0 +six~=1.17.0 # via python-dateutil sortedcontainers~=2.4.0 # via distributed @@ -173,15 +167,15 @@ tornado~=6.4.2 # via # bokeh # distributed -urllib3~=2.2.3 +urllib3~=2.3.0 # via # distributed # requests -xarray~=2024.10.0 +xarray~=2025.1.2 # via # emsarray # emsarray (pyproject.toml) -xyzservices~=2024.9.0 +xyzservices~=2025.1.0 # via bokeh zict~=3.0.0 # via distributed @@ -191,6 +185,6 @@ zict~=3.0.0 # emsarray # pytz # tzdata -pytz tzdata +pytz certifi From ffc3d71a48a69f9d50bfa3a3b02267ac8b6e8e20 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 3 Mar 2026 15:09:18 +1100 Subject: [PATCH 3/4] Drop dependency on pytz It was used in emsarray.utils, but pulled in as a transitive dependency via pandas. Pandas dropped it as a requirement which broke things. Python now has sufficient timezone capabilities included, pytz is not longer required by emsarray. --- docs/releases/development.rst | 6 ++++++ src/emsarray/utils.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/releases/development.rst b/docs/releases/development.rst index dbef7446..5d509723 100644 --- a/docs/releases/development.rst +++ b/docs/releases/development.rst @@ -13,3 +13,9 @@ Next release (in development) (:pr:`213`, :pr:`215`). * Add :attr:`.Grid.centroid_coordinates` attribute (:pr:`214`). +* Stop using pytz. + The Python datetime module now has sufficient functionality, + the external dependency is no longer required. + pytz was included as a dependency of pandas, + and pandas recently dropped pytz which broke things. + (:pr:`219`). diff --git a/src/emsarray/utils.py b/src/emsarray/utils.py index 17aa2f84..e279c563 100644 --- a/src/emsarray/utils.py +++ b/src/emsarray/utils.py @@ -23,7 +23,6 @@ import cftime import netCDF4 import numpy -import pytz import shapely import xarray from xarray.core.dtypes import maybe_promote @@ -174,12 +173,12 @@ def format_time_units_for_ems(units: str, calendar: str | None = DEFAULT_CALENDA period, date_string = cftime._datesplit(units) time_bits = cftime._parse_date(date_string.strip()) offset_total = time_bits[-1] - tzinfo = pytz.FixedOffset(offset_total) + tzinfo = datetime.timezone(datetime.timedelta(minutes=offset_total)) reference_datetime = cftime.num2pydate(0, units, calendar) # datetimes come out of num2pydate naive and in UTC # This will put them in the correct timezone - offset_datetime = reference_datetime.replace(tzinfo=pytz.UTC).astimezone(tzinfo) + offset_datetime = reference_datetime.replace(tzinfo=datetime.UTC).astimezone(tzinfo) offset_hours, offset_minutes = divmod(int(time_bits[-1]), 60) offset_string = f'{offset_hours:+d}:{offset_minutes:02d}' From 346fa1e27b42ff323d14dbde4006d45e91bad127 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 3 Mar 2026 15:33:03 +1100 Subject: [PATCH 4/4] Fix some mypy issues after version updates --- src/emsarray/conventions/ugrid.py | 2 +- src/emsarray/operations/triangulate.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emsarray/conventions/ugrid.py b/src/emsarray/conventions/ugrid.py index ddc4f436..482b2ef7 100644 --- a/src/emsarray/conventions/ugrid.py +++ b/src/emsarray/conventions/ugrid.py @@ -259,7 +259,7 @@ def update_connectivity( values = numpy.ma.masked_equal(raw_values, fill_value) if connectivity.dims[1] == primary_dimension: - values = numpy.transpose(values) + values = values.transpose() elif primary_dimension not in connectivity.dims: raise ValueError("Connectivity variable does not contain primary dimension") diff --git a/src/emsarray/operations/triangulate.py b/src/emsarray/operations/triangulate.py index 3ba48c75..04661e32 100644 --- a/src/emsarray/operations/triangulate.py +++ b/src/emsarray/operations/triangulate.py @@ -248,7 +248,7 @@ def _add_triangles(polygon_index: int, vertex_triangles: numpy.ndarray) -> None: convex_hulls = shapely.convex_hull(polygons) convex_hull_length = shapely.get_num_coordinates(convex_hulls) convex_hull_length[convex_hull_length != 0] -= 1 - polygon_is_concave = numpy.flatnonzero(convex_hull_length != polygon_length) + polygon_is_concave = numpy.flatnonzero(convex_hull_length != polygon_length).tolist() # Categorize each polygon by length, skipping concave polygons. # We will handle them separately. @@ -266,7 +266,7 @@ def _add_triangles(polygon_index: int, vertex_triangles: numpy.ndarray) -> None: # Because these triangles are convex and because we only care about the # vertex indexes, we can maniuplate the vertex indexes to make a fan # triangulation easily. - same_length_polygon_indexes = numpy.flatnonzero(polygon_length == unique_length) + same_length_polygon_indexes = numpy.flatnonzero(polygon_length == unique_length).tolist() if unique_length == 3: for polygon_index in same_length_polygon_indexes: