From 580a76ee38185a55da1ad62bbafe11399bf9b7e9 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 12:54:11 +0100 Subject: [PATCH 1/7] Enable testing against latest cfpint. Revert tests scope. --- .github/workflows/ci-tests.yml | 7 +++++++ lib/iris/common/mixin.py | 15 +++++++++++++-- noxfile.py | 26 ++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 9088aef8f1..4c95c49d0d 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -96,6 +96,13 @@ jobs: env_name: ${{ env.ENV_NAME }} install_packages: "cartopy nox pip" +# - name: "install latest cfpint" +# run: | +# mkdir -p ~/extra_installs +# git clone https://github.com/SciTools/cfpint.git ~/extra_installs/cfpint +# mkdir -p ~/.local/lib/python${{ matrix.python-version }}/site-packages +# echo $(readlink -f ~/extra_installs/cfpint/src) >~/.local/lib/python${{ matrix.python-version }}/site-packages/cfpint.pth + - name: "conda info" run: | conda info diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index bcdc7f5f01..b6a41e484d 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -23,11 +23,22 @@ cf_units = None try: - import cfpint import pint +except ImportError: + pint = None + +try: + import cfpint except ImportError: cfpint = None +print("\n") +import sys + +print("sys.path .. ") +print("\n".join([f" {x}" for x in sys.path])) +print("pint=", pint) +print("cfpint=", cfpint) import numpy as np import iris.std_names @@ -521,7 +532,7 @@ def is_vertical(self): # FOR NOW: insist on pint units -_DEFAULT_UNITCLASS: type = CfpintUnit +_DEFAULT_UNITCLASS: type = CfUnit # And force pint too. # TODO: since we may have seen problems with doing this dynamically, this could affect diff --git a/noxfile.py b/noxfile.py index 4d733fa2d4..705446cb1e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,6 +6,7 @@ import hashlib import os +import pathlib from pathlib import Path import nox @@ -181,6 +182,31 @@ def tests(session: nox.sessions.Session): prepare_venv(session) session.install("--no-deps", "--editable", ".") session.env.update(ENV) + + # HACK EXTRAS: install pint; download cfpint + make it importable with a pth + session.conda_install("pint") + + def expth(pth): + return str(pathlib.Path(pth).expanduser().absolute()) + + session.run("mkdir", "-p", expth("~/extra_installs"), external=True) + session.run( + "git", + "clone", + "https://github.com/SciTools/cfpint.git", + expth("~/extra_installs/cfpint"), + external=True, + ) + session.run( + "mkdir", + "-p", + expth(f"~/.local/lib/python{session.python}/site-packages"), + external=True, + ) + path = expth(f"~/.local/lib/python{session.python}/site-packages/cfpint.pth") + with open(path, "w") as f_out: + f_out.write(expth("~/extra_installs/cfpint/src")) + run_args = [ "pytest", "-n", From da7a28b40c9ee08949719e8b0f007c8635aeec38 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 15:23:50 +0100 Subject: [PATCH 2/7] Re-enable None units assignment. --- lib/iris/common/mixin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index b6a41e484d..752cd06bb0 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -641,7 +641,9 @@ def units(self) -> cf_units.Unit | cfpint.Unit: @units.setter def units(self, unit: cf_units.Unit | cfpint.Unit | str | None) -> None: # unit = cf_units.as_unit(unit) - self._metadata_manager.units = default_units_class().from_unit(unit) + if unit is not None: + unit = default_units_class().from_unit(unit) + self._metadata_manager.units = unit @property def attributes(self) -> LimitedAttributeDict: From bbe34f60a86c52abee9e273be84aae46fa4b6100 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 15:34:16 +0100 Subject: [PATCH 3/7] Reinstate CfUnit.from_unit calls cf_units.as_unit first. --- lib/iris/common/mixin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index 752cd06bb0..111593ab85 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -190,6 +190,7 @@ class CfUnit(cf_units.Unit): @classmethod def from_unit(cls, unit: cf_units.Unit): """Cast a :class:`cf_units.Unit` to an :class:`Unit`.""" + unit = cf_units.as_unit(unit) if isinstance(unit, CfUnit): result = unit elif isinstance(unit, cf_units.Unit): From 739699086ba7cef1e9654edb1d94c8efdb1e4170 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 15:50:52 +0100 Subject: [PATCH 4/7] Ensure 'None' units become 'unknown's. --- lib/iris/common/mixin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index 111593ab85..692e4ec0d4 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -641,9 +641,7 @@ def units(self) -> cf_units.Unit | cfpint.Unit: @units.setter def units(self, unit: cf_units.Unit | cfpint.Unit | str | None) -> None: - # unit = cf_units.as_unit(unit) - if unit is not None: - unit = default_units_class().from_unit(unit) + unit = default_units_class().from_unit(unit) self._metadata_manager.units = unit @property From 1ca2fc7ed80c5c1d619336ff871315653060c78e Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 17:33:18 +0100 Subject: [PATCH 5/7] Make repr of CfUnit backwards-compatible. --- lib/iris/common/mixin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index 692e4ec0d4..5b3a34bff1 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -246,6 +246,12 @@ def _round(date): return result + def __repr__(self): + # Adjust repr to look like the parent class, to avoid many CML errors. + string = super().__repr__() + string = string.replace(self.__class__.__name__ + "(", "Unit(", 1) + return string + if cfpint is not None: From b1a21d3cf316dbde741852ca6eddbaf6e23eb1f0 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 17:36:22 +0100 Subject: [PATCH 6/7] Revert temporary/debug changes. --- .github/workflows/ci-tests.yml | 7 ------- lib/iris/common/mixin.py | 13 +------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 4c95c49d0d..9088aef8f1 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -96,13 +96,6 @@ jobs: env_name: ${{ env.ENV_NAME }} install_packages: "cartopy nox pip" -# - name: "install latest cfpint" -# run: | -# mkdir -p ~/extra_installs -# git clone https://github.com/SciTools/cfpint.git ~/extra_installs/cfpint -# mkdir -p ~/.local/lib/python${{ matrix.python-version }}/site-packages -# echo $(readlink -f ~/extra_installs/cfpint/src) >~/.local/lib/python${{ matrix.python-version }}/site-packages/cfpint.pth - - name: "conda info" run: | conda info diff --git a/lib/iris/common/mixin.py b/lib/iris/common/mixin.py index 5b3a34bff1..ce8e7a520c 100644 --- a/lib/iris/common/mixin.py +++ b/lib/iris/common/mixin.py @@ -22,23 +22,12 @@ except ImportError: cf_units = None -try: - import pint -except ImportError: - pint = None - try: import cfpint + import pint except ImportError: cfpint = None -print("\n") -import sys - -print("sys.path .. ") -print("\n".join([f" {x}" for x in sys.path])) -print("pint=", pint) -print("cfpint=", cfpint) import numpy as np import iris.std_names From 15d4282f1701cbcc660c17d4ebd394839657a515 Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Tue, 31 Mar 2026 17:53:00 +0100 Subject: [PATCH 7/7] Fix some random test failures. --- lib/iris/tests/unit/common/mixin/test_pintunits.py | 13 ++++++++++++- lib/iris/tests/unit/test_Future.py | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/iris/tests/unit/common/mixin/test_pintunits.py b/lib/iris/tests/unit/common/mixin/test_pintunits.py index 359c4c175e..c7cdaaed3a 100644 --- a/lib/iris/tests/unit/common/mixin/test_pintunits.py +++ b/lib/iris/tests/unit/common/mixin/test_pintunits.py @@ -1,3 +1,12 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Stopgap testing for pint units. + +So far, only a few specific things are tested. +""" + from datetime import datetime import cf_units @@ -29,7 +38,9 @@ def test_nounit_eq(): def test_calendar(): unit = CfpintUnit("days since 1970-01-01", calendar="360_day") - assert repr(unit) == "" + # NOTE: no <>, due to "backwards compatibility" for assert_CDL + # TODO: remove the CfpintUnit._REPR_NO_LTGT + assert repr(unit) == "Unit('days since 1970-01-01', calendar='360_day')" # TODO: should really add the calendar to the string format # I think this is a bit horrible, # .. but it is cf_units behaviour + currently required for correct netcdf saving diff --git a/lib/iris/tests/unit/test_Future.py b/lib/iris/tests/unit/test_Future.py index e9ce4af222..445e8bf40d 100644 --- a/lib/iris/tests/unit/test_Future.py +++ b/lib/iris/tests/unit/test_Future.py @@ -122,7 +122,8 @@ class Test__str_repr: def _check_content(self, future, text): assert text == ( "Future(datum_support=False, pandas_ndim=False, save_split_attrs=False, " - "date_microseconds=False, derived_bounds=False, lam_pole_offset=False)" + "date_microseconds=False, derived_bounds=False, lam_pole_offset=False, " + "use_cfpint_units=False)" ) # Also just check that all the property elements are included for propname in future.__dict__.keys():