Repository: SciTools/iris (python)

Issue:
XML cube output fails for numpy < 2.3
## 🐛 Bug Report
Changes made in #6518 result in cube.xml() failing for numpy versions < 2.3

## How To Reproduce
With HEAD of Iris main and numpy==2.2.6:

```python
from iris.cube import Cube
import numpy as np

print(np.__version__)

cube = Cube(data=np.ones(10), standard_name='air_pressure')
print(cube.xml())
```

Error message:
```
  File ".../python3.12/site-packages/numpy/_core/arrayprint.py", line 361, in get_printoptions
    opts['legacy'] = {
                     ^
KeyError: '2.2'
```

## Expected behaviour
Shouldn't error.
Looks like some defensive conditionals are needed for checking numpy version?

## Environment 
numpy==2.2.6


Pull request description:
Configure numpy printoptions legacy
## 🚀 Pull Request

### Description
<!-- Provide a clear description about your awesome pull request -->
<!-- Tell us all about your new feature, improvement, or bug fix -->

Follow-up to #6518

Closes #6542

---
[Consult Iris pull request check list]( https://scitools-iris.readthedocs.io/en/latest/developers_guide/contributing_pull_request_checklist.html)

---
Add any of the below labels to trigger actions on this PR:

- https://github.com/SciTools/iris/labels/benchmark_this


Patch:
diff --git a/lib/iris/cube.py b/lib/iris/cube.py
index d75b43b6c..d8006c1f2 100644
--- a/lib/iris/cube.py
+++ b/lib/iris/cube.py
@@ -28,6 +28,7 @@ from cf_units import Unit
 import dask.array as da
 import numpy as np
 import numpy.ma as ma
+from packaging.version import Version
 
 import iris._constraints
 from iris._data_manager import DataManager
@@ -60,6 +61,15 @@ import iris.warnings
 __all__ = ["Cube", "CubeAttrsDict", "CubeList"]
 
 
+# Determine the numpy printoptions legacy setting.
+_NP_VERSION = Version(np.__version__)
+_NP_LEGACY_VERSION = Version("2.2")
+NP_PRINTOPTIONS_LEGACY = (
+    str(_NP_LEGACY_VERSION)
+    if Version(f"{_NP_VERSION.major}.{_NP_VERSION.minor}") > _NP_LEGACY_VERSION
+    else False
+)
+
 # The XML namespace to use for CubeML documents
 XML_NAMESPACE_URI = "urn:x-iris:cubeml-0.2"
 
@@ -162,7 +172,7 @@ class CubeList(list):
 
     def xml(self, checksum=False, order=True, byteorder=True):
         """Return a string of the XML that this list of cubes represents."""
-        with np.printoptions(legacy="2.2"):
+        with np.printoptions(legacy=NP_PRINTOPTIONS_LEGACY):
             doc = Document()
             cubes_xml_element = doc.createElement("cubes")
             cubes_xml_element.setAttribute("xmlns", XML_NAMESPACE_URI)
@@ -3865,7 +3875,7 @@ class Cube(CFVariableMixin):
         byteorder: bool = True,
     ) -> str:
         """Return a fully valid CubeML string representation of the Cube."""
-        with np.printoptions(legacy="2.2"):
+        with np.printoptions(legacy=NP_PRINTOPTIONS_LEGACY):
             doc = Document()
 
             cube_xml_element = self._xml_element(


Test patch:
diff --git a/lib/iris/tests/test_pp_module.py b/lib/iris/tests/test_pp_module.py
index 5db8c7069..9b8babd86 100644
--- a/lib/iris/tests/test_pp_module.py
+++ b/lib/iris/tests/test_pp_module.py
@@ -12,6 +12,7 @@ import numpy as np
 from numpy.testing import assert_array_equal
 import pytest
 
+from iris.cube import NP_PRINTOPTIONS_LEGACY
 import iris.fileformats.pp as pp
 from iris.tests import _shared_utils
 
@@ -64,7 +65,7 @@ class IrisPPTest:
         for pp_field in pp_fields:
             pp_field.data
 
-        with np.printoptions(legacy="2.2"):
+        with np.printoptions(legacy=NP_PRINTOPTIONS_LEGACY):
             test_string = str(pp_fields)
         reference_path = _shared_utils.get_result_path(reference_filename)
         if os.path.isfile(reference_path):