diff --git a/.github/workflows/benchmarks_run.yml b/.github/workflows/benchmarks_run.yml
index a39c531a778..02b913c6f06 100644
--- a/.github/workflows/benchmarks_run.yml
+++ b/.github/workflows/benchmarks_run.yml
@@ -29,7 +29,7 @@ jobs:
env:
IRIS_TEST_DATA_LOC_PATH: benchmarks
IRIS_TEST_DATA_PATH: benchmarks/iris-test-data
- IRIS_TEST_DATA_VERSION: "2.19"
+ IRIS_TEST_DATA_VERSION: "2.21"
# Lets us manually bump the cache to rebuild
ENV_CACHE_BUILD: "0"
TEST_DATA_CACHE_BUILD: "2"
diff --git a/.github/workflows/ci-manifest.yml b/.github/workflows/ci-manifest.yml
index 391f944310a..65716338de2 100644
--- a/.github/workflows/ci-manifest.yml
+++ b/.github/workflows/ci-manifest.yml
@@ -23,4 +23,4 @@ concurrency:
jobs:
manifest:
name: "check-manifest"
- uses: scitools/workflows/.github/workflows/ci-manifest.yml@2023.09.1
+ uses: scitools/workflows/.github/workflows/ci-manifest.yml@2023.10.0
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index 5c48966ce83..8d84d4e1372 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -50,7 +50,7 @@ jobs:
session: "tests"
env:
- IRIS_TEST_DATA_VERSION: "2.19"
+ IRIS_TEST_DATA_VERSION: "2.21"
ENV_NAME: "ci-tests"
steps:
diff --git a/.github/workflows/refresh-lockfiles.yml b/.github/workflows/refresh-lockfiles.yml
index a3f0c7f05fe..d92b653f26f 100644
--- a/.github/workflows/refresh-lockfiles.yml
+++ b/.github/workflows/refresh-lockfiles.yml
@@ -14,5 +14,5 @@ on:
jobs:
refresh_lockfiles:
- uses: scitools/workflows/.github/workflows/refresh-lockfiles.yml@2023.09.1
+ uses: scitools/workflows/.github/workflows/refresh-lockfiles.yml@2023.10.0
secrets: inherit
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 67b0515e8bc..f3634103477 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -46,7 +46,7 @@ jobs:
If you still care about this issue, then please either:
* Re-open this issue, if you have sufficient permissions, or
- * Add a comment pinging `@SciTools/iris-devs` who will re-open on your behalf.
+ * Add a comment stating that this is still relevant and someone will re-open it on your behalf.
# Comment on the staled prs while closed.
close-pr-message: |
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 32c51d35f9f..db6237d9444 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,7 +13,7 @@ minimum_pre_commit_version: 1.21.0
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.4.0
+ rev: v4.5.0
hooks:
# Prevent giant files from being committed.
- id: check-added-large-files
@@ -29,14 +29,14 @@ repos:
- id: no-commit-to-branch
- repo: https://github.com/codespell-project/codespell
- rev: "v2.2.5"
+ rev: "v2.2.6"
hooks:
- id: codespell
types_or: [asciidoc, python, markdown, rst]
additional_dependencies: [tomli]
- repo: https://github.com/psf/black
- rev: 23.9.1
+ rev: 23.10.0
hooks:
- id: black
pass_filenames: false
diff --git a/benchmarks/benchmarks/cperf/__init__.py b/benchmarks/benchmarks/cperf/__init__.py
index fb311c44dce..814d29338ff 100644
--- a/benchmarks/benchmarks/cperf/__init__.py
+++ b/benchmarks/benchmarks/cperf/__init__.py
@@ -53,7 +53,7 @@ def setup(self, file_type, three_d, three_times):
if three_d:
create_kwargs["n_levels"] = 71
- # Will re-use a file if already present.
+ # Will reuse a file if already present.
file_path = make_cubesphere_testfile(**create_kwargs)
else:
diff --git a/benchmarks/benchmarks/experimental/ugrid/regions_combine.py b/benchmarks/benchmarks/experimental/ugrid/regions_combine.py
index 16044c663a3..5ecc90930be 100644
--- a/benchmarks/benchmarks/experimental/ugrid/regions_combine.py
+++ b/benchmarks/benchmarks/experimental/ugrid/regions_combine.py
@@ -23,7 +23,7 @@
from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD
from iris.experimental.ugrid.utils import recombine_submeshes
-from ... import TrackAddedMemoryAllocation
+from ... import TrackAddedMemoryAllocation, on_demand_benchmark
from ...generate_data.ugrid import make_cube_like_2d_cubesphere
@@ -200,6 +200,8 @@ class CombineRegionsComputeRealData(MixinCombineRegions):
def time_compute_data(self, n_cubesphere):
_ = self.recombined_cube.data
+ # Vulnerable to noise, so disabled by default.
+ @on_demand_benchmark
@TrackAddedMemoryAllocation.decorator
def track_addedmem_compute_data(self, n_cubesphere):
_ = self.recombined_cube.data
@@ -217,6 +219,8 @@ def time_save(self, n_cubesphere):
# Save to disk, which must compute data + stream it to file.
save(self.recombined_cube, "tmp.nc")
+ # Vulnerable to noise, so disabled by default.
+ @on_demand_benchmark
@TrackAddedMemoryAllocation.decorator
def track_addedmem_save(self, n_cubesphere):
save(self.recombined_cube, "tmp.nc")
@@ -245,6 +249,8 @@ def time_stream_file2file(self, n_cubesphere):
# Save to disk, which must compute data + stream it to file.
save(self.recombined_cube, "tmp.nc")
+ # Vulnerable to noise, so disabled by default.
+ @on_demand_benchmark
@TrackAddedMemoryAllocation.decorator
def track_addedmem_stream_file2file(self, n_cubesphere):
save(self.recombined_cube, "tmp.nc")
diff --git a/benchmarks/benchmarks/generate_data/stock.py b/benchmarks/benchmarks/generate_data/stock.py
index eaf46bb4056..954e791f435 100644
--- a/benchmarks/benchmarks/generate_data/stock.py
+++ b/benchmarks/benchmarks/generate_data/stock.py
@@ -39,7 +39,7 @@ def _external(func_name_, temp_file_dir, **kwargs_):
)
if not REUSE_DATA or not save_path.is_file():
# The xios functions take control of save location so need to move to
- # a more specific name that allows re-use.
+ # a more specific name that allows reuse.
actual_path = run_function_elsewhere(
_external,
func_name_=func_name,
diff --git a/benchmarks/benchmarks/load/__init__.py b/benchmarks/benchmarks/load/__init__.py
index 3b2a83b1b1d..3d15629f9e2 100644
--- a/benchmarks/benchmarks/load/__init__.py
+++ b/benchmarks/benchmarks/load/__init__.py
@@ -69,7 +69,7 @@ def time_realise(self, _, __, ___, ____) -> None:
class STASHConstraint:
- # xyz sizes mimic LoadAndRealise to maximise file re-use.
+ # xyz sizes mimic LoadAndRealise to maximise file reuse.
params = [[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], ["FF", "PP"]]
param_names = ["xyz", "file_format"]
diff --git a/benchmarks/benchmarks/save.py b/benchmarks/benchmarks/save.py
index d00c66a0ca9..e9a7918dcc4 100644
--- a/benchmarks/benchmarks/save.py
+++ b/benchmarks/benchmarks/save.py
@@ -16,7 +16,7 @@
from iris import save
from iris.experimental.ugrid import save_mesh
-from . import TrackAddedMemoryAllocation
+from . import TrackAddedMemoryAllocation, on_demand_benchmark
from .generate_data.ugrid import make_cube_like_2d_cubesphere
@@ -47,6 +47,8 @@ def time_netcdf_save_mesh(self, n_cubesphere, is_unstructured):
if is_unstructured:
self._save_mesh(self.cube)
+ # Vulnerable to noise, so disabled by default.
+ @on_demand_benchmark
@TrackAddedMemoryAllocation.decorator
def track_addedmem_netcdf_save(self, n_cubesphere, is_unstructured):
# Don't need to copy the cube here since track_ benchmarks don't
diff --git a/benchmarks/bm_runner.py b/benchmarks/bm_runner.py
index b0f98c04ac4..4b8f6e1f18e 100644
--- a/benchmarks/bm_runner.py
+++ b/benchmarks/bm_runner.py
@@ -82,7 +82,7 @@ def _prep_data_gen_env() -> None:
else:
echo("Setting up the data generation environment ...")
# Get Nox to build an environment for the `tests` session, but don't
- # run the session. Will re-use a cached environment if appropriate.
+ # run the session. Will reuse a cached environment if appropriate.
_subprocess_runner(
[
"nox",
diff --git a/docs/gallery_code/meteorology/plot_COP_1d.py b/docs/gallery_code/meteorology/plot_COP_1d.py
index bebbad4224a..2181b89b8cb 100644
--- a/docs/gallery_code/meteorology/plot_COP_1d.py
+++ b/docs/gallery_code/meteorology/plot_COP_1d.py
@@ -54,7 +54,7 @@ def main():
)
# Generate area-weights array. As e1 and a1b are on the same grid we can
- # do this just once and re-use. This method requires bounds on lat/lon
+ # do this just once and reuse. This method requires bounds on lat/lon
# coords, so let's add some in sensible locations using the "guess_bounds"
# method.
e1.coord("latitude").guess_bounds()
diff --git a/docs/gallery_code/meteorology/plot_COP_maps.py b/docs/gallery_code/meteorology/plot_COP_maps.py
index 5e158346a96..529018ec8ca 100644
--- a/docs/gallery_code/meteorology/plot_COP_maps.py
+++ b/docs/gallery_code/meteorology/plot_COP_maps.py
@@ -171,23 +171,13 @@ def main():
)
plt.gca().coastlines()
- # Now add a colourbar who's leftmost point is the same as the leftmost
- # point of the left hand plot and rightmost point is the rightmost
- # point of the right hand plot.
-
- # Get the positions of the 2nd plot and the left position of the 1st plot.
- left, bottom, width, height = ax_array[1].get_position().bounds
- first_plot_left = ax_array[0].get_position().bounds[0]
-
- # The width of the colorbar should now be simple.
- width = left - first_plot_left + width
-
- # Add axes to the figure, to place the colour bar.
- colorbar_axes = fig.add_axes([first_plot_left, 0.18, width, 0.03])
-
- # Add the colour bar.
+ # Now add a colour bar which spans the two plots. Here we pass Figure.axes
+ # which is a list of all (two) axes currently on the figure. Note that
+ # these are different to the contents of ax_array, because those were
+ # standard Matplotlib Axes that Iris automatically replaced with Cartopy
+ # GeoAxes.
cbar = plt.colorbar(
- contour_result, colorbar_axes, orientation="horizontal"
+ contour_result, ax=fig.axes, aspect=60, orientation="horizontal"
)
# Label the colour bar and add ticks.
diff --git a/docs/src/_templates/layout.html b/docs/src/_templates/layout.html
deleted file mode 100644
index 974bd127539..00000000000
--- a/docs/src/_templates/layout.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "pydata_sphinx_theme/layout.html" %}
-
-{# This uses blocks. See:
- https://www.sphinx-doc.org/en/master/templating.html
-#}
-
-
- {%- block docs_body %}
-
- {% if on_rtd and rtd_version == 'latest' %}
-
- You are viewing the latest unreleased documentation
- v{{ version }}. You can switch to a stable version
- via the flyout menu in the bottom corner of the screen.
-
-
- {%- endif %}
-
- {{ super() }}
-{%- endblock %}
diff --git a/docs/src/conf.py b/docs/src/conf.py
index 7f7322c1f85..17a0d621cb8 100644
--- a/docs/src/conf.py
+++ b/docs/src/conf.py
@@ -294,7 +294,9 @@ def _dotv(version):
"collapse_navigation": True,
"navigation_depth": 3,
"show_prev_next": True,
- "navbar_align": "left",
+ "navbar_align": "content",
+ # removes the search box from the top bar
+ "navbar_persistent": [],
# TODO: review if 6 links is too crowded.
"header_links_before_dropdown": 6,
"github_url": "https://github.com/SciTools/iris",
@@ -329,6 +331,16 @@ def _dotv(version):
},
}
+# if we are building via Read The Docs and it is the latest (not stable)
+if on_rtd and rtd_version == "latest":
+ html_theme_options[
+ "announcement"
+ ] = f"""
+ You are viewing the latest unreleased documentation
+ {version}. You can switch to a
+ stable
+ version."""
+
rev_parse = run(["git", "rev-parse", "--short", "HEAD"], capture_output=True)
commit_sha = rev_parse.stdout.decode().strip()
diff --git a/docs/src/developers_guide/contributing_documentation_easy.rst b/docs/src/developers_guide/contributing_documentation_easy.rst
index f54de628bfd..51554f9e19f 100755
--- a/docs/src/developers_guide/contributing_documentation_easy.rst
+++ b/docs/src/developers_guide/contributing_documentation_easy.rst
@@ -81,9 +81,9 @@ Describing what you've changed and why will help the person who reviews your cha
.. tip::
If you're not sure that you're making your pull request right, or have a
- question, then make it anyway! You can then comment on it tagging
- ``@SciTools/iris-devs`` to ask your question (then edit your pull request if
- you need to).
+ question, then make it anyway! You can then comment on it to ask your
+ question, then someone from the dev team will be happy to help you out (then
+ edit your pull request if you need to).
What Happens Next?
^^^^^^^^^^^^^^^^^^
diff --git a/docs/src/further_topics/filtering_warnings.rst b/docs/src/further_topics/filtering_warnings.rst
new file mode 100644
index 00000000000..2cbad525d30
--- /dev/null
+++ b/docs/src/further_topics/filtering_warnings.rst
@@ -0,0 +1,271 @@
+.. _filtering-warnings:
+
+==================
+Filtering Warnings
+==================
+
+Since Iris cannot predict your specific needs, it by default raises Warnings
+for anything that might be a problem for **any** user, and is designed to work with
+you to ``ignore`` Warnings which you do not find helpful.
+
+.. testsetup:: filtering_warnings
+
+ from pathlib import Path
+ import sys
+ import warnings
+
+ import iris
+ import iris.coord_systems
+ import iris.exceptions
+
+ # Hack to ensure doctests actually see Warnings that are raised, and that
+ # they have a relative path (so a test pass is not machine-dependent).
+ warnings.filterwarnings("default")
+ IRIS_FILE = Path(iris.__file__)
+ def custom_warn(message, category, filename, lineno, file=None, line=None):
+ filepath = Path(filename)
+ filename = str(filepath.relative_to(IRIS_FILE.parents[1]))
+ sys.stdout.write(warnings.formatwarning(message, category, filename, lineno))
+ warnings.showwarning = custom_warn
+
+ geog_cs_globe = iris.coord_systems.GeogCS(6400000)
+ orthographic_coord_system = iris.coord_systems.Orthographic(
+ longitude_of_projection_origin=0,
+ latitude_of_projection_origin=0,
+ ellipsoid=geog_cs_globe,
+ )
+
+
+ def my_operation():
+ geog_cs_globe.inverse_flattening = 0.1
+ _ = orthographic_coord_system.as_cartopy_crs()
+
+Here is a hypothetical operation - ``my_operation()`` - which raises two
+Warnings:
+
+.. doctest:: filtering_warnings
+
+ >>> my_operation()
+ ...
+ iris/coord_systems.py:456: IrisUserWarning: Setting inverse_flattening does not affect other properties of the GeogCS object. To change other properties set them explicitly or create a new GeogCS instance.
+ warnings.warn(wmsg, category=iris.exceptions.IrisUserWarning)
+ iris/coord_systems.py:823: IrisDefaultingWarning: Discarding false_easting and false_northing that are not used by Cartopy.
+ warnings.warn(
+
+Warnings can be suppressed using the Python warnings filter with the ``ignore``
+action. Detailed information is available in the Python documentation:
+:external+python:mod:`warnings`.
+
+The key points are:
+
+- :ref:`When`: a warnings filter can be applied
+ either from the command line or from within Python.
+- :ref:`What`: a warnings filter accepts
+ various arguments to specify which Warnings are being filtered. Both broad
+ and narrow filters are possible.
+
+.. _warning-filter-application:
+
+**When** a Warnings Filter can be Applied
+-----------------------------------------
+
+- **Command line:** setting the :external+python:envvar:`PYTHONWARNINGS`
+ environment variable.
+- **Command line:** the `python -W `_
+ command line argument.
+- **Within Python:** use :func:`warnings.filterwarnings` .
+
+The :ref:`warning-filter-specificity` section demonstrates using
+:func:`warnings.filterwarnings`, and shows the equivalent **command line**
+approaches.
+
+
+.. _warning-filter-specificity:
+
+**What** Warnings will be Filtered
+----------------------------------
+
+.. note::
+
+ For all of these examples we are using the
+ :class:`~warnings.catch_warnings` context manager to ensure any changes to
+ settings are temporary.
+
+ This should always work fine for the ``ignore``
+ warning filter action, but note that some of the other actions
+ may not behave correctly with all Iris operations, as
+ :class:`~warnings.catch_warnings` is not thread-safe (e.g. using the
+ ``once`` action may cause 1 warning per chunk of lazy data).
+
+Specific Warnings
+~~~~~~~~~~~~~~~~~
+
+**When you do not want a specific warning, but still want all others.**
+
+You can target specific Warning messages, e.g.
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings("ignore", message="Discarding false_easting")
+ ... my_operation()
+ ...
+ iris/coord_systems.py:456: IrisUserWarning: Setting inverse_flattening does not affect other properties of the GeogCS object. To change other properties set them explicitly or create a new GeogCS instance.
+ warnings.warn(wmsg, category=iris.exceptions.IrisUserWarning)
+
+::
+
+ python -W ignore:"Discarding false_easting"
+ export PYTHONWARNINGS=ignore:"Discarding false_easting"
+
+----
+
+Or you can target Warnings raised by specific lines of specific modules, e.g.
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings("ignore", module="iris.coord_systems", lineno=456)
+ ... my_operation()
+ ...
+ iris/coord_systems.py:823: IrisDefaultingWarning: Discarding false_easting and false_northing that are not used by Cartopy.
+ warnings.warn(
+
+::
+
+ python -W ignore:::iris.coord_systems:454
+ export PYTHONWARNINGS=ignore:::iris.coord_systems:454
+
+Warnings from a Common Source
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**When you do not want ANY warnings raised by a module, or collection of
+modules.**
+
+E.g. filtering the ``coord_systems`` module:
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings("ignore", module="iris.coord_systems")
+ ... my_operation()
+
+::
+
+ python -W ignore:::iris.coord_systems
+ export PYTHONWARNINGS=ignore:::iris.coord_systems
+
+----
+
+If using :func:`warnings.filterwarnings` , you can also use partial
+definitions. The below example will ``ignore`` all Warnings from ``iris`` as a
+whole.
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings("ignore", module="iris")
+ ... my_operation()
+
+The above 'partial' filter is not available with the command line approaches.
+
+Warnings of a Common Type
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**When you do not want any Warnings of the same nature, from anywhere in the
+code you are calling.**
+
+The below example will ``ignore`` any
+:class:`~iris.exceptions.IrisDefaultingWarning` that gets raised by *any*
+module during execution:
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings(
+ ... "ignore",
+ ... category=iris.exceptions.IrisDefaultingWarning
+ ... )
+ ... my_operation()
+ ...
+ iris/coord_systems.py:456: IrisUserWarning: Setting inverse_flattening does not affect other properties of the GeogCS object. To change other properties set them explicitly or create a new GeogCS instance.
+ warnings.warn(wmsg, category=iris.exceptions.IrisUserWarning)
+
+----
+
+Using :class:`~iris.exceptions.IrisUserWarning` in the filter will ``ignore``
+both Warnings, since :class:`~iris.exceptions.IrisDefaultingWarning` subclasses
+:class:`~iris.exceptions.IrisUserWarning` :
+
+.. doctest:: filtering_warnings
+
+ >>> with warnings.catch_warnings():
+ ... warnings.filterwarnings(
+ ... "ignore",
+ ... category=iris.exceptions.IrisUserWarning
+ ... )
+ ... my_operation()
+
+----
+
+The command line approaches can only handle the built-in Warning
+categories (`cpython#66733`_)::
+
+ python -W ignore::UserWarning
+ export PYTHONWARNINGS=ignore::UserWarning
+
+----
+
+There are several built-in Python warning categories that can be used here
+(:class:`DeprecationWarning` being a popular example, see
+:external+python:mod:`warnings` for more). Since Iris has
+so many different warnings that might be raised, Iris subclasses
+:class:`UserWarning` to :class:`~iris.exceptions.IrisUserWarning`, which itself
+has **many** specialised subclasses. These subclasses exist to give you more
+granularity in your warning filtering; you can see the full list by
+searching the :mod:`iris.exceptions` page for ``warning`` .
+
+.. attention::
+
+ If you have ideas for adding/altering Iris' warning categories, please
+ :ref:`get in touch`! The categories exist to
+ make your life easier, and it is simple to make modifications.
+
+
+More Detail
+-----------
+
+Different people use Iris for very different purposes, from quick file
+visualisation to extract-transform-load to statistical analysis. These
+contrasting priorities mean disagreement on which Iris problems can be ignored
+and which are critically important.
+
+For problems that prevent Iris functioning: **Concrete Exceptions** are raised, which
+stop code from running any further - no debate here. For less catastrophic
+problems: **Warnings** are raised,
+which notify you (in ``stderr``) but allow code to continue running. The Warnings are
+there because Iris may **OR may not** function in the way you expect,
+depending on what you need - e.g. a problem might prevent data being saved to
+NetCDF, but statistical analysis will still work fine.
+
+Examples of Iris Warnings
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- If you attempt to plot un-bounded point data as a ``pcolormesh``: Iris will
+ guess appropriate bounds around each point so that quadrilaterals can be
+ plotted. This permanently modifies the relevant coordinates, so the you are
+ warned in case downstream operations assume un-bounded coordinates.
+- If you load a NetCDF file where a CF variable references another variable -
+ e.g. ``my_var:coordinates = "depth_var" ;`` - but the referenced variable
+ (``depth_var``) is not in the file: Iris will still construct
+ its data model, but without this reference relationship. You are warned since
+ the file includes an error and the loaded result might therefore not be as
+ expected.
+
+
+.. testcleanup:: filtering_warnings
+
+ warnings.filterwarnings("ignore")
+
+
+.. _cpython#66733: https://github.com/python/cpython/issues/66733
diff --git a/docs/src/userguide/index.rst b/docs/src/userguide/index.rst
index 771aa450a3d..c87323da8e0 100644
--- a/docs/src/userguide/index.rst
+++ b/docs/src/userguide/index.rst
@@ -42,6 +42,7 @@ they may serve as a useful reference for future exploration.
:maxdepth: 2
:caption: Further Topics
+ ../further_topics/filtering_warnings
../further_topics/metadata
../further_topics/lenient_metadata
../further_topics/lenient_maths
diff --git a/docs/src/whatsnew/3.7.rst b/docs/src/whatsnew/3.7.rst
index d5ea21f3d74..f1c7fb5f2ca 100644
--- a/docs/src/whatsnew/3.7.rst
+++ b/docs/src/whatsnew/3.7.rst
@@ -1,7 +1,7 @@
.. include:: ../common_links.inc
-v3.7 (16 Aug 2023) [release candidate]
-**************************************
+v3.7 (16 Aug 2023)
+******************
This document explains the changes made to Iris for this release
(:doc:`View all changes `.)
@@ -46,7 +46,7 @@ This document explains the changes made to Iris for this release
#. `@rcomer`_ rewrote :func:`~iris.util.broadcast_to_shape` so it now handles
lazy data. (:pull:`5307`)
-
+
.. _concat_warnings:
#. `@acchamber`_ added error and warning messages about coordinate overlaps to
@@ -69,11 +69,11 @@ This document explains the changes made to Iris for this release
=============
#. `@acchamber`_ fixed a bug with :func:`~iris.util.unify_time_units` so it does not block
- concatenation through different data types in rare instances. (:pull:`5372`)
+ concatenation through different data types in rare instances. (:pull:`5372`)
#. `@acchamber`_ removed some obsolete code that prevented extraction of time points
from cubes with bounded times (:pull:`5175`)
-
+
.. _cftime_warnings:
#. `@rcomer`_ modified pp-loading to avoid a ``cftime`` warning for non-standard
diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst
index ed42ea5cf17..a17fb1ac2d7 100644
--- a/docs/src/whatsnew/latest.rst
+++ b/docs/src/whatsnew/latest.rst
@@ -30,7 +30,13 @@ This document explains the changes made to Iris for this release
✨ Features
===========
-#. N/A
+#. `@trexfeathers`_ and `@HGWright`_ (reviewer) sub-categorised all Iris'
+ :class:`UserWarning`\s for richer filtering. The full index of
+ sub-categories can be seen here: :mod:`iris.exceptions` . (:pull:`5498`)
+
+#. `@trexfeathers`_ added the :class:`~iris.coord_systems.ObliqueMercator`
+ and :class:`~iris.coord_systems.RotatedMercator` coordinate systems,
+ complete with NetCDF loading and saving. (:pull:`5548`)
🐛 Bugs Fixed
@@ -64,19 +70,24 @@ This document explains the changes made to Iris for this release
🔗 Dependencies
===============
-#. N/A
+#. `@bjlittle`_ enforced the minimum pin of ``numpy>1.21`` in accordance with the `NEP29 Drop Schedule`_.
+ (:pull:`5525`)
📚 Documentation
================
+#. `@trexfeathers`_ documented the intended use of warnings filtering with
+ Iris. See :ref:`filtering-warnings`. (:pull:`5509`)
+
+#. `@rcomer`_ updated the
+ :ref:`sphx_glr_generated_gallery_meteorology_plot_COP_maps.py` to show how
+ a colourbar may steal space from multiple axes. (:pull:`5537`)
+
#. `@tkknight`_ improved the top navgation bar alignment and amount of
links shown. Also improved how the warning banner is implemented.
(:pull:`5505` and :pull:`5508`)
-#. `@tkknight`_ improved the top navgation bar alignment and amount of
- links shown. Also improved how the warning banner is implemented.
- (https://github.com/SciTools/iris/pull/5505 and https://github.com/SciTools/iris/pull/5508)
💼 Internal
===========
@@ -87,12 +98,24 @@ This document explains the changes made to Iris for this release
working properly. (Main pull request: :pull:`5437`, more detail:
:pull:`5430`, :pull:`5431`, :pull:`5432`, :pull:`5434`, :pull:`5436`)
+#. `@trexfeathers`_ set a number of memory benchmarks to be on-demand, as they
+ were vulnerable to false positives in CI runs. (:pull:`5481`)
+
+#. `@acchamber`_ and `@ESadek-MO`_ resolved several deprecation to reduce
+ number of warnings raised during tests.
+ (:pull:`5493`, :pull:`5511`)
+
#. `@trexfeathers`_ replaced all uses of the ``logging.WARNING`` level, in
favour of using Python warnings, following team agreement. (:pull:`5488`)
#. `@trexfeathers`_ adapted benchmarking to work with ASV ``>=v0.6`` by no
longer using the ``--strict`` argument. (:pull:`5496`)
+#. `@fazledyn-or`_ replaced ``NotImplementedError`` with ``NotImplemented`` as
+ a proper method call. (:pull:`5544`)
+
+#. `@bjlittle`_ corrected various comment spelling mistakes detected by
+ `codespell`_. (:pull:`5546`)
.. comment
@@ -100,6 +123,13 @@ This document explains the changes made to Iris for this release
core dev names are automatically included by the common_links.inc:
.. _@scottrobinson02: https://github.com/scottrobinson02
+.. _@acchamber: https://github.com/acchamber
+.. _@fazledyn-or: https://github.com/fazledyn-or
+
.. comment
Whatsnew resources in alphabetical order:
+
+.. _NEP29 Drop Schedule: https://numpy.org/neps/nep-0029-deprecation_policy.html#drop-schedule
+.. _codespell: https://github.com/codespell-project/codespell
+
diff --git a/lib/iris/__init__.py b/lib/iris/__init__.py
index 0e6670533fa..2a3bd8a7538 100644
--- a/lib/iris/__init__.py
+++ b/lib/iris/__init__.py
@@ -175,6 +175,8 @@ def __init__(self, datum_support=False, pandas_ndim=False):
# self.__dict__['example_future_flag'] = example_future_flag
self.__dict__["datum_support"] = datum_support
self.__dict__["pandas_ndim"] = pandas_ndim
+ # TODO: next major release: set IrisDeprecation to subclass
+ # DeprecationWarning instead of UserWarning.
def __repr__(self):
# msg = ('Future(example_future_flag={})')
diff --git a/lib/iris/_concatenate.py b/lib/iris/_concatenate.py
index c6d58b16225..837afd73f32 100644
--- a/lib/iris/_concatenate.py
+++ b/lib/iris/_concatenate.py
@@ -16,6 +16,7 @@
import iris.coords
import iris.cube
+import iris.exceptions
from iris.util import array_equal, guess_coord_axis
#
@@ -998,7 +999,7 @@ def register(
raise iris.exceptions.ConcatenateError([msg])
elif not match:
msg = f"Found cubes with overlap on concatenate axis {candidate_axis}, skipping concatenation for these cubes"
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisUserWarning)
# Check for compatible AuxCoords.
if match:
diff --git a/lib/iris/_deprecation.py b/lib/iris/_deprecation.py
index 73fcedcd828..027e11f2dce 100644
--- a/lib/iris/_deprecation.py
+++ b/lib/iris/_deprecation.py
@@ -12,7 +12,13 @@
class IrisDeprecation(UserWarning):
- """An Iris deprecation warning."""
+ """
+ An Iris deprecation warning.
+
+ Note this subclasses UserWarning for backwards compatibility with Iris'
+ original deprecation warnings. Should subclass DeprecationWarning at the
+ next major release.
+ """
pass
@@ -44,7 +50,7 @@ def warn_deprecated(msg, stacklevel=2):
>>>
"""
- warnings.warn(msg, IrisDeprecation, stacklevel=stacklevel)
+ warnings.warn(msg, category=IrisDeprecation, stacklevel=stacklevel)
# A Mixin for a wrapper class that copies the docstring of the wrapped class
diff --git a/lib/iris/analysis/_regrid.py b/lib/iris/analysis/_regrid.py
index 4592a0ede70..65679cd968d 100644
--- a/lib/iris/analysis/_regrid.py
+++ b/lib/iris/analysis/_regrid.py
@@ -20,6 +20,7 @@
snapshot_grid,
)
from iris.analysis._scipy_interpolate import _RegularGridInterpolator
+from iris.exceptions import IrisImpossibleUpdateWarning
from iris.util import _meshgrid, guess_coord_axis
@@ -1136,6 +1137,6 @@ def regrid_reference_surface(
"Cannot update aux_factory {!r} because of dropped"
" coordinates.".format(factory.name())
)
- warnings.warn(msg)
+ warnings.warn(msg, category=IrisImpossibleUpdateWarning)
return result
diff --git a/lib/iris/analysis/calculus.py b/lib/iris/analysis/calculus.py
index 75b7d864066..44b1adc5801 100644
--- a/lib/iris/analysis/calculus.py
+++ b/lib/iris/analysis/calculus.py
@@ -24,6 +24,7 @@
import iris.analysis.maths
import iris.coord_systems
import iris.coords
+from iris.exceptions import IrisUserWarning
from iris.util import delta
__all__ = ["cube_delta", "curl", "differentiate"]
@@ -85,7 +86,10 @@ def _construct_midpoint_coord(coord, circular=None):
"Construction coordinate midpoints for the '{}' coordinate, "
"though it has the attribute 'circular'={}."
)
- warnings.warn(msg.format(circular, coord.circular, coord.name()))
+ warnings.warn(
+ msg.format(circular, coord.circular, coord.name()),
+ category=IrisUserWarning,
+ )
if coord.ndim != 1:
raise iris.exceptions.CoordinateMultiDimError(coord)
diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py
index 0d17f0b38ae..0fae5bc4996 100644
--- a/lib/iris/analysis/cartography.py
+++ b/lib/iris/analysis/cartography.py
@@ -401,16 +401,25 @@ def area_weights(cube, normalize=False):
cs = cube.coord_system("CoordSystem")
if isinstance(cs, iris.coord_systems.GeogCS):
if cs.inverse_flattening != 0.0:
- warnings.warn("Assuming spherical earth from ellipsoid.")
+ warnings.warn(
+ "Assuming spherical earth from ellipsoid.",
+ category=iris.exceptions.IrisDefaultingWarning,
+ )
radius_of_earth = cs.semi_major_axis
elif isinstance(cs, iris.coord_systems.RotatedGeogCS) and (
cs.ellipsoid is not None
):
if cs.ellipsoid.inverse_flattening != 0.0:
- warnings.warn("Assuming spherical earth from ellipsoid.")
+ warnings.warn(
+ "Assuming spherical earth from ellipsoid.",
+ category=iris.exceptions.IrisDefaultingWarning,
+ )
radius_of_earth = cs.ellipsoid.semi_major_axis
else:
- warnings.warn("Using DEFAULT_SPHERICAL_EARTH_RADIUS.")
+ warnings.warn(
+ "Using DEFAULT_SPHERICAL_EARTH_RADIUS.",
+ category=iris.exceptions.IrisDefaultingWarning,
+ )
radius_of_earth = DEFAULT_SPHERICAL_EARTH_RADIUS
# Get the lon and lat coords and axes
@@ -551,7 +560,7 @@ def cosine_latitude_weights(cube):
warnings.warn(
"Out of range latitude values will be "
"clipped to the valid range.",
- UserWarning,
+ category=iris.exceptions.IrisDefaultingWarning,
)
points = lat.points
l_weights = np.cos(points).clip(0.0, 1.0)
@@ -665,7 +674,8 @@ def project(cube, target_proj, nx=None, ny=None):
# Assume WGS84 latlon if unspecified
warnings.warn(
"Coordinate system of latitude and longitude "
- "coordinates is not specified. Assuming WGS84 Geodetic."
+ "coordinates is not specified. Assuming WGS84 Geodetic.",
+ category=iris.exceptions.IrisDefaultingWarning,
)
orig_cs = iris.coord_systems.GeogCS(
semi_major_axis=6378137.0, inverse_flattening=298.257223563
@@ -857,7 +867,8 @@ def project(cube, target_proj, nx=None, ny=None):
lat_coord.name(),
lon_coord.name(),
[coord.name() for coord in discarded_coords],
- )
+ ),
+ category=iris.exceptions.IrisIgnoringWarning,
)
# TODO handle derived coords/aux_factories
diff --git a/lib/iris/analysis/geometry.py b/lib/iris/analysis/geometry.py
index b246b518d41..9898f4e9747 100644
--- a/lib/iris/analysis/geometry.py
+++ b/lib/iris/analysis/geometry.py
@@ -74,7 +74,7 @@ def _extract_relevant_cube_slice(cube, geometry):
except ValueError:
warnings.warn(
"The geometry exceeds the cube's x dimension at the " "lower end.",
- UserWarning,
+ category=iris.exceptions.IrisGeometryExceedWarning,
)
x_min_ix = 0 if x_ascending else x_coord.points.size - 1
@@ -84,7 +84,7 @@ def _extract_relevant_cube_slice(cube, geometry):
except ValueError:
warnings.warn(
"The geometry exceeds the cube's x dimension at the " "upper end.",
- UserWarning,
+ category=iris.exceptions.IrisGeometryExceedWarning,
)
x_max_ix = x_coord.points.size - 1 if x_ascending else 0
@@ -94,7 +94,7 @@ def _extract_relevant_cube_slice(cube, geometry):
except ValueError:
warnings.warn(
"The geometry exceeds the cube's y dimension at the " "lower end.",
- UserWarning,
+ category=iris.exceptions.IrisGeometryExceedWarning,
)
y_min_ix = 0 if y_ascending else y_coord.points.size - 1
@@ -104,7 +104,7 @@ def _extract_relevant_cube_slice(cube, geometry):
except ValueError:
warnings.warn(
"The geometry exceeds the cube's y dimension at the " "upper end.",
- UserWarning,
+ category=iris.exceptions.IrisGeometryExceedWarning,
)
y_max_ix = y_coord.points.size - 1 if y_ascending else 0
diff --git a/lib/iris/analysis/maths.py b/lib/iris/analysis/maths.py
index b77c6cd80f9..5e180c6ee28 100644
--- a/lib/iris/analysis/maths.py
+++ b/lib/iris/analysis/maths.py
@@ -988,7 +988,8 @@ def _broadcast_cube_coord_data(cube, other, operation_name, dim=None):
if other.has_bounds():
warnings.warn(
"Using {!r} with a bounded coordinate is not well "
- "defined; ignoring bounds.".format(operation_name)
+ "defined; ignoring bounds.".format(operation_name),
+ category=iris.exceptions.IrisIgnoringBoundsWarning,
)
points = other.points
diff --git a/lib/iris/analysis/trajectory.py b/lib/iris/analysis/trajectory.py
index 84ce89ab6f2..2495ff12fc2 100644
--- a/lib/iris/analysis/trajectory.py
+++ b/lib/iris/analysis/trajectory.py
@@ -734,7 +734,7 @@ class UnstructuredNearestNeigbourRegridder:
"""
- # TODO: cache the necessary bits of the operation so re-use can actually
+ # TODO: cache the necessary bits of the operation so reuse can actually
# be more efficient.
def __init__(self, src_cube, target_grid_cube):
"""
@@ -873,7 +873,7 @@ def __init__(self, src_cube, target_grid_cube):
def __call__(self, src_cube):
# Check the source cube X and Y coords match the original.
# Note: for now, this is sufficient to ensure a valid trajectory
- # interpolation, but if in future we save + re-use the cache context
+ # interpolation, but if in future we save and reuse the cache context
# for the 'interpolate' call, we may need more checks here.
# Check the given cube against the original.
diff --git a/lib/iris/aux_factory.py b/lib/iris/aux_factory.py
index f49de62b3f0..323c89e3fb3 100644
--- a/lib/iris/aux_factory.py
+++ b/lib/iris/aux_factory.py
@@ -21,6 +21,7 @@
metadata_manager_factory,
)
import iris.coords
+from iris.exceptions import IrisIgnoringBoundsWarning
class AuxCoordFactory(CFVariableMixin, metaclass=ABCMeta):
@@ -441,7 +442,9 @@ def _check_dependencies(pressure_at_top, sigma, surface_air_pressure):
f"Coordinate '{coord.name()}' has bounds. These will "
"be disregarded"
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Check units
if sigma.units.is_unknown():
@@ -522,7 +525,8 @@ def make_coord(self, coord_dims_func):
if pressure_at_top.shape[-1:] not in [(), (1,)]:
warnings.warn(
"Pressure at top coordinate has bounds. These are being "
- "disregarded"
+ "disregarded",
+ category=IrisIgnoringBoundsWarning,
)
pressure_at_top_pts = nd_points_by_key["pressure_at_top"]
bds_shape = list(pressure_at_top_pts.shape) + [1]
@@ -530,7 +534,8 @@ def make_coord(self, coord_dims_func):
if surface_air_pressure.shape[-1:] not in [(), (1,)]:
warnings.warn(
"Surface pressure coordinate has bounds. These are being "
- "disregarded"
+ "disregarded",
+ category=IrisIgnoringBoundsWarning,
)
surface_air_pressure_pts = nd_points_by_key[
"surface_air_pressure"
@@ -595,7 +600,9 @@ def __init__(self, delta=None, sigma=None, orography=None):
"Orography coordinate {!r} has bounds."
" These will be disregarded.".format(orography.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
self.delta = delta
self.sigma = sigma
@@ -684,7 +691,7 @@ def make_coord(self, coord_dims_func):
warnings.warn(
"Orography coordinate has bounds. "
"These are being disregarded.",
- UserWarning,
+ category=IrisIgnoringBoundsWarning,
stacklevel=2,
)
orography_pts = nd_points_by_key["orography"]
@@ -739,7 +746,9 @@ def update(self, old_coord, new_coord=None):
"Orography coordinate {!r} has bounds."
" These will be disregarded.".format(new_coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
self.orography = new_coord
@@ -806,7 +815,9 @@ def _check_dependencies(delta, sigma, surface_air_pressure):
"Surface pressure coordinate {!r} has bounds. These will"
" be disregarded.".format(surface_air_pressure.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Check units.
if sigma is not None and sigma.units.is_unknown():
@@ -898,7 +909,8 @@ def make_coord(self, coord_dims_func):
if surface_air_pressure.shape[-1:] not in [(), (1,)]:
warnings.warn(
"Surface pressure coordinate has bounds. "
- "These are being disregarded."
+ "These are being disregarded.",
+ category=IrisIgnoringBoundsWarning,
)
surface_air_pressure_pts = nd_points_by_key[
"surface_air_pressure"
@@ -1012,7 +1024,9 @@ def _check_dependencies(sigma, eta, depth, depth_c, nsigma, zlev):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(term, coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
for coord, term in ((depth_c, "depth_c"), (nsigma, "nsigma")):
if coord is not None and coord.shape != (1,):
@@ -1187,7 +1201,9 @@ def make_coord(self, coord_dims_func):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(key, name)
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Swap bounds with points.
bds_shape = list(nd_points_by_key[key].shape) + [1]
bounds = nd_points_by_key[key].reshape(bds_shape)
@@ -1268,7 +1284,9 @@ def _check_dependencies(sigma, eta, depth):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(term, coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Check units.
if sigma is not None and sigma.units.is_unknown():
@@ -1349,7 +1367,9 @@ def make_coord(self, coord_dims_func):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(key, name)
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Swap bounds with points.
bds_shape = list(nd_points_by_key[key].shape) + [1]
bounds = nd_points_by_key[key].reshape(bds_shape)
@@ -1444,7 +1464,9 @@ def _check_dependencies(s, c, eta, depth, depth_c):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(term, coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
if depth_c is not None and depth_c.shape != (1,):
msg = (
@@ -1543,7 +1565,9 @@ def make_coord(self, coord_dims_func):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(key, name)
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Swap bounds with points.
bds_shape = list(nd_points_by_key[key].shape) + [1]
bounds = nd_points_by_key[key].reshape(bds_shape)
@@ -1637,7 +1661,9 @@ def _check_dependencies(s, eta, depth, a, b, depth_c):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(term, coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
coords = ((a, "a"), (b, "b"), (depth_c, "depth_c"))
for coord, term in coords:
@@ -1740,7 +1766,9 @@ def make_coord(self, coord_dims_func):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(key, name)
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Swap bounds with points.
bds_shape = list(nd_points_by_key[key].shape) + [1]
bounds = nd_points_by_key[key].reshape(bds_shape)
@@ -1839,7 +1867,9 @@ def _check_dependencies(s, c, eta, depth, depth_c):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(term, coord.name())
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
if depth_c is not None and depth_c.shape != (1,):
msg = (
@@ -1938,7 +1968,9 @@ def make_coord(self, coord_dims_func):
"The {} coordinate {!r} has bounds. "
"These are being disregarded.".format(key, name)
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=IrisIgnoringBoundsWarning, stacklevel=2
+ )
# Swap bounds with points.
bds_shape = list(nd_points_by_key[key].shape) + [1]
bounds = nd_points_by_key[key].reshape(bds_shape)
diff --git a/lib/iris/config.py b/lib/iris/config.py
index 79d141e53f3..03d3d363a62 100644
--- a/lib/iris/config.py
+++ b/lib/iris/config.py
@@ -36,6 +36,8 @@
import os.path
import warnings
+import iris.exceptions
+
def get_logger(
name, datefmt=None, fmt=None, level=None, propagate=None, handler=True
@@ -145,7 +147,10 @@ def get_dir_option(section, option, default=None):
"Ignoring config item {!r}:{!r} (section:option) as {!r}"
" is not a valid directory path."
)
- warnings.warn(msg.format(section, option, c_path))
+ warnings.warn(
+ msg.format(section, option, c_path),
+ category=iris.exceptions.IrisIgnoringWarning,
+ )
return path
@@ -251,7 +256,10 @@ def __setattr__(self, name, value):
"Attempting to set invalid value {!r} for "
"attribute {!r}. Defaulting to {!r}."
)
- warnings.warn(wmsg.format(value, name, good_value))
+ warnings.warn(
+ wmsg.format(value, name, good_value),
+ category=iris.exceptions.IrisDefaultingWarning,
+ )
value = good_value
self.__dict__[name] = value
diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py
index edf0c1871ba..3d986fefce0 100644
--- a/lib/iris/coord_systems.py
+++ b/lib/iris/coord_systems.py
@@ -10,11 +10,15 @@
from abc import ABCMeta, abstractmethod
from functools import cached_property
+import re
import warnings
import cartopy.crs as ccrs
import numpy as np
+from iris._deprecation import warn_deprecated
+import iris.exceptions
+
def _arg_default(value, default, cast_as=float):
"""Apply a default value and type for an optional kwarg."""
@@ -449,7 +453,7 @@ def inverse_flattening(self, value):
"the GeogCS object. To change other properties set them explicitly"
" or create a new GeogCS instance."
)
- warnings.warn(wmsg, UserWarning)
+ warnings.warn(wmsg, category=iris.exceptions.IrisUserWarning)
value = float(value)
self._inverse_flattening = value
@@ -818,7 +822,8 @@ def as_cartopy_crs(self):
warnings.warn(
"Discarding false_easting and false_northing that are "
- "not used by Cartopy."
+ "not used by Cartopy.",
+ category=iris.exceptions.IrisDefaultingWarning,
)
return ccrs.Orthographic(
@@ -1631,3 +1636,197 @@ def as_cartopy_crs(self):
def as_cartopy_projection(self):
return self.as_cartopy_crs()
+
+
+class ObliqueMercator(CoordSystem):
+ """
+ A cylindrical map projection, with XY coordinates measured in metres.
+
+ Designed for regions not well suited to :class:`Mercator` or
+ :class:`TransverseMercator`, as the positioning of the cylinder is more
+ customisable.
+
+ See Also
+ --------
+ :class:`RotatedMercator`
+
+ """
+
+ grid_mapping_name = "oblique_mercator"
+
+ def __init__(
+ self,
+ azimuth_of_central_line,
+ latitude_of_projection_origin,
+ longitude_of_projection_origin,
+ false_easting=None,
+ false_northing=None,
+ scale_factor_at_projection_origin=None,
+ ellipsoid=None,
+ ):
+ """
+ Constructs an ObliqueMercator object.
+
+ Parameters
+ ----------
+ azimuth_of_central_line : float
+ Azimuth of centerline clockwise from north at the center point of
+ the centre line.
+ latitude_of_projection_origin : float
+ The true longitude of the central meridian in degrees.
+ longitude_of_projection_origin: float
+ The true latitude of the planar origin in degrees.
+ false_easting: float, optional
+ X offset from the planar origin in metres.
+ Defaults to 0.0 .
+ false_northing: float, optional
+ Y offset from the planar origin in metres.
+ Defaults to 0.0 .
+ scale_factor_at_projection_origin: float, optional
+ Scale factor at the central meridian.
+ Defaults to 1.0 .
+ ellipsoid: :class:`GeogCS`, optional
+ If given, defines the ellipsoid.
+
+ Examples
+ --------
+ >>> from iris.coord_systems import GeogCS, ObliqueMercator
+ >>> my_ellipsoid = GeogCS(6371229.0, None, 0.0)
+ >>> ObliqueMercator(90.0, -22.0, -59.0, -25000.0, -25000.0, 1., my_ellipsoid)
+ ObliqueMercator(azimuth_of_central_line=90.0, latitude_of_projection_origin=-22.0, longitude_of_projection_origin=-59.0, false_easting=-25000.0, false_northing=-25000.0, scale_factor_at_projection_origin=1.0, ellipsoid=GeogCS(6371229.0))
+
+ """
+ #: Azimuth of centerline clockwise from north.
+ self.azimuth_of_central_line = float(azimuth_of_central_line)
+
+ #: True latitude of planar origin in degrees.
+ self.latitude_of_projection_origin = float(
+ latitude_of_projection_origin
+ )
+
+ #: True longitude of planar origin in degrees.
+ self.longitude_of_projection_origin = float(
+ longitude_of_projection_origin
+ )
+
+ #: X offset from planar origin in metres.
+ self.false_easting = _arg_default(false_easting, 0)
+
+ #: Y offset from planar origin in metres.
+ self.false_northing = _arg_default(false_northing, 0)
+
+ #: Scale factor at the central meridian.
+ self.scale_factor_at_projection_origin = _arg_default(
+ scale_factor_at_projection_origin, 1.0
+ )
+
+ #: Ellipsoid definition (:class:`GeogCS` or None).
+ self.ellipsoid = ellipsoid
+
+ def __repr__(self):
+ return (
+ "{!s}(azimuth_of_central_line={!r}, "
+ "latitude_of_projection_origin={!r}, "
+ "longitude_of_projection_origin={!r}, false_easting={!r}, "
+ "false_northing={!r}, scale_factor_at_projection_origin={!r}, "
+ "ellipsoid={!r})".format(
+ self.__class__.__name__,
+ self.azimuth_of_central_line,
+ self.latitude_of_projection_origin,
+ self.longitude_of_projection_origin,
+ self.false_easting,
+ self.false_northing,
+ self.scale_factor_at_projection_origin,
+ self.ellipsoid,
+ )
+ )
+
+ def as_cartopy_crs(self):
+ globe = self._ellipsoid_to_globe(self.ellipsoid, None)
+
+ return ccrs.ObliqueMercator(
+ central_longitude=self.longitude_of_projection_origin,
+ central_latitude=self.latitude_of_projection_origin,
+ false_easting=self.false_easting,
+ false_northing=self.false_northing,
+ scale_factor=self.scale_factor_at_projection_origin,
+ azimuth=self.azimuth_of_central_line,
+ globe=globe,
+ )
+
+ def as_cartopy_projection(self):
+ return self.as_cartopy_crs()
+
+
+class RotatedMercator(ObliqueMercator):
+ """
+ :class:`ObliqueMercator` with ``azimuth_of_central_line=90``.
+
+ As noted in CF versions 1.10 and earlier:
+
+ The Rotated Mercator projection is an Oblique Mercator projection
+ with azimuth = +90.
+
+ .. deprecated:: 3.8.0
+ This coordinate system was introduced as already scheduled for removal
+ in a future release, since CF version 1.11 onwards now requires use of
+ :class:`ObliqueMercator` with ``azimuth_of_central_line=90.`` .
+ Any :class:`RotatedMercator` instances will always be saved to NetCDF
+ as the ``oblique_mercator`` grid mapping.
+
+ """
+
+ def __init__(
+ self,
+ latitude_of_projection_origin,
+ longitude_of_projection_origin,
+ false_easting=None,
+ false_northing=None,
+ scale_factor_at_projection_origin=None,
+ ellipsoid=None,
+ ):
+ """
+ Constructs a RotatedMercator object.
+
+ Parameters
+ ----------
+ latitude_of_projection_origin : float
+ The true longitude of the central meridian in degrees.
+ longitude_of_projection_origin: float
+ The true latitude of the planar origin in degrees.
+ false_easting: float, optional
+ X offset from the planar origin in metres.
+ Defaults to 0.0 .
+ false_northing: float, optional
+ Y offset from the planar origin in metres.
+ Defaults to 0.0 .
+ scale_factor_at_projection_origin: float, optional
+ Scale factor at the central meridian.
+ Defaults to 1.0 .
+ ellipsoid: :class:`GeogCS`, optional
+ If given, defines the ellipsoid.
+
+ """
+ message = (
+ "iris.coord_systems.RotatedMercator is deprecated, and will be "
+ "removed in a future release. Instead please use "
+ "iris.coord_systems.ObliqueMercator with "
+ "azimuth_of_central_line=90 ."
+ )
+ warn_deprecated(message)
+
+ super().__init__(
+ 90.0,
+ latitude_of_projection_origin,
+ longitude_of_projection_origin,
+ false_easting,
+ false_northing,
+ scale_factor_at_projection_origin,
+ ellipsoid,
+ )
+
+ def __repr__(self):
+ # Remove the azimuth argument from the parent repr.
+ result = super().__repr__()
+ result = re.sub(r"azimuth_of_central_line=\d*\.?\d*, ", "", result)
+ return result
diff --git a/lib/iris/coords.py b/lib/iris/coords.py
index 1a6e8d4e6a8..d5ee2667d83 100644
--- a/lib/iris/coords.py
+++ b/lib/iris/coords.py
@@ -2057,7 +2057,8 @@ def contiguous_bounds(self):
if self.ndim == 1:
warnings.warn(
"Coordinate {!r} is not bounded, guessing "
- "contiguous bounds.".format(self.name())
+ "contiguous bounds.".format(self.name()),
+ category=iris.exceptions.IrisGuessBoundsWarning,
)
bounds = self._guess_bounds()
elif self.ndim == 2:
@@ -2224,7 +2225,10 @@ def serialize(x):
"Collapsing a multi-dimensional coordinate. "
"Metadata may not be fully descriptive for {!r}."
)
- warnings.warn(msg.format(self.name()))
+ warnings.warn(
+ msg.format(self.name()),
+ category=iris.exceptions.IrisVagueMetadataWarning,
+ )
else:
try:
self._sanity_check_bounds()
@@ -2234,7 +2238,10 @@ def serialize(x):
"Metadata may not be fully descriptive for {!r}. "
"Ignoring bounds."
)
- warnings.warn(msg.format(str(exc), self.name()))
+ warnings.warn(
+ msg.format(str(exc), self.name()),
+ category=iris.exceptions.IrisVagueMetadataWarning,
+ )
self.bounds = None
else:
if not self.is_contiguous():
@@ -2242,7 +2249,10 @@ def serialize(x):
"Collapsing a non-contiguous coordinate. "
"Metadata may not be fully descriptive for {!r}."
)
- warnings.warn(msg.format(self.name()))
+ warnings.warn(
+ msg.format(self.name()),
+ category=iris.exceptions.IrisVagueMetadataWarning,
+ )
if self.has_bounds():
item = self.core_bounds()
@@ -3109,7 +3119,7 @@ def __str__(self):
def __add__(self, other):
# Disable the default tuple behaviour of tuple concatenation
- raise NotImplementedError()
+ return NotImplemented
def xml_element(self, doc):
"""
diff --git a/lib/iris/cube.py b/lib/iris/cube.py
index aec80dce47e..60fdbc9c946 100644
--- a/lib/iris/cube.py
+++ b/lib/iris/cube.py
@@ -3857,7 +3857,10 @@ def collapsed(self, coords, aggregator, **kwargs):
]
if lat_match:
for coord in lat_match:
- warnings.warn(msg.format(coord.name()))
+ warnings.warn(
+ msg.format(coord.name()),
+ category=iris.exceptions.IrisUserWarning,
+ )
# Determine the dimensions we need to collapse (and those we don't)
if aggregator.cell_method == "peak":
@@ -4444,7 +4447,8 @@ def rolling_window(self, coord, aggregator, window, **kwargs):
if coord_.has_bounds():
warnings.warn(
"The bounds of coordinate %r were ignored in "
- "the rolling window operation." % coord_.name()
+ "the rolling window operation." % coord_.name(),
+ category=iris.exceptions.IrisIgnoringBoundsWarning,
)
if coord_.ndim != 1:
diff --git a/lib/iris/exceptions.py b/lib/iris/exceptions.py
index 5d3da3349e1..919917a01d4 100644
--- a/lib/iris/exceptions.py
+++ b/lib/iris/exceptions.py
@@ -180,3 +180,212 @@ class CannotAddError(ValueError):
"""Raised when an object (e.g. coord) cannot be added to a :class:`~iris.cube.Cube`."""
pass
+
+
+###############################################################################
+# WARNINGS
+# Please namespace all warning objects (i.e. prefix with Iris...).
+
+
+class IrisUserWarning(UserWarning):
+ """
+ Base class for :class:`UserWarning`\\ s generated by Iris.
+ """
+
+ pass
+
+
+class IrisLoadWarning(IrisUserWarning):
+ """Any warning relating to loading."""
+
+ pass
+
+
+class IrisSaveWarning(IrisUserWarning):
+ """Any warning relating to saving."""
+
+ pass
+
+
+class IrisCfWarning(IrisUserWarning):
+ """Any warning relating to :term:`CF Conventions` ."""
+
+ pass
+
+
+class IrisIgnoringWarning(IrisUserWarning):
+ """
+ Any warning that involves an Iris operation not using some information.
+
+ E.g. :class:`~iris.aux_factory.AuxCoordFactory` generation disregarding
+ bounds.
+ """
+
+ pass
+
+
+class IrisDefaultingWarning(IrisUserWarning):
+ """
+ Any warning that involves Iris changing invalid/missing information.
+
+ E.g. creating a :class:`~iris.coords.AuxCoord` from an invalid
+ :class:`~iris.coords.DimCoord` definition.
+ """
+
+ pass
+
+
+class IrisVagueMetadataWarning(IrisUserWarning):
+ """Warnings where object metadata may not be fully descriptive."""
+
+ pass
+
+
+class IrisUnsupportedPlottingWarning(IrisUserWarning):
+ """Warnings where support for a plotting module/function is not guaranteed."""
+
+ pass
+
+
+class IrisImpossibleUpdateWarning(IrisUserWarning):
+ """
+ Warnings where it is not possible to update an object.
+
+ Mainly generated during regridding where the necessary information for
+ updating an :class:`~iris.aux_factory.AuxCoordFactory` is no longer
+ present.
+ """
+
+ pass
+
+
+class IrisGeometryExceedWarning(IrisUserWarning):
+ """:mod:`iris.analysis.geometry` warnings about geometry exceeding dimensions."""
+
+ pass
+
+
+class IrisMaskValueMatchWarning(IrisUserWarning):
+ """Warnings where the value representing masked data is actually present in data."""
+
+ pass
+
+
+########
+
+
+class IrisCfLoadWarning(IrisCfWarning, IrisLoadWarning):
+ """Any warning relating to both loading and :term:`CF Conventions` ."""
+
+ pass
+
+
+class IrisCfSaveWarning(IrisCfWarning, IrisSaveWarning):
+ """Any warning relating to both saving and :term:`CF Conventions` ."""
+
+ pass
+
+
+class IrisCfInvalidCoordParamWarning(IrisCfLoadWarning):
+ """
+ Warnings where incorrect information for CF coord construction is in a file.
+ """
+
+ pass
+
+
+class IrisCfMissingVarWarning(IrisCfLoadWarning):
+ """
+ Warnings where a CF variable references another variable that is not in the file.
+ """
+
+ pass
+
+
+class IrisCfLabelVarWarning(IrisCfLoadWarning, IrisIgnoringWarning):
+ """
+ Warnings where a CF string/label variable is being used inappropriately.
+ """
+
+ pass
+
+
+class IrisCfNonSpanningVarWarning(IrisCfLoadWarning, IrisIgnoringWarning):
+ """
+ Warnings where a CF variable is ignored because it does not span the required dimension.
+ """
+
+ pass
+
+
+########
+
+
+class IrisIgnoringBoundsWarning(IrisIgnoringWarning):
+ """
+ Warnings where bounds information has not been used by an Iris operation.
+ """
+
+ pass
+
+
+class IrisCannotAddWarning(IrisIgnoringWarning):
+ """
+ Warnings where a member object cannot be added to a :class:`~iris.cube.Cube` .
+ """
+
+ pass
+
+
+class IrisGuessBoundsWarning(IrisDefaultingWarning):
+ """
+ Warnings where Iris has filled absent bounds information with a best estimate.
+ """
+
+ pass
+
+
+class IrisPpClimModifiedWarning(IrisSaveWarning, IrisDefaultingWarning):
+ """
+ Warnings where a climatology has been modified while saving :term:`Post Processing (PP) Format` .
+ """
+
+ pass
+
+
+class IrisFactoryCoordNotFoundWarning(IrisLoadWarning):
+ """
+ Warnings where a referenced factory coord can not be found when loading a variable in :term:`NetCDF Format`.
+ """
+
+ pass
+
+
+class IrisNimrodTranslationWarning(IrisLoadWarning):
+ """
+ For unsupported vertical coord types in :mod:`iris.file_formats.nimrod_load_rules`.
+
+ (Pre-dates the full categorisation of Iris UserWarnings).
+ """
+
+ pass
+
+
+class IrisUnknownCellMethodWarning(IrisCfLoadWarning):
+ """
+ If a loaded :class:`~iris.coords.CellMethod` is not one the method names known to Iris.
+
+ (Pre-dates the full categorisation of Iris UserWarnings).
+ """
+
+ pass
+
+
+class IrisSaverFillValueWarning(IrisMaskValueMatchWarning, IrisSaveWarning):
+ """
+ For fill value complications during Iris file saving :term:`NetCDF Format`.
+
+ (Pre-dates the full categorisation of Iris UserWarnings).
+ """
+
+ pass
diff --git a/lib/iris/experimental/regrid.py b/lib/iris/experimental/regrid.py
index 76c6002d2bf..d5fa7c6f72d 100644
--- a/lib/iris/experimental/regrid.py
+++ b/lib/iris/experimental/regrid.py
@@ -43,6 +43,7 @@
import iris.analysis.cartography
import iris.coord_systems
import iris.cube
+from iris.exceptions import IrisImpossibleUpdateWarning
from iris.util import _meshgrid
wmsg = (
@@ -538,7 +539,7 @@ def regrid_reference_surface(
"Cannot update aux_factory {!r} because of dropped"
" coordinates.".format(factory.name())
)
- warnings.warn(msg)
+ warnings.warn(msg, category=IrisImpossibleUpdateWarning)
return result
def __call__(self, src_cube):
diff --git a/lib/iris/experimental/ugrid/cf.py b/lib/iris/experimental/ugrid/cf.py
index 86b76c7a751..42c1cfd0a34 100644
--- a/lib/iris/experimental/ugrid/cf.py
+++ b/lib/iris/experimental/ugrid/cf.py
@@ -12,6 +12,7 @@
"""
import warnings
+from ...exceptions import IrisCfLabelVarWarning, IrisCfMissingVarWarning
from ...fileformats import cf
from .mesh import Connectivity
@@ -65,7 +66,9 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"{nc_var_name}"
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message, category=IrisCfMissingVarWarning
+ )
else:
# Restrict to non-string type i.e. not a
# CFLabelVariable.
@@ -80,7 +83,9 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"CF-netCDF label variable."
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message, category=IrisCfLabelVarWarning
+ )
return result
@@ -136,7 +141,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"variable {nc_var_name}"
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message,
+ category=IrisCfMissingVarWarning,
+ )
else:
# Restrict to non-string type i.e. not a
# CFLabelVariable.
@@ -154,7 +162,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"CF-netCDF label variable."
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message,
+ category=IrisCfLabelVarWarning,
+ )
return result
@@ -211,7 +222,9 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"referenced by netCDF variable {nc_var_name}"
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message, category=IrisCfMissingVarWarning
+ )
else:
# Restrict to non-string type i.e. not a
# CFLabelVariable.
@@ -226,7 +239,9 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
f"variable."
)
if warn:
- warnings.warn(message)
+ warnings.warn(
+ message, category=IrisCfLabelVarWarning
+ )
return result
diff --git a/lib/iris/experimental/ugrid/load.py b/lib/iris/experimental/ugrid/load.py
index d2670ac690c..67d14919300 100644
--- a/lib/iris/experimental/ugrid/load.py
+++ b/lib/iris/experimental/ugrid/load.py
@@ -19,6 +19,11 @@
from ...config import get_logger
from ...coords import AuxCoord
+from ...exceptions import (
+ IrisCfWarning,
+ IrisDefaultingWarning,
+ IrisIgnoringWarning,
+)
from ...fileformats._nc_load_rules.helpers import get_attr_units, get_names
from ...fileformats.netcdf import loader as nc_loader
from ...io import decode_uri, expand_filespecs
@@ -35,6 +40,20 @@
logger = get_logger(__name__, propagate=True, handler=False)
+class _WarnComboCfDefaulting(IrisCfWarning, IrisDefaultingWarning):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboCfDefaultingIgnoring(
+ _WarnComboCfDefaulting, IrisIgnoringWarning
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
class ParseUGridOnLoad(threading.local):
def __init__(self):
"""
@@ -351,7 +370,10 @@ def _build_mesh(cf, mesh_var, file_path):
)
if cf_role_message:
cf_role_message += " Correcting to 'mesh_topology'."
- warnings.warn(cf_role_message)
+ warnings.warn(
+ cf_role_message,
+ category=_WarnComboCfDefaulting,
+ )
if hasattr(mesh_var, "volume_node_connectivity"):
topology_dimension = 3
@@ -369,7 +391,7 @@ def _build_mesh(cf, mesh_var, file_path):
f" : *Assuming* topology_dimension={topology_dimension}"
", consistent with the attached connectivities."
)
- warnings.warn(msg)
+ warnings.warn(msg, category=_WarnComboCfDefaulting)
else:
quoted_topology_dimension = mesh_var.topology_dimension
if quoted_topology_dimension != topology_dimension:
@@ -381,7 +403,10 @@ def _build_mesh(cf, mesh_var, file_path):
f"{quoted_topology_dimension}"
" -- ignoring this as it is inconsistent."
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboCfDefaultingIgnoring,
+ )
node_dimension = None
edge_dimension = getattr(mesh_var, "edge_dimension", None)
diff --git a/lib/iris/fileformats/_ff.py b/lib/iris/fileformats/_ff.py
index 2545bc39ae2..5121b47976c 100644
--- a/lib/iris/fileformats/_ff.py
+++ b/lib/iris/fileformats/_ff.py
@@ -13,7 +13,11 @@
import numpy as np
-from iris.exceptions import NotYetImplementedError
+from iris.exceptions import (
+ IrisDefaultingWarning,
+ IrisLoadWarning,
+ NotYetImplementedError,
+)
from iris.fileformats._ff_cross_references import STASH_TRANS
from . import pp
@@ -118,6 +122,12 @@
REAL_POLE_LON = 5
+class _WarnComboLoadingDefaulting(IrisDefaultingWarning, IrisLoadWarning):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
class Grid:
"""
An abstract class representing the default/file-level grid
@@ -431,7 +441,8 @@ def grid(self):
grid_class = NewDynamics
warnings.warn(
"Staggered grid type: {} not currently interpreted, assuming "
- "standard C-grid".format(self.grid_staggering)
+ "standard C-grid".format(self.grid_staggering),
+ category=_WarnComboLoadingDefaulting,
)
grid = grid_class(
self.column_dependent_constants,
@@ -554,7 +565,7 @@ def range_order(range1, range2, resolution):
"may be incorrect, not having taken into account the "
"boundary size."
)
- warnings.warn(msg)
+ warnings.warn(msg, category=IrisLoadWarning)
else:
range2 = field_dim[0] - res_low
range1 = field_dim[0] - halo_dim * res_low
@@ -628,7 +639,8 @@ def _adjust_field_for_lbc(self, field):
"The LBC has a bdy less than 0. No "
"case has previously been seen of "
"this, and the decompression may be "
- "erroneous."
+ "erroneous.",
+ category=IrisLoadWarning,
)
field.bzx -= field.bdx * boundary_packing.x_halo
field.bzy -= field.bdy * boundary_packing.y_halo
@@ -741,7 +753,8 @@ def _extract_field(self):
"which has not been explicitly "
"handled by the fieldsfile loader."
" Assuming the data is on a P grid"
- ".".format(stash, subgrid)
+ ".".format(stash, subgrid),
+ category=_WarnComboLoadingDefaulting,
)
field.x, field.y = grid.vectors(subgrid)
@@ -757,14 +770,18 @@ def _extract_field(self):
"STASH to grid type mapping. Picking the P "
"position as the cell type".format(stash)
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboLoadingDefaulting,
+ )
field.bzx, field.bdx = grid.regular_x(subgrid)
field.bzy, field.bdy = grid.regular_y(subgrid)
field.bplat = grid.pole_lat
field.bplon = grid.pole_lon
elif no_x or no_y:
warnings.warn(
- "Partially missing X or Y coordinate values."
+ "Partially missing X or Y coordinate values.",
+ category=IrisLoadWarning,
)
# Check for LBC fields.
@@ -810,7 +827,9 @@ def _extract_field(self):
"Input field skipped as PPField creation failed :"
" error = {!r}"
)
- warnings.warn(msg.format(str(valerr)))
+ warnings.warn(
+ msg.format(str(valerr)), category=IrisLoadWarning
+ )
def __iter__(self):
return pp._interpret_fields(self._extract_field())
diff --git a/lib/iris/fileformats/_nc_load_rules/actions.py b/lib/iris/fileformats/_nc_load_rules/actions.py
index 09237d3f118..44ef7ac5492 100644
--- a/lib/iris/fileformats/_nc_load_rules/actions.py
+++ b/lib/iris/fileformats/_nc_load_rules/actions.py
@@ -44,6 +44,7 @@
import warnings
from iris.config import get_logger
+import iris.exceptions
import iris.fileformats.cf
import iris.fileformats.pp as pp
@@ -53,6 +54,24 @@
logger = get_logger(__name__, fmt="[%(funcName)s]")
+class _WarnComboCfLoadIgnoring(
+ iris.exceptions.IrisCfLoadWarning,
+ iris.exceptions.IrisIgnoringWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboLoadIgnoring(
+ iris.exceptions.IrisLoadWarning,
+ iris.exceptions.IrisIgnoringWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
def _default_rulenamesfunc(func_name):
# A simple default function to deduce the rules-name from an action-name.
funcname_prefix = "action_"
@@ -137,6 +156,14 @@ def action_default(engine):
None,
hh.build_geostationary_coordinate_system,
),
+ hh.CF_GRID_MAPPING_OBLIQUE: (
+ None,
+ hh.build_oblique_mercator_coordinate_system,
+ ),
+ hh.CF_GRID_MAPPING_ROTATED_MERCATOR: (
+ None,
+ hh.build_oblique_mercator_coordinate_system,
+ ),
}
@@ -471,7 +498,10 @@ def action_formula_type(engine, formula_root_fact):
succeed = False
rule_name += f"(FAILED - unrecognised formula type = {formula_type!r})"
msg = f"Ignored formula of unrecognised type: {formula_type!r}."
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboCfLoadIgnoring,
+ )
if succeed:
# Check we don't already have one.
existing_type = engine.requires.get("formula_type")
@@ -486,7 +516,10 @@ def action_formula_type(engine, formula_root_fact):
f"Formula of type ={formula_type!r} "
f"overrides another of type ={existing_type!r}.)"
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboLoadIgnoring,
+ )
rule_name += f"_{formula_type}"
# Set 'requires' info for iris.fileformats.netcdf._load_aux_factory.
engine.requires["formula_type"] = formula_type
diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py
index bbf9c660c5f..9c75c0e8661 100644
--- a/lib/iris/fileformats/_nc_load_rules/helpers.py
+++ b/lib/iris/fileformats/_nc_load_rules/helpers.py
@@ -23,6 +23,7 @@
import pyproj
import iris
+from iris._deprecation import warn_deprecated
import iris.aux_factory
from iris.common.mixin import _get_valid_standard_name
import iris.coord_systems
@@ -124,6 +125,8 @@
CF_GRID_MAPPING_TRANSVERSE = "transverse_mercator"
CF_GRID_MAPPING_VERTICAL = "vertical_perspective"
CF_GRID_MAPPING_GEOSTATIONARY = "geostationary"
+CF_GRID_MAPPING_OBLIQUE = "oblique_mercator"
+CF_GRID_MAPPING_ROTATED_MERCATOR = "rotated_mercator"
#
# CF Attribute Names.
@@ -154,6 +157,7 @@
CF_ATTR_GRID_STANDARD_PARALLEL = "standard_parallel"
CF_ATTR_GRID_PERSPECTIVE_HEIGHT = "perspective_point_height"
CF_ATTR_GRID_SWEEP_ANGLE_AXIS = "sweep_angle_axis"
+CF_ATTR_GRID_AZIMUTH_CENT_LINE = "azimuth_of_central_line"
CF_ATTR_POSITIVE = "positive"
CF_ATTR_STD_NAME = "standard_name"
CF_ATTR_LONG_NAME = "long_name"
@@ -219,6 +223,42 @@
]
+class _WarnComboIgnoringLoad(
+ iris.exceptions.IrisIgnoringWarning,
+ iris.exceptions.IrisLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboDefaultingLoad(
+ iris.exceptions.IrisDefaultingWarning,
+ iris.exceptions.IrisLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboDefaultingCfLoad(
+ iris.exceptions.IrisCfLoadWarning,
+ iris.exceptions.IrisDefaultingWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboIgnoringCfLoad(
+ iris.exceptions.IrisIgnoringWarning,
+ iris.exceptions.IrisCfLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
def _split_cell_methods(nc_cell_methods: str) -> List[re.Match]:
"""
Split a CF cell_methods attribute string into a list of zero or more cell
@@ -256,7 +296,11 @@ def _split_cell_methods(nc_cell_methods: str) -> List[re.Match]:
"Cell methods may be incorrectly parsed due to mismatched "
"brackets"
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg,
+ category=iris.exceptions.IrisCfLoadWarning,
+ stacklevel=2,
+ )
if bracket_depth > 0 and ind in name_start_inds:
name_start_inds.remove(ind)
@@ -275,14 +319,21 @@ def _split_cell_methods(nc_cell_methods: str) -> List[re.Match]:
msg = (
f"Failed to fully parse cell method string: {nc_cell_methods}"
)
- warnings.warn(msg, UserWarning, stacklevel=2)
+ warnings.warn(
+ msg, category=iris.exceptions.IrisCfLoadWarning, stacklevel=2
+ )
continue
nc_cell_methods_matches.append(nc_cell_method_match)
return nc_cell_methods_matches
-class UnknownCellMethodWarning(Warning):
+class UnknownCellMethodWarning(iris.exceptions.IrisUnknownCellMethodWarning):
+ """
+ Backwards compatible form of :class:`iris.exceptions.IrisUnknownCellMethodWarning`.
+ """
+
+ # TODO: remove at the next major release.
pass
@@ -320,7 +371,7 @@ def parse_cell_methods(nc_cell_methods):
msg = "NetCDF variable contains unknown cell method {!r}"
warnings.warn(
msg.format("{}".format(method_words[0])),
- UnknownCellMethodWarning,
+ category=UnknownCellMethodWarning,
)
d[_CM_METHOD] = method
name = d[_CM_NAME]
@@ -389,7 +440,6 @@ def parse_cell_methods(nc_cell_methods):
################################################################################
def build_cube_metadata(engine):
"""Add the standard meta data to the cube."""
-
cf_var = engine.cf_var
cube = engine.cube
@@ -436,7 +486,10 @@ def build_cube_metadata(engine):
cube.attributes[str(attr_name)] = attr_value
except ValueError as e:
msg = "Skipping global attribute {!r}: {}"
- warnings.warn(msg.format(attr_name, str(e)))
+ warnings.warn(
+ msg.format(attr_name, str(e)),
+ category=_WarnComboIgnoringLoad,
+ )
################################################################################
@@ -479,7 +532,7 @@ def _get_ellipsoid(cf_grid_var):
"applied. To apply the datum when loading, use the "
"iris.FUTURE.datum_support flag."
)
- warnings.warn(wmsg, FutureWarning, stacklevel=14)
+ warnings.warn(wmsg, category=FutureWarning, stacklevel=14)
datum = None
if datum is not None:
@@ -512,7 +565,10 @@ def build_rotated_coordinate_system(engine, cf_grid_var):
cf_grid_var, CF_ATTR_GRID_NORTH_POLE_LON, 0.0
)
if north_pole_latitude is None or north_pole_longitude is None:
- warnings.warn("Rotated pole position is not fully specified")
+ warnings.warn(
+ "Rotated pole position is not fully specified",
+ category=iris.exceptions.IrisCfLoadWarning,
+ )
north_pole_grid_lon = getattr(
cf_grid_var, CF_ATTR_GRID_NORTH_POLE_GRID_LON, 0.0
@@ -841,6 +897,58 @@ def build_geostationary_coordinate_system(engine, cf_grid_var):
return cs
+################################################################################
+def build_oblique_mercator_coordinate_system(engine, cf_grid_var):
+ """
+ Create an oblique mercator coordinate system from the CF-netCDF
+ grid mapping variable.
+
+ """
+ ellipsoid = _get_ellipsoid(cf_grid_var)
+
+ azimuth_of_central_line = getattr(
+ cf_grid_var, CF_ATTR_GRID_AZIMUTH_CENT_LINE, None
+ )
+ latitude_of_projection_origin = getattr(
+ cf_grid_var, CF_ATTR_GRID_LAT_OF_PROJ_ORIGIN, None
+ )
+ longitude_of_projection_origin = getattr(
+ cf_grid_var, CF_ATTR_GRID_LON_OF_PROJ_ORIGIN, None
+ )
+ scale_factor_at_projection_origin = getattr(
+ cf_grid_var, CF_ATTR_GRID_SCALE_FACTOR_AT_PROJ_ORIGIN, None
+ )
+ false_easting = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_EASTING, None)
+ false_northing = getattr(cf_grid_var, CF_ATTR_GRID_FALSE_NORTHING, None)
+ kwargs = dict(
+ azimuth_of_central_line=azimuth_of_central_line,
+ latitude_of_projection_origin=latitude_of_projection_origin,
+ longitude_of_projection_origin=longitude_of_projection_origin,
+ scale_factor_at_projection_origin=scale_factor_at_projection_origin,
+ false_easting=false_easting,
+ false_northing=false_northing,
+ ellipsoid=ellipsoid,
+ )
+
+ # Handle the alternative form noted in CF: rotated mercator.
+ grid_mapping_name = getattr(cf_grid_var, CF_ATTR_GRID_MAPPING_NAME)
+ candidate_systems = dict(
+ oblique_mercator=iris.coord_systems.ObliqueMercator,
+ rotated_mercator=iris.coord_systems.RotatedMercator,
+ )
+ if grid_mapping_name == "rotated_mercator":
+ message = (
+ "Iris will stop loading the rotated_mercator grid mapping name in "
+ "a future release, in accordance with CF version 1.11 . Instead "
+ "please use oblique_mercator with azimuth_of_central_line = 90 ."
+ )
+ warn_deprecated(message)
+ del kwargs[CF_ATTR_GRID_AZIMUTH_CENT_LINE]
+
+ cs = candidate_systems[grid_mapping_name](**kwargs)
+ return cs
+
+
################################################################################
def get_attr_units(cf_var, attributes):
attr_units = getattr(cf_var, CF_ATTR_UNITS, UNKNOWN_UNIT_STRING)
@@ -859,7 +967,10 @@ def get_attr_units(cf_var, attributes):
msg = "Ignoring netCDF variable {!r} invalid units {!r}".format(
cf_var.cf_name, attr_units
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboIgnoringCfLoad,
+ )
attributes["invalid_units"] = attr_units
attr_units = UNKNOWN_UNIT_STRING
@@ -948,7 +1059,8 @@ def get_cf_bounds_var(cf_coord_var):
if attr_bounds is not None and attr_climatology is not None:
warnings.warn(
"Ignoring climatology in favour of bounds attribute "
- "on NetCDF variable {!r}.".format(cf_coord_var.cf_name)
+ "on NetCDF variable {!r}.".format(cf_coord_var.cf_name),
+ category=_WarnComboIgnoringCfLoad,
)
return cf_bounds_var, climatological
@@ -1007,7 +1119,10 @@ def build_dimension_coordinate(
if ma.is_masked(points_data):
points_data = ma.filled(points_data)
msg = "Gracefully filling {!r} dimension coordinate masked points"
- warnings.warn(msg.format(str(cf_coord_var.cf_name)))
+ warnings.warn(
+ msg.format(str(cf_coord_var.cf_name)),
+ category=_WarnComboDefaultingLoad,
+ )
# Get any coordinate bounds.
cf_bounds_var, climatological = get_cf_bounds_var(cf_coord_var)
@@ -1017,7 +1132,10 @@ def build_dimension_coordinate(
if ma.is_masked(bounds_data):
bounds_data = ma.filled(bounds_data)
msg = "Gracefully filling {!r} dimension coordinate masked bounds"
- warnings.warn(msg.format(str(cf_coord_var.cf_name)))
+ warnings.warn(
+ msg.format(str(cf_coord_var.cf_name)),
+ category=_WarnComboDefaultingLoad,
+ )
# Handle transposed bounds where the vertex dimension is not
# the last one. Test based on shape to support different
# dimension names.
@@ -1082,7 +1200,10 @@ def build_dimension_coordinate(
"Failed to create {name!r} dimension coordinate: {error}\n"
"Gracefully creating {name!r} auxiliary coordinate instead."
)
- warnings.warn(msg.format(name=str(cf_coord_var.cf_name), error=e_msg))
+ warnings.warn(
+ msg.format(name=str(cf_coord_var.cf_name), error=e_msg),
+ category=_WarnComboDefaultingCfLoad,
+ )
coord = iris.coords.AuxCoord(
points_data,
standard_name=standard_name,
@@ -1097,7 +1218,10 @@ def build_dimension_coordinate(
try:
cube.add_aux_coord(coord, data_dims)
except iris.exceptions.CannotAddError as e_msg:
- warnings.warn(coord_skipped_msg.format(error=e_msg))
+ warnings.warn(
+ coord_skipped_msg.format(error=e_msg),
+ category=iris.exceptions.IrisCannotAddWarning,
+ )
coord_skipped = True
else:
# Add the dimension coordinate to the cube.
@@ -1108,7 +1232,10 @@ def build_dimension_coordinate(
# Scalar coords are placed in the aux_coords container.
cube.add_aux_coord(coord, data_dims)
except iris.exceptions.CannotAddError as e_msg:
- warnings.warn(coord_skipped_msg.format(error=e_msg))
+ warnings.warn(
+ coord_skipped_msg.format(error=e_msg),
+ category=iris.exceptions.IrisCannotAddWarning,
+ )
coord_skipped = True
if not coord_skipped:
@@ -1186,7 +1313,10 @@ def build_auxiliary_coordinate(
cube.add_aux_coord(coord, data_dims)
except iris.exceptions.CannotAddError as e_msg:
msg = "{name!r} coordinate not added to Cube: {error}"
- warnings.warn(msg.format(name=str(cf_coord_var.cf_name), error=e_msg))
+ warnings.warn(
+ msg.format(name=str(cf_coord_var.cf_name), error=e_msg),
+ category=iris.exceptions.IrisCannotAddWarning,
+ )
else:
# Make a list with names, stored on the engine, so we can find them all later.
engine.cube_parts["coordinates"].append((coord, cf_coord_var.cf_name))
@@ -1237,7 +1367,10 @@ def build_cell_measures(engine, cf_cm_var):
cube.add_cell_measure(cell_measure, data_dims)
except iris.exceptions.CannotAddError as e_msg:
msg = "{name!r} cell measure not added to Cube: {error}"
- warnings.warn(msg.format(name=str(cf_cm_var.cf_name), error=e_msg))
+ warnings.warn(
+ msg.format(name=str(cf_cm_var.cf_name), error=e_msg),
+ category=iris.exceptions.IrisCannotAddWarning,
+ )
else:
# Make a list with names, stored on the engine, so we can find them all later.
engine.cube_parts["cell_measures"].append(
@@ -1286,7 +1419,10 @@ def build_ancil_var(engine, cf_av_var):
cube.add_ancillary_variable(av, data_dims)
except iris.exceptions.CannotAddError as e_msg:
msg = "{name!r} ancillary variable not added to Cube: {error}"
- warnings.warn(msg.format(name=str(cf_av_var.cf_name), error=e_msg))
+ warnings.warn(
+ msg.format(name=str(cf_av_var.cf_name), error=e_msg),
+ category=iris.exceptions.IrisCannotAddWarning,
+ )
else:
# Make a list with names, stored on the engine, so we can find them all later.
engine.cube_parts["ancillary_variables"].append(
@@ -1503,7 +1639,8 @@ def has_supported_mercator_parameters(engine, cf_name):
):
warnings.warn(
"It does not make sense to provide both "
- '"scale_factor_at_projection_origin" and "standard_parallel".'
+ '"scale_factor_at_projection_origin" and "standard_parallel".',
+ category=iris.exceptions.IrisCfInvalidCoordParamWarning,
)
is_valid = False
@@ -1533,7 +1670,10 @@ def has_supported_polar_stereographic_parameters(engine, cf_name):
latitude_of_projection_origin != 90
and latitude_of_projection_origin != -90
):
- warnings.warn('"latitude_of_projection_origin" must be +90 or -90.')
+ warnings.warn(
+ '"latitude_of_projection_origin" must be +90 or -90.',
+ category=iris.exceptions.IrisCfInvalidCoordParamWarning,
+ )
is_valid = False
if (
@@ -1542,14 +1682,16 @@ def has_supported_polar_stereographic_parameters(engine, cf_name):
):
warnings.warn(
"It does not make sense to provide both "
- '"scale_factor_at_projection_origin" and "standard_parallel".'
+ '"scale_factor_at_projection_origin" and "standard_parallel".',
+ category=iris.exceptions.IrisCfInvalidCoordParamWarning,
)
is_valid = False
if scale_factor_at_projection_origin is None and standard_parallel is None:
warnings.warn(
'One of "scale_factor_at_projection_origin" and '
- '"standard_parallel" is required.'
+ '"standard_parallel" is required.',
+ category=iris.exceptions.IrisCfInvalidCoordParamWarning,
)
is_valid = False
diff --git a/lib/iris/fileformats/_structured_array_identification.py b/lib/iris/fileformats/_structured_array_identification.py
index b313500de75..11c62983e39 100644
--- a/lib/iris/fileformats/_structured_array_identification.py
+++ b/lib/iris/fileformats/_structured_array_identification.py
@@ -417,7 +417,7 @@ def filter_strides_of_length(length):
# If we are to build another dimension on top of this possible
# structure, we need to compute the stride that would be
# needed for that dimension.
- next_stride = np.product(
+ next_stride = np.prod(
[struct.size for (_, struct) in potential]
)
diff --git a/lib/iris/fileformats/cf.py b/lib/iris/fileformats/cf.py
index 2ed01846bd4..f412955adbc 100644
--- a/lib/iris/fileformats/cf.py
+++ b/lib/iris/fileformats/cf.py
@@ -23,6 +23,7 @@
import numpy as np
import numpy.ma as ma
+import iris.exceptions
from iris.fileformats.netcdf import _thread_safe_nc
import iris.util
@@ -280,7 +281,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF ancillary data variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
result[name] = CFAncillaryDataVariable(
name, variables[name]
@@ -323,7 +327,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF auxiliary coordinate variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
# Restrict to non-string type i.e. not a CFLabelVariable.
if not _is_str_dtype(variables[name]):
@@ -369,7 +376,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF boundary variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
result[name] = CFBoundaryVariable(
name, variables[name]
@@ -441,7 +451,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF climatology variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
result[name] = CFClimatologyVariable(
name, variables[name]
@@ -582,7 +595,8 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if warn:
message = "Missing CF-netCDF formula term variable %r, referenced by netCDF variable %r"
warnings.warn(
- message % (variable_name, nc_var_name)
+ message % (variable_name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
)
else:
if variable_name not in result:
@@ -646,7 +660,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF grid mapping variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
result[name] = CFGridMappingVariable(
name, variables[name]
@@ -685,7 +702,10 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if name not in variables:
if warn:
message = "Missing CF-netCDF label variable %r, referenced by netCDF variable %r"
- warnings.warn(message % (name, nc_var_name))
+ warnings.warn(
+ message % (name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
+ )
else:
# Register variable, but only allow string type.
var = variables[name]
@@ -857,7 +877,8 @@ def identify(cls, variables, ignore=None, target=None, warn=True):
if warn:
message = "Missing CF-netCDF measure variable %r, referenced by netCDF variable %r"
warnings.warn(
- message % (variable_name, nc_var_name)
+ message % (variable_name, nc_var_name),
+ category=iris.exceptions.IrisCfMissingVarWarning,
)
else:
result[variable_name] = CFMeasureVariable(
@@ -1069,7 +1090,8 @@ def __init__(self, file_source, warn=False, monotonic=False):
]:
warnings.warn(
"Optimise CF-netCDF loading by converting data from NetCDF3 "
- 'to NetCDF4 file format using the "nccopy" command.'
+ 'to NetCDF4 file format using the "nccopy" command.',
+ category=iris.exceptions.IrisLoadWarning,
)
self._check_monotonic = monotonic
@@ -1210,7 +1232,10 @@ def _build(cf_variable):
cf_variable.dimensions,
)
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=iris.exceptions.IrisCfNonSpanningVarWarning,
+ )
# Build CF data variable relationships.
if isinstance(cf_variable, CFDataVariable):
@@ -1261,7 +1286,10 @@ def _build(cf_variable):
cf_variable.dimensions,
)
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=iris.exceptions.IrisCfNonSpanningVarWarning,
+ )
# Add the CF group to the variable.
cf_variable.cf_group = cf_group
diff --git a/lib/iris/fileformats/name_loaders.py b/lib/iris/fileformats/name_loaders.py
index 0189a8806f2..cb8867b6ea2 100644
--- a/lib/iris/fileformats/name_loaders.py
+++ b/lib/iris/fileformats/name_loaders.py
@@ -17,7 +17,7 @@
import iris.coord_systems
from iris.coords import AuxCoord, CellMethod, DimCoord
import iris.cube
-from iris.exceptions import TranslationError
+from iris.exceptions import IrisLoadWarning, TranslationError
import iris.util
EARTH_RADIUS = 6371229.0
@@ -273,7 +273,9 @@ def _parse_units(units):
try:
units = cf_units.Unit(units)
except ValueError:
- warnings.warn("Unknown units: {!r}".format(units))
+ warnings.warn(
+ "Unknown units: {!r}".format(units), category=IrisLoadWarning
+ )
units = cf_units.Unit(None)
return units
@@ -611,7 +613,9 @@ def _build_cell_methods(av_or_ints, coord):
else:
cell_method = None
msg = "Unknown {} statistic: {!r}. Unable to create cell method."
- warnings.warn(msg.format(coord, av_or_int))
+ warnings.warn(
+ msg.format(coord, av_or_int), category=IrisLoadWarning
+ )
cell_methods.append(cell_method) # NOTE: this can be a None
return cell_methods
diff --git a/lib/iris/fileformats/netcdf/loader.py b/lib/iris/fileformats/netcdf/loader.py
index 20d255ea44d..29202af89e1 100644
--- a/lib/iris/fileformats/netcdf/loader.py
+++ b/lib/iris/fileformats/netcdf/loader.py
@@ -50,6 +50,15 @@
NetCDFDataProxy = _thread_safe_nc.NetCDFDataProxy
+class _WarnComboIgnoringBoundsLoad(
+ iris.exceptions.IrisIgnoringBoundsWarning,
+ iris.exceptions.IrisLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
def _actions_engine():
# Return an 'actions engine', which provides a pyke-rules-like interface to
# the core cf translation code.
@@ -352,7 +361,8 @@ def coord_from_term(term):
return coord
warnings.warn(
"Unable to find coordinate for variable "
- "{!r}".format(name)
+ "{!r}".format(name),
+ category=iris.exceptions.IrisFactoryCoordNotFoundWarning,
)
if formula_type == "atmosphere_sigma_coordinate":
@@ -393,7 +403,10 @@ def coord_from_term(term):
coord_p0.name()
)
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboIgnoringBoundsLoad,
+ )
coord_a = coord_from_term("a")
if coord_a is not None:
if coord_a.units.is_unknown():
@@ -584,7 +597,10 @@ def load_cubes(file_sources, callback=None, constraints=None):
try:
_load_aux_factory(engine, cube)
except ValueError as e:
- warnings.warn("{}".format(e))
+ warnings.warn(
+ "{}".format(e),
+ category=iris.exceptions.IrisLoadWarning,
+ )
# Perform any user registered callback function.
cube = run_callback(callback, cube, cf_var, file_source)
diff --git a/lib/iris/fileformats/netcdf/saver.py b/lib/iris/fileformats/netcdf/saver.py
index c0cfd3d10b2..011f74892dd 100644
--- a/lib/iris/fileformats/netcdf/saver.py
+++ b/lib/iris/fileformats/netcdf/saver.py
@@ -102,6 +102,9 @@
# UKMO specific attributes that should not be global.
_UKMO_DATA_ATTRS = ["STASH", "um_stash_source", "ukmo__process_flags"]
+# TODO: whenever we advance to CF-1.11 we should then discuss a completion date
+# for the deprecation of Rotated Mercator in coord_systems.py and
+# _nc_load_rules/helpers.py .
CF_CONVENTIONS_VERSION = "CF-1.7"
_FactoryDefn = collections.namedtuple(
@@ -157,6 +160,15 @@
}
+class _WarnComboMaskSave(
+ iris.exceptions.IrisMaskValueMatchWarning,
+ iris.exceptions.IrisSaveWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
class CFNameCoordMap:
"""Provide a simple CF name to CF coordinate mapping."""
@@ -308,7 +320,12 @@ def _data_fillvalue_check(arraylib, data, check_value):
return is_masked, contains_value
-class SaverFillValueWarning(UserWarning):
+class SaverFillValueWarning(iris.exceptions.IrisSaverFillValueWarning):
+ """
+ Backwards compatible form of :class:`iris.exceptions.IrisSaverFillValueWarning`.
+ """
+
+ # TODO: remove at the next major release.
pass
@@ -359,7 +376,10 @@ def _fillvalue_report(fill_info, is_masked, contains_fill_value, warn=False):
)
if warn and result is not None:
- warnings.warn(result)
+ warnings.warn(
+ result,
+ category=_WarnComboMaskSave,
+ )
return result
@@ -733,7 +753,7 @@ def write(
msg = "cf_profile is available but no {} defined.".format(
"cf_patch"
)
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisCfSaveWarning)
@staticmethod
def check_attribute_compliance(container, data_dtype):
@@ -992,7 +1012,7 @@ def _add_inner_related_vars(
for element in sorted(
coordlike_elements, key=lambda element: element.name()
):
- # Re-use, or create, the associated CF-netCDF variable.
+ # Reuse, or create, the associated CF-netCDF variable.
cf_name = self._name_coord_map.name(element)
if cf_name is None:
# Not already present : create it
@@ -1144,7 +1164,7 @@ def _add_aux_factories(self, cube, cf_var_cube, dimension_names):
"Unable to determine formula terms "
"for AuxFactory: {!r}".format(factory)
)
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisSaveWarning)
else:
# Override `standard_name`, `long_name`, and `axis` of the
# primary coord that signals the presence of a dimensionless
@@ -2126,7 +2146,10 @@ def add_ellipsoid(ellipsoid):
# osgb (a specific tmerc)
elif isinstance(cs, iris.coord_systems.OSGB):
- warnings.warn("OSGB coordinate system not yet handled")
+ warnings.warn(
+ "OSGB coordinate system not yet handled",
+ category=iris.exceptions.IrisSaveWarning,
+ )
# lambert azimuthal equal area
elif isinstance(
@@ -2190,12 +2213,41 @@ def add_ellipsoid(ellipsoid):
)
cf_var_grid.sweep_angle_axis = cs.sweep_angle_axis
+ # oblique mercator (and rotated variant)
+ # Use duck-typing over isinstance() - subclasses (i.e.
+ # RotatedMercator) upset mock tests.
+ elif (
+ getattr(cs, "grid_mapping_name", None)
+ == "oblique_mercator"
+ ):
+ # RotatedMercator subclasses ObliqueMercator, and RM
+ # instances are implicitly saved as OM due to inherited
+ # properties. This is correct because CF 1.11 is removing
+ # all mention of RM.
+ if cs.ellipsoid:
+ add_ellipsoid(cs.ellipsoid)
+ cf_var_grid.azimuth_of_central_line = (
+ cs.azimuth_of_central_line
+ )
+ cf_var_grid.latitude_of_projection_origin = (
+ cs.latitude_of_projection_origin
+ )
+ cf_var_grid.longitude_of_projection_origin = (
+ cs.longitude_of_projection_origin
+ )
+ cf_var_grid.false_easting = cs.false_easting
+ cf_var_grid.false_northing = cs.false_northing
+ cf_var_grid.scale_factor_at_projection_origin = (
+ cs.scale_factor_at_projection_origin
+ )
+
# other
else:
warnings.warn(
"Unable to represent the horizontal "
"coordinate system. The coordinate system "
- "type %r is not yet implemented." % type(cs)
+ "type %r is not yet implemented." % type(cs),
+ category=iris.exceptions.IrisSaveWarning,
)
self._coord_systems.append(cs)
@@ -2359,7 +2411,7 @@ def set_packing_ncattrs(cfvar):
"attribute, but {attr_name!r} should only be a CF "
"global attribute.".format(attr_name=attr_name)
)
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisCfSaveWarning)
_setncattr(cf_var, attr_name, value)
@@ -2593,7 +2645,9 @@ def complete(self, issue_warnings=True) -> List[Warning]:
if issue_warnings:
# Issue any delayed warnings from the compute.
for delayed_warning in result_warnings:
- warnings.warn(delayed_warning)
+ warnings.warn(
+ delayed_warning, category=iris.exceptions.IrisSaveWarning
+ )
return result_warnings
@@ -2911,7 +2965,7 @@ def is_valid_packspec(p):
msg = "cf_profile is available but no {} defined.".format(
"cf_patch_conventions"
)
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisCfSaveWarning)
# Add conventions attribute.
sman.update_global_attributes(Conventions=conventions)
diff --git a/lib/iris/fileformats/nimrod_load_rules.py b/lib/iris/fileformats/nimrod_load_rules.py
index fd1ccb0e958..17db0644ee9 100644
--- a/lib/iris/fileformats/nimrod_load_rules.py
+++ b/lib/iris/fileformats/nimrod_load_rules.py
@@ -16,7 +16,11 @@
import iris
import iris.coord_systems
from iris.coords import DimCoord
-from iris.exceptions import CoordinateNotFoundError, TranslationError
+from iris.exceptions import (
+ CoordinateNotFoundError,
+ IrisNimrodTranslationWarning,
+ TranslationError,
+)
__all__ = ["run"]
@@ -28,7 +32,12 @@
)
-class TranslationWarning(Warning):
+class TranslationWarning(IrisNimrodTranslationWarning):
+ """
+ Backwards compatible form of :class:`iris.exceptions.IrisNimrodTranslationWarning`.
+ """
+
+ # TODO: remove at the next major release.
pass
@@ -181,7 +190,8 @@ def units(cube, field):
warnings.warn(
"Unhandled units '{0}' recorded in cube attributes.".format(
field_units
- )
+ ),
+ category=IrisNimrodTranslationWarning,
)
cube.attributes["invalid_units"] = field_units
@@ -417,7 +427,8 @@ def coord_system(field, handle_metadata_errors):
if any([is_missing(field, v) for v in crs_args]):
warnings.warn(
"Coordinate Reference System is not completely defined. "
- "Plotting and reprojection may be impaired."
+ "Plotting and reprojection may be impaired.",
+ category=IrisNimrodTranslationWarning,
)
coord_sys = iris.coord_systems.TransverseMercator(
*crs_args, iris.coord_systems.GeogCS(**ellipsoid)
@@ -539,7 +550,7 @@ def vertical_coord(cube, field):
f"{field.vertical_coord_type} != {field.reference_vertical_coord_type}. "
f"Assuming {field.vertical_coord_type}"
)
- warnings.warn(msg)
+ warnings.warn(msg, category=IrisNimrodTranslationWarning)
coord_point = field.vertical_coord
if coord_point == 8888.0:
@@ -586,7 +597,7 @@ def vertical_coord(cube, field):
warnings.warn(
"Vertical coord {!r} not yet handled"
"".format(field.vertical_coord_type),
- TranslationWarning,
+ category=TranslationWarning,
)
@@ -831,7 +842,8 @@ def probability_coord(cube, field, handle_metadata_errors):
)
warnings.warn(
f"No default units for {coord_name} coord of {cube.name()}. "
- "Meta-data may be incomplete."
+ "Meta-data may be incomplete.",
+ category=IrisNimrodTranslationWarning,
)
new_coord = iris.coords.AuxCoord(
np.array(coord_val, dtype=np.float32), bounds=bounds, **coord_keys
diff --git a/lib/iris/fileformats/pp.py b/lib/iris/fileformats/pp.py
index 65e0e16d725..e19ba3adfff 100644
--- a/lib/iris/fileformats/pp.py
+++ b/lib/iris/fileformats/pp.py
@@ -27,6 +27,7 @@
from iris._lazy_data import as_concrete_data, as_lazy_data, is_lazy_data
import iris.config
import iris.coord_systems
+import iris.exceptions
# NOTE: this is for backwards-compatitibility *ONLY*
# We could simply remove it for v2.0 ?
@@ -220,6 +221,33 @@
}
+class _WarnComboLoadingMask(
+ iris.exceptions.IrisLoadWarning,
+ iris.exceptions.IrisMaskValueMatchWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboLoadingDefaulting(
+ iris.exceptions.IrisDefaultingWarning,
+ iris.exceptions.IrisLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
+class _WarnComboIgnoringLoad(
+ iris.exceptions.IrisIgnoringWarning,
+ iris.exceptions.IrisLoadWarning,
+):
+ """One-off combination of warning classes - enhances user filtering."""
+
+ pass
+
+
class STASH(collections.namedtuple("STASH", "model section item")):
"""
A class to hold a single STASH code.
@@ -1165,7 +1193,10 @@ def save(self, file_handle):
"missing data. To save these as normal values, please "
"set the field BMDI not equal to any valid data points."
)
- warnings.warn(msg.format(mdi))
+ warnings.warn(
+ msg.format(mdi),
+ category=_WarnComboLoadingMask,
+ )
if isinstance(data, ma.MaskedArray):
if ma.is_masked(data):
data = data.filled(fill_value=mdi)
@@ -1290,7 +1321,8 @@ def save(self, file_handle):
warnings.warn(
"Downcasting array precision from float64 to float32"
" for save.If float64 precision is required then"
- " please save in a different format"
+ " please save in a different format",
+ category=_WarnComboLoadingDefaulting,
)
data = data.astype(">f4")
lb[self.HEADER_DICT["lbuser"][0]] = 1
@@ -1732,7 +1764,8 @@ def _interpret_fields(fields):
warnings.warn(
"Landmask compressed fields existed without a "
"landmask to decompress with. The data will have "
- "a shape of (0, 0) and will not read."
+ "a shape of (0, 0) and will not read.",
+ category=iris.exceptions.IrisLoadWarning,
)
mask_shape = (0, 0)
else:
@@ -1901,7 +1934,10 @@ def _field_gen(filename, read_data_bytes, little_ended=False):
"Unable to interpret field {}. {}. Skipping "
"the remainder of the file.".format(field_count, str(e))
)
- warnings.warn(msg)
+ warnings.warn(
+ msg,
+ category=_WarnComboIgnoringLoad,
+ )
break
# Skip the trailing 4-byte word containing the header length
@@ -1921,7 +1957,8 @@ def _field_gen(filename, read_data_bytes, little_ended=False):
warnings.warn(
wmsg.format(
pp_field.lblrec * PP_WORD_DEPTH, len_of_data_plus_extra
- )
+ ),
+ category=_WarnComboIgnoringLoad,
)
break
diff --git a/lib/iris/fileformats/pp_save_rules.py b/lib/iris/fileformats/pp_save_rules.py
index 0369fc9fd0f..998255ff2bf 100644
--- a/lib/iris/fileformats/pp_save_rules.py
+++ b/lib/iris/fileformats/pp_save_rules.py
@@ -10,6 +10,7 @@
import iris
from iris.aux_factory import HybridHeightFactory, HybridPressureFactory
+from iris.exceptions import IrisPpClimModifiedWarning
from iris.fileformats._ff_cross_references import STASH_TRANS
from iris.fileformats._pp_lbproc_pairs import LBPROC_MAP
from iris.fileformats.rules import (
@@ -890,4 +891,4 @@ def verify(cube, field):
def _conditional_warning(condition, warning):
if condition:
- warnings.warn(warning)
+ warnings.warn(warning, category=IrisPpClimModifiedWarning)
diff --git a/lib/iris/fileformats/rules.py b/lib/iris/fileformats/rules.py
index 707fd587576..d5a4b9c8234 100644
--- a/lib/iris/fileformats/rules.py
+++ b/lib/iris/fileformats/rules.py
@@ -47,7 +47,8 @@ def as_cube(self):
src_cubes = src_cubes.merge(unique=False)
if len(src_cubes) > 1:
warnings.warn(
- "Multiple reference cubes for {}".format(self.name)
+ "Multiple reference cubes for {}".format(self.name),
+ category=iris.exceptions.IrisUserWarning,
)
src_cube = src_cubes[-1]
@@ -329,7 +330,7 @@ def _make_cube(field, converter):
cube.units = metadata.units
except ValueError:
msg = "Ignoring PP invalid units {!r}".format(metadata.units)
- warnings.warn(msg)
+ warnings.warn(msg, category=iris.exceptions.IrisIgnoringWarning)
cube.attributes["invalid_units"] = metadata.units
cube.units = cf_units._UNKNOWN_UNIT_STRING
@@ -350,7 +351,10 @@ def _resolve_factory_references(
except _ReferenceError as e:
msg = "Unable to create instance of {factory}. " + str(e)
factory_name = factory.factory_class.__name__
- warnings.warn(msg.format(factory=factory_name))
+ warnings.warn(
+ msg.format(factory=factory_name),
+ category=iris.exceptions.IrisUserWarning,
+ )
else:
aux_factory = factory.factory_class(*args)
cube.add_aux_factory(aux_factory)
diff --git a/lib/iris/iterate.py b/lib/iris/iterate.py
index cf16c9cbe65..cc82433e852 100644
--- a/lib/iris/iterate.py
+++ b/lib/iris/iterate.py
@@ -14,6 +14,8 @@
import numpy as np
+from iris.exceptions import IrisUserWarning
+
__all__ = ["izip"]
@@ -164,7 +166,8 @@ def izip(*cubes, **kwargs):
warnings.warn(
"Iterating over coordinate '%s' in step whose "
"definitions match but whose values "
- "differ." % coord_a.name()
+ "differ." % coord_a.name(),
+ category=IrisUserWarning,
)
return _ZipSlicesIterator(
diff --git a/lib/iris/pandas.py b/lib/iris/pandas.py
index 4d6681e94e1..0d0e65d648a 100644
--- a/lib/iris/pandas.py
+++ b/lib/iris/pandas.py
@@ -29,6 +29,7 @@
from iris._deprecation import warn_deprecated
from iris.coords import AncillaryVariable, AuxCoord, CellMeasure, DimCoord
from iris.cube import Cube, CubeList
+from iris.exceptions import IrisIgnoringWarning
def _get_dimensional_metadata(name, values, calendar=None, dm_class=None):
@@ -82,7 +83,7 @@ def _add_iris_coord(cube, name, points, dim, calendar=None):
Add a Coord or other dimensional metadata to a Cube from a Pandas index or columns array.
"""
# Most functionality has been abstracted to _get_dimensional_metadata,
- # allowing re-use in as_cube() and as_cubes().
+ # allowing reuse in as_cube() and as_cubes().
coord = _get_dimensional_metadata(name, points, calendar)
if coord.__class__ == DimCoord:
@@ -398,7 +399,7 @@ def as_cubes(
cube_shape = getattr(pandas_index, "levshape", (pandas_index.nunique(),))
n_rows = len(pandas_structure)
- if np.product(cube_shape) > n_rows:
+ if np.prod(cube_shape) > n_rows:
message = (
f"Not all index values have a corresponding row - {n_rows} rows "
f"cannot be reshaped into {cube_shape}. Consider padding with NaN "
@@ -446,7 +447,7 @@ def format_dimensional_metadata(dm_class_, values_, name_, dimensions_):
if columns_ignored:
ignored_args = ", ".join([t[2] for t in class_arg_mapping])
message = f"The input pandas_structure is a Series; ignoring arguments: {ignored_args} ."
- warnings.warn(message)
+ warnings.warn(message, category=IrisIgnoringWarning)
class_arg_mapping = []
non_data_names = []
@@ -896,7 +897,7 @@ def merge_metadata(meta_var_list):
"'iris.FUTURE.pandas_ndim = True'. More info is in the "
"documentation."
)
- warnings.warn(message, FutureWarning)
+ warnings.warn(message, category=FutureWarning)
# The legacy behaviour.
data = cube.data
diff --git a/lib/iris/plot.py b/lib/iris/plot.py
index ebcb5c3bcba..28b458f715f 100644
--- a/lib/iris/plot.py
+++ b/lib/iris/plot.py
@@ -34,7 +34,7 @@
import iris.coord_systems
import iris.coords
import iris.cube
-from iris.exceptions import IrisError
+from iris.exceptions import IrisError, IrisUnsupportedPlottingWarning
# Importing iris.palette to register the brewer palettes.
import iris.palette
@@ -2023,7 +2023,7 @@ def update_animation_iris(i, cubes, vmin, vmax, coords):
"use: {}."
)
msg = msg.format(plot_func.__module__, supported)
- warnings.warn(msg, UserWarning)
+ warnings.warn(msg, category=IrisUnsupportedPlottingWarning)
supported = ["contour", "contourf", "pcolor", "pcolormesh"]
if plot_func.__name__ not in supported:
@@ -2032,7 +2032,7 @@ def update_animation_iris(i, cubes, vmin, vmax, coords):
"use: {}."
)
msg = msg.format(plot_func.__name__, supported)
- warnings.warn(msg, UserWarning)
+ warnings.warn(msg, category=IrisUnsupportedPlottingWarning)
# Determine plot range.
vmin = kwargs.pop("vmin", min([cc.data.min() for cc in cubes]))
diff --git a/lib/iris/tests/graphics/idiff.py b/lib/iris/tests/graphics/idiff.py
index 62e72f4e0e0..4af7f4726d8 100755
--- a/lib/iris/tests/graphics/idiff.py
+++ b/lib/iris/tests/graphics/idiff.py
@@ -28,6 +28,7 @@
from matplotlib.testing.exceptions import ImageComparisonFailure # noqa
import matplotlib.widgets as mwidget # noqa
+from iris.exceptions import IrisIgnoringWarning # noqa
import iris.tests # noqa
import iris.tests.graphics as graphics # noqa
@@ -151,7 +152,7 @@ def step_over_diffs(result_dir, display=True):
distance = graphics.get_phash(reference_image_path) - phash
except FileNotFoundError:
wmsg = "Ignoring unregistered test result {!r}."
- warnings.warn(wmsg.format(test_key))
+ warnings.warn(wmsg.format(test_key), category=IrisIgnoringWarning)
continue
processed = True
diff --git a/lib/iris/tests/integration/experimental/test_ugrid_load.py b/lib/iris/tests/integration/experimental/test_ugrid_load.py
index b0b60ee506d..d94e85d2f59 100644
--- a/lib/iris/tests/integration/experimental/test_ugrid_load.py
+++ b/lib/iris/tests/integration/experimental/test_ugrid_load.py
@@ -19,6 +19,7 @@
import pytest
from iris import Constraint, load
+from iris.exceptions import IrisCfWarning
from iris.experimental.ugrid.load import (
PARSE_UGRID_ON_LOAD,
load_mesh,
@@ -170,7 +171,7 @@ def create_synthetic_file(self, **create_kwargs):
def test_mesh_bad_topology_dimension(self):
# Check that the load generates a suitable warning.
warn_regex = r"topology_dimension.* ignoring"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(IrisCfWarning, match=warn_regex):
template = "minimal_bad_topology_dim"
dim_line = "mesh_var:topology_dimension = 1 ;" # which is wrong !
cube = self.create_synthetic_test_cube(
@@ -183,7 +184,7 @@ def test_mesh_bad_topology_dimension(self):
def test_mesh_no_topology_dimension(self):
# Check that the load generates a suitable warning.
warn_regex = r"Mesh variable.* has no 'topology_dimension'"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(IrisCfWarning, match=warn_regex):
template = "minimal_bad_topology_dim"
dim_line = "" # don't create ANY topology_dimension property
cube = self.create_synthetic_test_cube(
@@ -196,7 +197,7 @@ def test_mesh_no_topology_dimension(self):
def test_mesh_bad_cf_role(self):
# Check that the load generates a suitable warning.
warn_regex = r"inappropriate cf_role"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(IrisCfWarning, match=warn_regex):
template = "minimal_bad_mesh_cf_role"
dim_line = 'mesh_var:cf_role = "foo" ;'
_ = self.create_synthetic_test_cube(
@@ -206,7 +207,7 @@ def test_mesh_bad_cf_role(self):
def test_mesh_no_cf_role(self):
# Check that the load generates a suitable warning.
warn_regex = r"no cf_role attribute"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(IrisCfWarning, match=warn_regex):
template = "minimal_bad_mesh_cf_role"
dim_line = ""
_ = self.create_synthetic_test_cube(
diff --git a/lib/iris/tests/integration/netcdf/test_delayed_save.py b/lib/iris/tests/integration/netcdf/test_delayed_save.py
index 616feb3b0e6..09f6235aab6 100644
--- a/lib/iris/tests/integration/netcdf/test_delayed_save.py
+++ b/lib/iris/tests/integration/netcdf/test_delayed_save.py
@@ -17,8 +17,8 @@
import pytest
import iris
+from iris.exceptions import IrisSaverFillValueWarning
from iris.fileformats.netcdf._thread_safe_nc import default_fillvals
-from iris.fileformats.netcdf.saver import SaverFillValueWarning
import iris.tests
from iris.tests.stock import realistic_4d
@@ -311,7 +311,7 @@ def test_fill_warnings(self, warning_type, output_path, save_is_delayed):
result_warnings = [
log.message
for log in logged_warnings
- if isinstance(log.message, SaverFillValueWarning)
+ if isinstance(log.message, IrisSaverFillValueWarning)
]
if save_is_delayed:
@@ -320,7 +320,9 @@ def test_fill_warnings(self, warning_type, output_path, save_is_delayed):
# Complete the operation now
with warnings.catch_warnings():
# NOTE: warnings should *not* be issued here, instead they are returned.
- warnings.simplefilter("error", category=SaverFillValueWarning)
+ warnings.simplefilter(
+ "error", category=IrisSaverFillValueWarning
+ )
result_warnings = result.compute()
# Either way, we should now have 2 similar warnings.
diff --git a/lib/iris/tests/integration/netcdf/test_general.py b/lib/iris/tests/integration/netcdf/test_general.py
index dc0c29455f5..6214f09e7e6 100644
--- a/lib/iris/tests/integration/netcdf/test_general.py
+++ b/lib/iris/tests/integration/netcdf/test_general.py
@@ -25,7 +25,7 @@
from iris.coords import CellMethod
from iris.cube import Cube, CubeList
import iris.exceptions
-from iris.fileformats.netcdf import Saver, UnknownCellMethodWarning
+from iris.fileformats.netcdf import Saver
# Get the netCDF4 module, but in a sneaky way that avoids triggering the "do not import
# netCDF4" check in "iris.tests.test_coding_standards.test_netcdf4_import()".
@@ -141,7 +141,9 @@ def test_unknown_method(self):
warning_messages = [
warn
for warn in warning_messages
- if isinstance(warn, UnknownCellMethodWarning)
+ if isinstance(
+ warn, iris.exceptions.IrisUnknownCellMethodWarning
+ )
]
self.assertEqual(len(warning_messages), 1)
message = warning_messages[0].args[0]
diff --git a/lib/iris/tests/integration/netcdf/test_self_referencing.py b/lib/iris/tests/integration/netcdf/test_self_referencing.py
index 3395296e11e..554fabb4fcf 100644
--- a/lib/iris/tests/integration/netcdf/test_self_referencing.py
+++ b/lib/iris/tests/integration/netcdf/test_self_referencing.py
@@ -16,6 +16,7 @@
import numpy as np
import iris
+from iris.exceptions import IrisCfMissingVarWarning
from iris.fileformats.netcdf import _thread_safe_nc
@@ -46,7 +47,9 @@ def test_cmip6_volcello_load_issue_3367(self):
with mock.patch("warnings.warn") as warn:
# ensure file loads without failure
cube = iris.load_cube(self.fname)
- warn.assert_has_calls([mock.call(expected_msg)])
+ warn.assert_has_calls(
+ [mock.call(expected_msg, category=IrisCfMissingVarWarning)]
+ )
# extra check to ensure correct variable was found
assert cube.standard_name == "ocean_volume"
@@ -113,7 +116,9 @@ def test_self_referencing_load_issue_3367(self):
with mock.patch("warnings.warn") as warn:
# ensure file loads without failure
cube = iris.load_cube(self.temp_dir_path)
- warn.assert_called_with(expected_msg)
+ warn.assert_called_with(
+ expected_msg, category=IrisCfMissingVarWarning
+ )
# extra check to ensure correct variable was found
assert cube.standard_name == "ocean_volume"
diff --git a/lib/iris/tests/integration/netcdf/test_thread_safety.py b/lib/iris/tests/integration/netcdf/test_thread_safety.py
index 5ed32d06718..c5779250a21 100644
--- a/lib/iris/tests/integration/netcdf/test_thread_safety.py
+++ b/lib/iris/tests/integration/netcdf/test_thread_safety.py
@@ -38,7 +38,7 @@ def tiny_chunks():
def _check_tiny_loaded_chunks(cube: Cube):
assert cube.has_lazy_data()
cube_lazy_data = cube.core_data()
- assert np.product(cube_lazy_data.chunksize) < cube_lazy_data.size
+ assert np.prod(cube_lazy_data.chunksize) < cube_lazy_data.size
with dask.config.set({"array.chunk-size": "1KiB"}):
yield _check_tiny_loaded_chunks
diff --git a/lib/iris/tests/integration/test_pp.py b/lib/iris/tests/integration/test_pp.py
index e654694aa72..026bdae58a9 100644
--- a/lib/iris/tests/integration/test_pp.py
+++ b/lib/iris/tests/integration/test_pp.py
@@ -18,7 +18,7 @@
from iris.aux_factory import HybridHeightFactory, HybridPressureFactory
from iris.coords import AuxCoord, CellMethod, DimCoord
from iris.cube import Cube
-from iris.exceptions import IgnoreCubeException
+from iris.exceptions import IgnoreCubeException, IrisUserWarning
import iris.fileformats.pp
from iris.fileformats.pp import load_pairs_from_fields
import iris.fileformats.pp_load_rules
@@ -290,7 +290,7 @@ def test_hybrid_pressure_with_duplicate_references(self):
"iris.fileformats.pp.load", new=load
) as load, mock.patch("warnings.warn") as warn:
_, _, _ = iris.fileformats.pp.load_cubes("DUMMY")
- warn.assert_called_with(msg)
+ warn.assert_called_with(msg, category=IrisUserWarning)
def test_hybrid_height_with_non_standard_coords(self):
# Check the save rules are using the AuxFactory to find the
@@ -415,7 +415,7 @@ def test_hybrid_height_round_trip_no_reference(self):
"Unable to create instance of HybridHeightFactory. "
"The source data contains no field(s) for 'orography'."
)
- warn.assert_called_with(msg)
+ warn.assert_called_with(msg, category=IrisUserWarning)
# Check the data cube is set up to use hybrid height.
self._test_coord(
diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json
index 2313c25270f..2a6e2c4dbc5 100644
--- a/lib/iris/tests/results/imagerepo.json
+++ b/lib/iris/tests/results/imagerepo.json
@@ -1,6 +1,6 @@
{
"gallery_tests.test_plot_COP_1d.0": "aefec91c3601249cc9b3336dc4c8cdb31a64c6d997b3c0eccb5932d285e42f33",
- "gallery_tests.test_plot_COP_maps.0": "ea9130db95668524913e6ac168991f0d956e917ec76396b96a853dcf94696935",
+ "gallery_tests.test_plot_COP_maps.0": "ea91789995668566913e43474adb6a917e8d947c4b46957ec6716a91958e6f81",
"gallery_tests.test_plot_SOI_filtering.0": "fa56f295c5e0694a3c17a58d95e8da536233da99984c5af4c6739b4a9a444eb4",
"gallery_tests.test_plot_TEC.0": "e5a761b69a589a4bc46f9e48c65c6631ce61d1ce3982c13739b33193c0ee3f8c",
"gallery_tests.test_plot_anomaly_log_colouring.0": "ec4464e384a39b13931a9b1c85696da968d5e6e63e26847bdbd399938d3c5a4c",
diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py
index 6cea9dc0014..54309e3906b 100644
--- a/lib/iris/tests/test_coding_standards.py
+++ b/lib/iris/tests/test_coding_standards.py
@@ -8,6 +8,7 @@
# importing anything else
import iris.tests as tests # isort:skip
+import ast
from datetime import datetime
from fnmatch import fnmatch
from glob import glob
@@ -133,6 +134,66 @@ def test_python_versions():
assert search in path.read_text()
+def test_categorised_warnings():
+ """
+ To ensure that all UserWarnings raised by Iris are categorised, for ease of use.
+
+ No obvious category? Use the parent:
+ :class:`iris.exceptions.IrisUserWarning`.
+
+ Warning matches multiple categories? Create a one-off combo class. For
+ example:
+
+ .. code-block:: python
+
+ class _WarnComboCfDefaulting(IrisCfWarning, IrisDefaultingWarning):
+ \"""
+ One-off combination of warning classes - enhances user filtering.
+ \"""
+ pass
+
+ """
+ warns_without_category = []
+ warns_with_user_warning = []
+ tmp_list = []
+
+ for file_path in Path(IRIS_DIR).rglob("*.py"):
+ file_text = file_path.read_text()
+ parsed = ast.parse(source=file_text)
+ calls = filter(lambda node: hasattr(node, "func"), ast.walk(parsed))
+ warn_calls = filter(
+ lambda c: getattr(c.func, "attr", None) == "warn", calls
+ )
+
+ warn_call: ast.Call
+ for warn_call in warn_calls:
+ warn_ref = f"{file_path}:{warn_call.lineno}"
+ tmp_list.append(warn_ref)
+
+ category_kwargs = filter(
+ lambda k: k.arg == "category", warn_call.keywords
+ )
+ category_kwarg: ast.keyword = next(category_kwargs, None)
+
+ if category_kwarg is None:
+ warns_without_category.append(warn_ref)
+ # Work with Attribute or Name instances.
+ elif (
+ getattr(category_kwarg.value, "attr", None)
+ or getattr(category_kwarg.value, "id", None)
+ ) == "UserWarning":
+ warns_with_user_warning.append(warn_ref)
+
+ # This avoids UserWarnings being raised by unwritten default behaviour.
+ assert (
+ warns_without_category == []
+ ), "All warnings raised by Iris must be raised with the category kwarg."
+
+ assert (
+ warns_with_user_warning == []
+ ), "No warnings raised by Iris can be the base UserWarning class."
+
+
class TestLicenseHeaders(tests.IrisTest):
@staticmethod
def whatchanged_parse(whatchanged_output):
diff --git a/lib/iris/tests/test_concatenate.py b/lib/iris/tests/test_concatenate.py
index 9287a79fda9..ec92838466a 100644
--- a/lib/iris/tests/test_concatenate.py
+++ b/lib/iris/tests/test_concatenate.py
@@ -20,6 +20,7 @@
from iris.aux_factory import HybridHeightFactory
from iris.coords import AncillaryVariable, AuxCoord, CellMeasure, DimCoord
import iris.cube
+from iris.exceptions import IrisUserWarning
import iris.tests.stock as stock
@@ -340,7 +341,8 @@ def test_points_overlap_increasing(self):
cubes.append(_make_cube((0, 2), y, 1))
cubes.append(_make_cube((1, 3), y, 2))
with pytest.warns(
- UserWarning, match="Found cubes with overlap on concatenate axis"
+ IrisUserWarning,
+ match="Found cubes with overlap on concatenate axis",
):
result = concatenate(cubes)
self.assertEqual(len(result), 2)
@@ -351,7 +353,8 @@ def test_points_overlap_decreasing(self):
cubes.append(_make_cube(x, (3, 0, -1), 1))
cubes.append(_make_cube(x, (1, -1, -1), 2))
with pytest.warns(
- UserWarning, match="Found cubes with overlap on concatenate axis"
+ IrisUserWarning,
+ match="Found cubes with overlap on concatenate axis",
):
result = concatenate(cubes)
self.assertEqual(len(result), 2)
@@ -366,7 +369,8 @@ def test_bounds_overlap_increasing(self):
)
cubes.append(cube)
with pytest.warns(
- UserWarning, match="Found cubes with overlap on concatenate axis"
+ IrisUserWarning,
+ match="Found cubes with overlap on concatenate axis",
):
result = concatenate(cubes)
self.assertEqual(len(result), 2)
@@ -381,7 +385,8 @@ def test_bounds_overlap_decreasing(self):
)
cubes.append(cube)
with pytest.warns(
- UserWarning, match="Found cubes with overlap on concatenate axis"
+ IrisUserWarning,
+ match="Found cubes with overlap on concatenate axis",
):
result = concatenate(cubes)
self.assertEqual(len(result), 2)
diff --git a/lib/iris/tests/test_coordsystem.py b/lib/iris/tests/test_coordsystem.py
index 7cd15297cc8..2e5aef249ce 100644
--- a/lib/iris/tests/test_coordsystem.py
+++ b/lib/iris/tests/test_coordsystem.py
@@ -18,6 +18,7 @@
)
import iris.coords
import iris.cube
+from iris.exceptions import IrisUserWarning
import iris.tests.stock
@@ -341,7 +342,7 @@ def test_inverse_flattening_change(self):
cs = GeogCS(6543210, 6500000)
initial_crs = cs.as_cartopy_crs()
with self.assertWarnsRegex(
- UserWarning,
+ IrisUserWarning,
"Setting inverse_flattening does not affect other properties of the GeogCS object.",
):
cs.inverse_flattening = cs.inverse_flattening + 1
diff --git a/lib/iris/tests/test_hybrid.py b/lib/iris/tests/test_hybrid.py
index 76fc971a083..b070f36a7ab 100644
--- a/lib/iris/tests/test_hybrid.py
+++ b/lib/iris/tests/test_hybrid.py
@@ -18,6 +18,7 @@
import iris
from iris.aux_factory import HybridHeightFactory, HybridPressureFactory
+from iris.exceptions import IrisIgnoringBoundsWarning
import iris.tests.stock
@@ -136,7 +137,7 @@ def test_invalid_dependencies(self):
with warnings.catch_warnings():
# Cause all warnings to raise Exceptions
warnings.simplefilter("error")
- with self.assertRaises(UserWarning):
+ with self.assertRaises(IrisIgnoringBoundsWarning):
_ = HybridHeightFactory(orography=sigma)
def test_bounded_orography(self):
@@ -154,7 +155,7 @@ def test_bounded_orography(self):
with warnings.catch_warnings():
# Cause all warnings to raise Exceptions
warnings.simplefilter("error")
- with self.assertRaisesRegex(UserWarning, msg):
+ with self.assertRaisesRegex(IrisIgnoringBoundsWarning, msg):
self.cube.coord("altitude")
@@ -215,7 +216,7 @@ def test_invalid_dependencies(self):
with warnings.catch_warnings():
# Cause all warnings to raise Exceptions
warnings.simplefilter("error")
- with self.assertRaises(UserWarning):
+ with self.assertRaises(IrisIgnoringBoundsWarning):
_ = HybridPressureFactory(
sigma=sigma, surface_air_pressure=sigma
)
@@ -235,7 +236,7 @@ def test_bounded_surface_pressure(self):
with warnings.catch_warnings():
# Cause all warnings to raise Exceptions
warnings.simplefilter("error")
- with self.assertRaisesRegex(UserWarning, msg):
+ with self.assertRaisesRegex(IrisIgnoringBoundsWarning, msg):
self.cube.coord("air_pressure")
diff --git a/lib/iris/tests/test_iterate.py b/lib/iris/tests/test_iterate.py
index ec86d2f69d3..6317ef32b58 100644
--- a/lib/iris/tests/test_iterate.py
+++ b/lib/iris/tests/test_iterate.py
@@ -22,6 +22,7 @@
import iris
import iris.analysis
+from iris.exceptions import IrisUserWarning
import iris.iterate
import iris.tests.stock
@@ -365,12 +366,12 @@ def test_izip_different_valued_coords(self):
warnings.simplefilter(
"error"
) # Cause all warnings to raise Exceptions
- with self.assertRaises(UserWarning):
+ with self.assertRaises(IrisUserWarning):
iris.iterate.izip(
self.cube_a, self.cube_b, coords=self.coord_names
)
# Call with coordinates, rather than names
- with self.assertRaises(UserWarning):
+ with self.assertRaises(IrisUserWarning):
iris.iterate.izip(
self.cube_a, self.cube_b, coords=[latitude, longitude]
)
diff --git a/lib/iris/tests/test_netcdf.py b/lib/iris/tests/test_netcdf.py
index 6438140ed96..2e389942bf3 100644
--- a/lib/iris/tests/test_netcdf.py
+++ b/lib/iris/tests/test_netcdf.py
@@ -26,6 +26,7 @@
from iris._lazy_data import is_lazy_data
import iris.analysis.trajectory
import iris.coord_systems as icoord_systems
+from iris.exceptions import IrisCfSaveWarning
from iris.fileformats._nc_load_rules import helpers as ncload_helpers
import iris.fileformats.netcdf
from iris.fileformats.netcdf import _thread_safe_nc
@@ -1099,7 +1100,9 @@ def test_conflicting_global_attributes(self):
with self.temp_filename(suffix=".nc") as filename:
with mock.patch("warnings.warn") as warn:
iris.save([self.cube, self.cube2], filename)
- warn.assert_called_with(expected_msg)
+ warn.assert_called_with(
+ expected_msg, category=IrisCfSaveWarning
+ )
self.assertCDL(
filename, ("netcdf", "netcdf_save_confl_global_attr.cdl")
)
diff --git a/lib/iris/tests/unit/analysis/cartography/test_project.py b/lib/iris/tests/unit/analysis/cartography/test_project.py
index 8649cc55ea0..c00830aaccc 100644
--- a/lib/iris/tests/unit/analysis/cartography/test_project.py
+++ b/lib/iris/tests/unit/analysis/cartography/test_project.py
@@ -16,6 +16,7 @@
import iris.coord_systems
import iris.coords
import iris.cube
+from iris.exceptions import IrisDefaultingWarning
import iris.tests
import iris.tests.stock
@@ -161,7 +162,8 @@ def test_no_coord_system(self):
warn.assert_called_once_with(
"Coordinate system of latitude and "
"longitude coordinates is not specified. "
- "Assuming WGS84 Geodetic."
+ "Assuming WGS84 Geodetic.",
+ category=IrisDefaultingWarning,
)
diff --git a/lib/iris/tests/unit/analysis/geometry/test_geometry_area_weights.py b/lib/iris/tests/unit/analysis/geometry/test_geometry_area_weights.py
index 49e03a1174e..62ab1ae2832 100644
--- a/lib/iris/tests/unit/analysis/geometry/test_geometry_area_weights.py
+++ b/lib/iris/tests/unit/analysis/geometry/test_geometry_area_weights.py
@@ -21,6 +21,7 @@
from iris.analysis.geometry import geometry_area_weights
from iris.coords import DimCoord
from iris.cube import Cube
+from iris.exceptions import IrisGeometryExceedWarning
import iris.tests.stock as stock
@@ -148,7 +149,9 @@ def test_distinct_xy_bounds_pole(self):
"The geometry exceeds the "
"cube's y dimension at the upper end.",
)
- self.assertTrue(issubclass(w[-1].category, UserWarning))
+ self.assertTrue(
+ issubclass(w[-1].category, IrisGeometryExceedWarning)
+ )
target = np.array(
[
[0, top_cell_half, top_cell_half, 0],
diff --git a/lib/iris/tests/unit/analysis/test_PERCENTILE.py b/lib/iris/tests/unit/analysis/test_PERCENTILE.py
index bfd3234d267..a29516c6049 100644
--- a/lib/iris/tests/unit/analysis/test_PERCENTILE.py
+++ b/lib/iris/tests/unit/analysis/test_PERCENTILE.py
@@ -94,7 +94,7 @@ class ScipyAggregateMixin:
Tests for calculations specific to the default (scipy) function. Includes
tests on masked data and tests to verify that the function is called with
the expected keywords. Needs to be used with AggregateMixin, as some of
- these tests re-use its method.
+ these tests reuse its method.
"""
diff --git a/lib/iris/tests/unit/coord_systems/test_ObliqueMercator.py b/lib/iris/tests/unit/coord_systems/test_ObliqueMercator.py
new file mode 100644
index 00000000000..0799fb881e2
--- /dev/null
+++ b/lib/iris/tests/unit/coord_systems/test_ObliqueMercator.py
@@ -0,0 +1,165 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""Unit tests for the :class:`iris.coord_systems.ObliqueMercator` class."""
+
+from typing import List, NamedTuple
+from unittest.mock import Mock
+
+from cartopy import crs as ccrs
+import pytest
+
+from iris.coord_systems import GeogCS, ObliqueMercator
+
+####
+# ALL TESTS MUST BE CONTAINED IN CLASSES, TO ENABLE INHERITANCE BY
+# test_RotatedMercator.py .
+####
+
+
+class GlobeWithEq(ccrs.Globe):
+ def __eq__(self, other):
+ """Need eq to enable comparison with expected arguments."""
+ result = NotImplemented
+ if isinstance(other, ccrs.Globe):
+ result = other.__dict__ == self.__dict__
+ return result
+
+
+class ParamTuple(NamedTuple):
+ """Used for easy coupling of test parameters."""
+
+ id: str
+ class_kwargs: dict
+ cartopy_kwargs: dict
+
+
+kwarg_permutations: List[ParamTuple] = [
+ ParamTuple(
+ "default",
+ dict(),
+ dict(),
+ ),
+ ParamTuple(
+ "azimuth",
+ dict(azimuth_of_central_line=90),
+ dict(azimuth=90),
+ ),
+ ParamTuple(
+ "central_longitude",
+ dict(longitude_of_projection_origin=90),
+ dict(central_longitude=90),
+ ),
+ ParamTuple(
+ "central_latitude",
+ dict(latitude_of_projection_origin=45),
+ dict(central_latitude=45),
+ ),
+ ParamTuple(
+ "false_easting_northing",
+ dict(false_easting=1000000, false_northing=-2000000),
+ dict(false_easting=1000000, false_northing=-2000000),
+ ),
+ ParamTuple(
+ "scale_factor",
+ # Number inherited from Cartopy's test_mercator.py .
+ dict(scale_factor_at_projection_origin=0.939692620786),
+ dict(scale_factor=0.939692620786),
+ ),
+ ParamTuple(
+ "globe",
+ dict(ellipsoid=GeogCS(1)),
+ dict(
+ globe=GlobeWithEq(semimajor_axis=1, semiminor_axis=1, ellipse=None)
+ ),
+ ),
+ ParamTuple(
+ "combo",
+ dict(
+ azimuth_of_central_line=90,
+ longitude_of_projection_origin=90,
+ latitude_of_projection_origin=45,
+ false_easting=1000000,
+ false_northing=-2000000,
+ scale_factor_at_projection_origin=0.939692620786,
+ ellipsoid=GeogCS(1),
+ ),
+ dict(
+ azimuth=90.0,
+ central_longitude=90.0,
+ central_latitude=45.0,
+ false_easting=1000000,
+ false_northing=-2000000,
+ scale_factor=0.939692620786,
+ globe=GlobeWithEq(
+ semimajor_axis=1, semiminor_axis=1, ellipse=None
+ ),
+ ),
+ ),
+]
+permutation_ids: List[str] = [p.id for p in kwarg_permutations]
+
+
+class TestArgs:
+ GeogCS = GeogCS
+ class_kwargs_default = dict(
+ azimuth_of_central_line=0.0,
+ latitude_of_projection_origin=0.0,
+ longitude_of_projection_origin=0.0,
+ )
+ cartopy_kwargs_default = dict(
+ central_longitude=0.0,
+ central_latitude=0.0,
+ false_easting=0.0,
+ false_northing=0.0,
+ scale_factor=1.0,
+ azimuth=0.0,
+ globe=None,
+ )
+
+ @pytest.fixture(
+ autouse=True, params=kwarg_permutations, ids=permutation_ids
+ )
+ def make_variant_inputs(self, request) -> None:
+ """Parse a ParamTuple into usable test information."""
+ inputs: ParamTuple = request.param
+ self.class_kwargs = dict(
+ self.class_kwargs_default, **inputs.class_kwargs
+ )
+ self.cartopy_kwargs_expected = dict(
+ self.cartopy_kwargs_default, **inputs.cartopy_kwargs
+ )
+
+ def make_instance(self) -> ObliqueMercator:
+ return ObliqueMercator(**self.class_kwargs)
+
+ @pytest.fixture()
+ def instance(self):
+ return self.make_instance()
+
+ def test_instantiate(self):
+ _ = self.make_instance()
+
+ def test_cartopy_crs(self, instance):
+ ccrs.ObliqueMercator = Mock()
+ instance.as_cartopy_crs()
+ ccrs.ObliqueMercator.assert_called_with(**self.cartopy_kwargs_expected)
+
+ def test_cartopy_projection(self, instance):
+ ccrs.ObliqueMercator = Mock()
+ instance.as_cartopy_projection()
+ ccrs.ObliqueMercator.assert_called_with(**self.cartopy_kwargs_expected)
+
+ @pytest.fixture()
+ def label_class(self, instance):
+ """Make the tested coordinate system available, even for subclasses."""
+ from iris import coord_systems
+
+ instance_class = "{!s}".format(instance.__class__.__name__)
+ globals()[instance_class] = getattr(coord_systems, instance_class)
+
+ def test_repr(self, instance, label_class):
+ """Test that the repr can be used to regenerate an identical object."""
+ assert eval(repr(instance)) == instance
diff --git a/lib/iris/tests/unit/coord_systems/test_RotatedMercator.py b/lib/iris/tests/unit/coord_systems/test_RotatedMercator.py
new file mode 100644
index 00000000000..97921efec6b
--- /dev/null
+++ b/lib/iris/tests/unit/coord_systems/test_RotatedMercator.py
@@ -0,0 +1,39 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""Unit tests for the :class:`iris.coord_systems.RotatedMercator` class."""
+
+import pytest
+
+from iris._deprecation import IrisDeprecation
+from iris.coord_systems import RotatedMercator
+
+from . import test_ObliqueMercator
+
+
+class TestArgs(test_ObliqueMercator.TestArgs):
+ class_kwargs_default = dict(
+ latitude_of_projection_origin=0.0,
+ longitude_of_projection_origin=0.0,
+ )
+ cartopy_kwargs_default = dict(
+ central_longitude=0.0,
+ central_latitude=0.0,
+ false_easting=0.0,
+ false_northing=0.0,
+ scale_factor=1.0,
+ azimuth=90.0,
+ globe=None,
+ )
+
+ def make_instance(self) -> RotatedMercator:
+ kwargs = self.class_kwargs
+ kwargs.pop("azimuth_of_central_line", None)
+ return RotatedMercator(**kwargs)
+
+
+def test_deprecated():
+ with pytest.warns(IrisDeprecation, match="azimuth_of_central_line=90"):
+ _ = RotatedMercator(0, 0)
diff --git a/lib/iris/tests/unit/coords/test_Coord.py b/lib/iris/tests/unit/coords/test_Coord.py
index 69b6b70c965..c548d017f2d 100644
--- a/lib/iris/tests/unit/coords/test_Coord.py
+++ b/lib/iris/tests/unit/coords/test_Coord.py
@@ -19,7 +19,7 @@
import iris
from iris.coords import AuxCoord, Coord, DimCoord
from iris.cube import Cube
-from iris.exceptions import UnitConversionError
+from iris.exceptions import IrisVagueMetadataWarning, UnitConversionError
from iris.tests.unit.coords import CoordTestMixin
Pair = collections.namedtuple("Pair", "points bounds")
@@ -482,7 +482,7 @@ def test_numeric_nd_multidim_bounds_warning(self):
"Collapsing a multi-dimensional coordinate. "
"Metadata may not be fully descriptive for 'y'."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
coord.collapsed()
def test_lazy_nd_multidim_bounds_warning(self):
@@ -493,7 +493,7 @@ def test_lazy_nd_multidim_bounds_warning(self):
"Collapsing a multi-dimensional coordinate. "
"Metadata may not be fully descriptive for 'y'."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
coord.collapsed()
def test_numeric_nd_noncontiguous_bounds_warning(self):
@@ -504,7 +504,7 @@ def test_numeric_nd_noncontiguous_bounds_warning(self):
"Collapsing a non-contiguous coordinate. "
"Metadata may not be fully descriptive for 'y'."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
coord.collapsed()
def test_lazy_nd_noncontiguous_bounds_warning(self):
@@ -515,7 +515,7 @@ def test_lazy_nd_noncontiguous_bounds_warning(self):
"Collapsing a non-contiguous coordinate. "
"Metadata may not be fully descriptive for 'y'."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
coord.collapsed()
def test_numeric_3_bounds(self):
@@ -530,7 +530,7 @@ def test_numeric_3_bounds(self):
r"1D coordinates with 2 bounds. Metadata may not be fully "
r"descriptive for 'x'. Ignoring bounds."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
collapsed_coord = coord.collapsed()
self.assertFalse(collapsed_coord.has_lazy_points())
@@ -553,7 +553,7 @@ def test_lazy_3_bounds(self):
r"1D coordinates with 2 bounds. Metadata may not be fully "
r"descriptive for 'x'. Ignoring bounds."
)
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisVagueMetadataWarning, msg):
collapsed_coord = coord.collapsed()
self.assertTrue(collapsed_coord.has_lazy_points())
diff --git a/lib/iris/tests/unit/cube/test_Cube.py b/lib/iris/tests/unit/cube/test_Cube.py
index a733665df89..443c9db546e 100644
--- a/lib/iris/tests/unit/cube/test_Cube.py
+++ b/lib/iris/tests/unit/cube/test_Cube.py
@@ -40,6 +40,8 @@
AncillaryVariableNotFoundError,
CellMeasureNotFoundError,
CoordinateNotFoundError,
+ IrisUserWarning,
+ IrisVagueMetadataWarning,
UnitConversionError,
)
import iris.tests.stock as stock
@@ -676,7 +678,10 @@ def _assert_warn_collapse_without_weight(self, coords, warn):
# Ensure that warning is raised.
msg = "Collapsing spatial coordinate {!r} without weighting"
for coord in coords:
- self.assertIn(mock.call(msg.format(coord)), warn.call_args_list)
+ self.assertIn(
+ mock.call(msg.format(coord), category=IrisUserWarning),
+ warn.call_args_list,
+ )
def _assert_nowarn_collapse_without_weight(self, coords, warn):
# Ensure that warning is not raised.
@@ -765,7 +770,10 @@ def _assert_warn_cannot_check_contiguity(self, warn):
f"bounds. Metadata may not be fully descriptive for "
f"'{coord}'. Ignoring bounds."
)
- self.assertIn(mock.call(msg), warn.call_args_list)
+ self.assertIn(
+ mock.call(msg, category=IrisVagueMetadataWarning),
+ warn.call_args_list,
+ )
def _assert_cube_as_expected(self, cube):
"""Ensure that cube data and coordinates are as expected."""
diff --git a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridAuxiliaryCoordinateVariable.py b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridAuxiliaryCoordinateVariable.py
index a4e0e05a08b..641b6b7b44f 100644
--- a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridAuxiliaryCoordinateVariable.py
+++ b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridAuxiliaryCoordinateVariable.py
@@ -20,6 +20,7 @@
import numpy as np
import pytest
+import iris.exceptions
from iris.experimental.ugrid.cf import CFUGridAuxiliaryCoordinateVariable
from iris.tests.unit.experimental.ugrid.cf.test_CFUGridReader import (
netcdf_ugrid_variable,
@@ -215,7 +216,10 @@ def test_warn(self):
}
def operation(warn: bool):
- warnings.warn("emit at least 1 warning")
+ warnings.warn(
+ "emit at least 1 warning",
+ category=iris.exceptions.IrisUserWarning,
+ )
result = CFUGridAuxiliaryCoordinateVariable.identify(
vars_all, warn=warn
)
@@ -223,7 +227,9 @@ def operation(warn: bool):
# Missing warning.
warn_regex = rf"Missing CF-netCDF auxiliary coordinate variable {subject_name}.*"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfMissingVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
@@ -235,7 +241,9 @@ def operation(warn: bool):
vars_all[subject_name] = netcdf_ugrid_variable(
subject_name, "", np.bytes_
)
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfLabelVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
diff --git a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridConnectivityVariable.py b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridConnectivityVariable.py
index 27d5c1db905..5a68a8c03f1 100644
--- a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridConnectivityVariable.py
+++ b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridConnectivityVariable.py
@@ -20,6 +20,7 @@
import numpy as np
import pytest
+import iris.exceptions
from iris.experimental.ugrid.cf import CFUGridConnectivityVariable
from iris.experimental.ugrid.mesh import Connectivity
from iris.tests.unit.experimental.ugrid.cf.test_CFUGridReader import (
@@ -204,7 +205,10 @@ def test_warn(self):
}
def operation(warn: bool):
- warnings.warn("emit at least 1 warning")
+ warnings.warn(
+ "emit at least 1 warning",
+ category=iris.exceptions.IrisUserWarning,
+ )
result = CFUGridConnectivityVariable.identify(vars_all, warn=warn)
self.assertDictEqual({}, result)
@@ -212,7 +216,9 @@ def operation(warn: bool):
warn_regex = (
rf"Missing CF-UGRID connectivity variable {subject_name}.*"
)
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfMissingVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
@@ -224,7 +230,9 @@ def operation(warn: bool):
vars_all[subject_name] = netcdf_ugrid_variable(
subject_name, "", np.bytes_
)
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfLabelVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
diff --git a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridMeshVariable.py b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridMeshVariable.py
index 6b278cf1b16..8302c301779 100644
--- a/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridMeshVariable.py
+++ b/lib/iris/tests/unit/experimental/ugrid/cf/test_CFUGridMeshVariable.py
@@ -20,6 +20,7 @@
import numpy as np
import pytest
+import iris.exceptions
from iris.experimental.ugrid.cf import CFUGridMeshVariable
from iris.tests.unit.experimental.ugrid.cf.test_CFUGridReader import (
netcdf_ugrid_variable,
@@ -247,13 +248,18 @@ def test_warn(self):
}
def operation(warn: bool):
- warnings.warn("emit at least 1 warning")
+ warnings.warn(
+ "emit at least 1 warning",
+ category=iris.exceptions.IrisUserWarning,
+ )
result = CFUGridMeshVariable.identify(vars_all, warn=warn)
self.assertDictEqual({}, result)
# Missing warning.
warn_regex = rf"Missing CF-UGRID mesh variable {subject_name}.*"
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfMissingVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
@@ -265,7 +271,9 @@ def operation(warn: bool):
vars_all[subject_name] = netcdf_ugrid_variable(
subject_name, "", np.bytes_
)
- with pytest.warns(UserWarning, match=warn_regex):
+ with pytest.warns(
+ iris.exceptions.IrisCfLabelVarWarning, match=warn_regex
+ ):
operation(warn=True)
with pytest.warns() as record:
operation(warn=False)
diff --git a/lib/iris/tests/unit/experimental/ugrid/mesh/test_Connectivity.py b/lib/iris/tests/unit/experimental/ugrid/mesh/test_Connectivity.py
index f343f4be242..7e90555801e 100644
--- a/lib/iris/tests/unit/experimental/ugrid/mesh/test_Connectivity.py
+++ b/lib/iris/tests/unit/experimental/ugrid/mesh/test_Connectivity.py
@@ -14,7 +14,7 @@
import numpy as np
from numpy import ma
-from pkg_resources import parse_version
+from packaging import version
from iris._lazy_data import as_lazy_data, is_lazy_data
from iris.experimental.ugrid.mesh import Connectivity
@@ -63,7 +63,7 @@ def test_indices(self):
def test_read_only(self):
attributes = ("indices", "cf_role", "start_index", "location_axis")
- if parse_version(python_version()) >= parse_version("3.11"):
+ if version.parse(python_version()) >= version.parse("3.11"):
msg = "object has no setter"
else:
msg = "can't set attribute"
diff --git a/lib/iris/tests/unit/experimental/ugrid/mesh/test_Mesh.py b/lib/iris/tests/unit/experimental/ugrid/mesh/test_Mesh.py
index f39f3706ee1..64d106f97d7 100644
--- a/lib/iris/tests/unit/experimental/ugrid/mesh/test_Mesh.py
+++ b/lib/iris/tests/unit/experimental/ugrid/mesh/test_Mesh.py
@@ -72,7 +72,7 @@ def setUpClass(cls):
class TestProperties1D(TestMeshCommon):
- # Tests that can re-use a single instance for greater efficiency.
+ # Tests that can reuse a single instance for greater efficiency.
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -737,7 +737,7 @@ def test___str__units_stdname(self):
class TestOperations1D(TestMeshCommon):
- # Tests that cannot re-use an existing Mesh instance, instead need a new
+ # Tests that cannot reuse an existing Mesh instance, instead need a new
# one each time.
def setUp(self):
self.mesh = mesh.Mesh(
diff --git a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
index cb90c176b6a..b95c5f09bb2 100644
--- a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
+++ b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
@@ -17,7 +17,7 @@
import dask.array as da
import numpy as np
-from pkg_resources import parse_version
+from packaging import version
import pytest
from iris._lazy_data import as_lazy_data, is_lazy_data
@@ -79,7 +79,7 @@ def setUp(self):
def test_fixed_metadata(self):
# Check that you cannot set any of these on an existing MeshCoord.
meshcoord = self.meshcoord
- if parse_version(python_version()) >= parse_version("3.11"):
+ if version.parse(python_version()) >= version.parse("3.11"):
msg = "object has no setter"
else:
msg = "can't set attribute"
@@ -578,7 +578,7 @@ def _make_test_meshcoord(
edge_xs = self.EDGECOORDS_BASENUM + np.arange(n_edges)
face_xs = self.FACECOORDS_BASENUM + np.arange(n_faces)
- # Record all these for re-use in tests
+ # Record all these for reuse in tests
self.n_faces = n_faces
self.n_nodes = n_nodes
self.face_xs = face_xs
diff --git a/lib/iris/tests/unit/fileformats/ff/test_FF2PP.py b/lib/iris/tests/unit/fileformats/ff/test_FF2PP.py
index cec4f53bc32..16943c0c15d 100644
--- a/lib/iris/tests/unit/fileformats/ff/test_FF2PP.py
+++ b/lib/iris/tests/unit/fileformats/ff/test_FF2PP.py
@@ -15,7 +15,7 @@
import numpy as np
-from iris.exceptions import NotYetImplementedError
+from iris.exceptions import IrisLoadWarning, NotYetImplementedError
import iris.fileformats._ff as ff
from iris.fileformats._ff import FF2PP
import iris.fileformats.pp as pp
@@ -467,7 +467,7 @@ def test_unequal_spacing_eitherside(self):
with mock.patch("warnings.warn") as warn:
result = ff2pp._det_border(field_x, None)
- warn.assert_called_with(msg)
+ warn.assert_called_with(msg, category=IrisLoadWarning)
self.assertIs(result, field_x)
def test_increasing_field_values(self):
diff --git a/lib/iris/tests/unit/fileformats/ff/test_FFHeader.py b/lib/iris/tests/unit/fileformats/ff/test_FFHeader.py
index 6a653970869..72d522ec85a 100644
--- a/lib/iris/tests/unit/fileformats/ff/test_FFHeader.py
+++ b/lib/iris/tests/unit/fileformats/ff/test_FFHeader.py
@@ -14,7 +14,7 @@
import numpy as np
-from iris.fileformats._ff import FFHeader
+from iris.fileformats._ff import FFHeader, _WarnComboLoadingDefaulting
MyGrid = collections.namedtuple("MyGrid", "column row real horiz_grid_type")
@@ -60,7 +60,8 @@ def test_unknown(self):
grid = header.grid()
warn.assert_called_with(
"Staggered grid type: 0 not currently"
- " interpreted, assuming standard C-grid"
+ " interpreted, assuming standard C-grid",
+ category=_WarnComboLoadingDefaulting,
)
self.assertIs(grid, mock.sentinel.grid)
diff --git a/lib/iris/tests/unit/fileformats/name_loaders/test__build_cell_methods.py b/lib/iris/tests/unit/fileformats/name_loaders/test__build_cell_methods.py
index ded635984c1..624837c19d1 100644
--- a/lib/iris/tests/unit/fileformats/name_loaders/test__build_cell_methods.py
+++ b/lib/iris/tests/unit/fileformats/name_loaders/test__build_cell_methods.py
@@ -15,6 +15,7 @@
from unittest import mock
import iris.coords
+from iris.exceptions import IrisLoadWarning
from iris.fileformats.name_loaders import _build_cell_methods
@@ -104,7 +105,7 @@ def test_unrecognised(self):
"Unknown {} statistic: {!r}. Unable to "
"create cell method.".format(coord_name, unrecognised_heading)
)
- warn.assert_called_with(expected_msg)
+ warn.assert_called_with(expected_msg, category=IrisLoadWarning)
def test_unrecognised_similar_to_no_averaging(self):
unrecognised_headings = [
@@ -129,7 +130,7 @@ def test_unrecognised_similar_to_no_averaging(self):
"Unknown {} statistic: {!r}. Unable to "
"create cell method.".format(coord_name, unrecognised_heading)
)
- warn.assert_called_with(expected_msg)
+ warn.assert_called_with(expected_msg, category=IrisLoadWarning)
if __name__ == "__main__":
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
index 399a987f116..38882810d29 100644
--- a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
@@ -12,6 +12,7 @@
import tempfile
import warnings
+from iris.exceptions import IrisLoadWarning
import iris.fileformats._nc_load_rules.engine
from iris.fileformats.cf import CFReader
import iris.fileformats.netcdf
@@ -138,7 +139,7 @@ def run_testcase(self, warning_regex=None, **testcase_kwargs):
if warning_regex is None:
context = self.assertNoWarningsRegexp()
else:
- context = self.assertWarnsRegex(UserWarning, warning_regex)
+ context = self.assertWarnsRegex(IrisLoadWarning, warning_regex)
with context:
cube = self.load_cube_from_cdl(cdl_string, cdl_path, nc_path)
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_oblique_mercator_coordinate_system.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_oblique_mercator_coordinate_system.py
new file mode 100644
index 00000000000..b11d8d3cca0
--- /dev/null
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_oblique_mercator_coordinate_system.py
@@ -0,0 +1,183 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""
+Test function :func:`iris.fileformats._nc_load_rules.helpers.build_oblique_mercator_coordinate_system`.
+
+"""
+from typing import List, NamedTuple, Type
+from unittest import mock
+
+import pytest
+
+from iris import coord_systems
+from iris._deprecation import IrisDeprecation
+from iris.coord_systems import (
+ CoordSystem,
+ GeogCS,
+ ObliqueMercator,
+ RotatedMercator,
+)
+from iris.fileformats._nc_load_rules.helpers import (
+ build_oblique_mercator_coordinate_system,
+)
+
+
+class ParamTuple(NamedTuple):
+ """Used for easy coupling of test parameters."""
+
+ id: str
+ nc_attributes: dict
+ expected_class: Type[CoordSystem]
+ coord_system_kwargs: dict
+
+
+kwarg_permutations: List[ParamTuple] = [
+ ParamTuple(
+ "default",
+ dict(),
+ ObliqueMercator,
+ dict(),
+ ),
+ ParamTuple(
+ "azimuth",
+ dict(azimuth_of_central_line=90),
+ ObliqueMercator,
+ dict(azimuth_of_central_line=90),
+ ),
+ ParamTuple(
+ "central_longitude",
+ dict(longitude_of_projection_origin=90),
+ ObliqueMercator,
+ dict(longitude_of_projection_origin=90),
+ ),
+ ParamTuple(
+ "central_latitude",
+ dict(latitude_of_projection_origin=45),
+ ObliqueMercator,
+ dict(latitude_of_projection_origin=45),
+ ),
+ ParamTuple(
+ "false_easting_northing",
+ dict(false_easting=1000000, false_northing=-2000000),
+ ObliqueMercator,
+ dict(false_easting=1000000, false_northing=-2000000),
+ ),
+ ParamTuple(
+ "scale_factor",
+ # Number inherited from Cartopy's test_mercator.py .
+ dict(scale_factor_at_projection_origin=0.939692620786),
+ ObliqueMercator,
+ dict(scale_factor_at_projection_origin=0.939692620786),
+ ),
+ ParamTuple(
+ "globe",
+ dict(semi_major_axis=1),
+ ObliqueMercator,
+ dict(ellipsoid=GeogCS(semi_major_axis=1, semi_minor_axis=1)),
+ ),
+ ParamTuple(
+ "combo",
+ dict(
+ azimuth_of_central_line=90,
+ longitude_of_projection_origin=90,
+ latitude_of_projection_origin=45,
+ false_easting=1000000,
+ false_northing=-2000000,
+ scale_factor_at_projection_origin=0.939692620786,
+ semi_major_axis=1,
+ ),
+ ObliqueMercator,
+ dict(
+ azimuth_of_central_line=90.0,
+ longitude_of_projection_origin=90.0,
+ latitude_of_projection_origin=45.0,
+ false_easting=1000000,
+ false_northing=-2000000,
+ scale_factor_at_projection_origin=0.939692620786,
+ ellipsoid=GeogCS(semi_major_axis=1, semi_minor_axis=1),
+ ),
+ ),
+ ParamTuple(
+ "rotated",
+ dict(grid_mapping_name="rotated_mercator"),
+ RotatedMercator,
+ dict(),
+ ),
+ ParamTuple(
+ "rotated_azimuth_ignored",
+ dict(
+ grid_mapping_name="rotated_mercator",
+ azimuth_of_central_line=45,
+ ),
+ RotatedMercator,
+ dict(),
+ ),
+]
+permutation_ids: List[str] = [p.id for p in kwarg_permutations]
+
+
+class TestAttributes:
+ """Test that NetCDF attributes are correctly converted to class arguments."""
+
+ nc_attributes_default = dict(
+ grid_mapping_name="oblique_mercator",
+ azimuth_of_central_line=0.0,
+ latitude_of_projection_origin=0.0,
+ longitude_of_projection_origin=0.0,
+ scale_factor_at_projection_origin=1.0,
+ # Optional attributes not included.
+ )
+ coord_system_kwargs_default = dict(
+ azimuth_of_central_line=0.0,
+ latitude_of_projection_origin=0.0,
+ longitude_of_projection_origin=0.0,
+ false_easting=None,
+ false_northing=None,
+ scale_factor_at_projection_origin=1.0,
+ ellipsoid=None,
+ )
+
+ @pytest.fixture(
+ autouse=True, params=kwarg_permutations, ids=permutation_ids
+ )
+ def make_variant_inputs(self, request) -> None:
+ """Parse a ParamTuple into usable test information."""
+ inputs: ParamTuple = request.param
+
+ self.nc_attributes = dict(
+ self.nc_attributes_default, **inputs.nc_attributes
+ )
+ self.expected_class = inputs.expected_class
+ coord_system_kwargs_expected = dict(
+ self.coord_system_kwargs_default, **inputs.coord_system_kwargs
+ )
+
+ if self.expected_class is RotatedMercator:
+ del coord_system_kwargs_expected["azimuth_of_central_line"]
+
+ self.coord_system_args_expected = list(
+ coord_system_kwargs_expected.values()
+ )
+
+ def test_attributes(self):
+ cf_var_mock = mock.Mock(spec=[], **self.nc_attributes)
+ coord_system_mock = mock.Mock(spec=self.expected_class)
+ setattr(coord_systems, self.expected_class.__name__, coord_system_mock)
+
+ _ = build_oblique_mercator_coordinate_system(None, cf_var_mock)
+ coord_system_mock.assert_called_with(*self.coord_system_args_expected)
+
+
+def test_deprecation():
+ nc_attributes = dict(
+ grid_mapping_name="rotated_mercator",
+ latitude_of_projection_origin=0.0,
+ longitude_of_projection_origin=0.0,
+ scale_factor_at_projection_origin=1.0,
+ )
+ cf_var_mock = mock.Mock(spec=[], **nc_attributes)
+ with pytest.warns(IrisDeprecation, match="azimuth_of_central_line = 90"):
+ _ = build_oblique_mercator_coordinate_system(None, cf_var_mock)
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_parse_cell_methods.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_parse_cell_methods.py
index 729a2d8b144..9935a6e5ae8 100644
--- a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_parse_cell_methods.py
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_parse_cell_methods.py
@@ -15,6 +15,7 @@
from unittest import mock
from iris.coords import CellMethod
+from iris.exceptions import IrisCfLoadWarning
from iris.fileformats._nc_load_rules.helpers import parse_cell_methods
@@ -123,7 +124,7 @@ def test_comment_bracket_mismatch_warning(self):
]
for cell_method_str in cell_method_strings:
with self.assertWarns(
- UserWarning,
+ IrisCfLoadWarning,
msg="Cell methods may be incorrectly parsed due to mismatched brackets",
):
_ = parse_cell_methods(cell_method_str)
@@ -139,7 +140,7 @@ def test_badly_formatted_warning(self):
]
for cell_method_str in cell_method_strings:
with self.assertWarns(
- UserWarning,
+ IrisCfLoadWarning,
msg=f"Failed to fully parse cell method string: {cell_method_str}",
):
_ = parse_cell_methods(cell_method_str)
diff --git a/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
index 841935cc814..c15c8737fd6 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
@@ -16,6 +16,7 @@
from iris.coords import DimCoord
from iris.cube import Cube
+from iris.exceptions import IrisFactoryCoordNotFoundWarning
from iris.fileformats.netcdf.loader import _load_aux_factory
@@ -165,7 +166,8 @@ def test_formula_terms_ap_missing_coords(self):
with mock.patch("warnings.warn") as warn:
_load_aux_factory(self.engine, self.cube)
warn.assert_called_once_with(
- "Unable to find coordinate for variable " "'ap'"
+ "Unable to find coordinate for variable " "'ap'",
+ category=IrisFactoryCoordNotFoundWarning,
)
self._check_no_delta()
diff --git a/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver.py b/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver.py
index 12af318c01f..8253e593689 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver.py
@@ -24,13 +24,16 @@
LambertAzimuthalEqualArea,
LambertConformal,
Mercator,
+ ObliqueMercator,
RotatedGeogCS,
+ RotatedMercator,
Stereographic,
TransverseMercator,
VerticalPerspective,
)
from iris.coords import AuxCoord, DimCoord
from iris.cube import Cube
+from iris.exceptions import IrisMaskValueMatchWarning
from iris.fileformats.netcdf import Saver, _thread_safe_nc
import iris.tests.stock as stock
@@ -555,7 +558,7 @@ def test_contains_fill_value_passed(self):
cube = self._make_cube(">f4")
fill_value = 1
with self.assertWarnsRegex(
- UserWarning,
+ IrisMaskValueMatchWarning,
"contains unmasked data points equal to the fill-value",
):
with self._netCDF_var(cube, fill_value=fill_value):
@@ -567,7 +570,7 @@ def test_contains_fill_value_byte(self):
cube = self._make_cube(">i1")
fill_value = 1
with self.assertWarnsRegex(
- UserWarning,
+ IrisMaskValueMatchWarning,
"contains unmasked data points equal to the fill-value",
):
with self._netCDF_var(cube, fill_value=fill_value):
@@ -579,7 +582,7 @@ def test_contains_default_fill_value(self):
cube = self._make_cube(">f4")
cube.data[0, 0] = _thread_safe_nc.default_fillvals["f4"]
with self.assertWarnsRegex(
- UserWarning,
+ IrisMaskValueMatchWarning,
"contains unmasked data points equal to the fill-value",
):
with self._netCDF_var(cube):
@@ -1064,6 +1067,50 @@ def test_geo_cs(self):
}
self._test(coord_system, expected)
+ def test_oblique_cs(self):
+ # Some none-default settings to confirm all parameters are being
+ # handled.
+
+ kwargs_rotated = dict(
+ latitude_of_projection_origin=90.0,
+ longitude_of_projection_origin=45.0,
+ false_easting=1000000.0,
+ false_northing=-2000000.0,
+ scale_factor_at_projection_origin=0.939692620786,
+ ellipsoid=GeogCS(1),
+ )
+
+ # Same as rotated, but with azimuth too.
+ oblique_azimuth = dict(azimuth_of_central_line=45.0)
+ kwargs_oblique = dict(kwargs_rotated, **oblique_azimuth)
+
+ expected_rotated = dict(
+ # Automatically converted to oblique_mercator in line with CF 1.11 .
+ grid_mapping_name=b"oblique_mercator",
+ # Azimuth should be automatically populated.
+ azimuth_of_central_line=90.0,
+ **kwargs_rotated,
+ )
+ # Convert the ellipsoid
+ expected_rotated.update(
+ dict(
+ earth_radius=expected_rotated.pop("ellipsoid").semi_major_axis,
+ longitude_of_prime_meridian=0.0,
+ )
+ )
+
+ # Same as rotated, but different azimuth.
+ expected_oblique = dict(expected_rotated, **oblique_azimuth)
+
+ oblique = ObliqueMercator(**kwargs_oblique)
+ rotated = RotatedMercator(**kwargs_rotated)
+
+ for coord_system, expected in [
+ (oblique, expected_oblique),
+ (rotated, expected_rotated),
+ ]:
+ self._test(coord_system, expected)
+
if __name__ == "__main__":
tests.main()
diff --git a/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver__lazy_stream_data.py b/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver__lazy_stream_data.py
index 9686c88abf9..da5f2d88fa4 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver__lazy_stream_data.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/saver/test_Saver__lazy_stream_data.py
@@ -18,6 +18,7 @@
import numpy as np
import pytest
+from iris.exceptions import IrisMaskValueMatchWarning
import iris.fileformats.netcdf._thread_safe_nc as threadsafe_nc
from iris.fileformats.netcdf.saver import Saver, _FillvalueCheckInfo
@@ -183,5 +184,5 @@ def test_warnings(self, compute, data_form):
if n_expected_warnings > 0:
warning = issued_warnings[0]
msg = "contains unmasked data points equal to the fill-value, 2.0"
- assert isinstance(warning, UserWarning)
+ assert isinstance(warning, IrisMaskValueMatchWarning)
assert msg in warning.args[0]
diff --git a/lib/iris/tests/unit/fileformats/netcdf/saver/test__fillvalue_report.py b/lib/iris/tests/unit/fileformats/netcdf/saver/test__fillvalue_report.py
index b2e4b63e3a4..317f75bb8cf 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/saver/test__fillvalue_report.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/saver/test__fillvalue_report.py
@@ -11,9 +11,9 @@
import numpy as np
import pytest
+from iris.exceptions import IrisSaverFillValueWarning
from iris.fileformats.netcdf._thread_safe_nc import default_fillvals
from iris.fileformats.netcdf.saver import (
- SaverFillValueWarning,
_fillvalue_report,
_FillvalueCheckInfo,
)
@@ -93,12 +93,14 @@ def test_warn(self, has_collision):
expected_msg = "'' contains unmasked data points equal to the fill-value"
# Enter a warnings context that checks for the error.
warning_context = pytest.warns(
- SaverFillValueWarning, match=expected_msg
+ IrisSaverFillValueWarning, match=expected_msg
)
warning_context.__enter__()
else:
# Check that we get NO warning of the expected type.
- warnings.filterwarnings("error", category=SaverFillValueWarning)
+ warnings.filterwarnings(
+ "error", category=IrisSaverFillValueWarning
+ )
# Do call: it should raise AND return a warning, ONLY IF there was a collision.
result = _fillvalue_report(
diff --git a/lib/iris/tests/unit/fileformats/nimrod_load_rules/test_vertical_coord.py b/lib/iris/tests/unit/fileformats/nimrod_load_rules/test_vertical_coord.py
index 44dcf8ac485..2279bcffc3e 100644
--- a/lib/iris/tests/unit/fileformats/nimrod_load_rules/test_vertical_coord.py
+++ b/lib/iris/tests/unit/fileformats/nimrod_load_rules/test_vertical_coord.py
@@ -63,7 +63,7 @@ def test_unhandled(self):
vertical_coord_val=1.0, vertical_coord_type=-1
)
warn.assert_called_once_with(
- "Vertical coord -1 not yet handled", TranslationWarning
+ "Vertical coord -1 not yet handled", category=TranslationWarning
)
def test_null(self):
diff --git a/lib/iris/tests/unit/fileformats/pp/test_PPField.py b/lib/iris/tests/unit/fileformats/pp/test_PPField.py
index 316894ded18..f2bbf97a807 100644
--- a/lib/iris/tests/unit/fileformats/pp/test_PPField.py
+++ b/lib/iris/tests/unit/fileformats/pp/test_PPField.py
@@ -13,6 +13,7 @@
import numpy as np
+from iris.exceptions import IrisDefaultingWarning, IrisMaskValueMatchWarning
import iris.fileformats.pp as pp
from iris.fileformats.pp import PPField, SplittableInt
@@ -91,7 +92,7 @@ def field_checksum(data):
data_64 = np.linspace(0, 1, num=10, endpoint=False).reshape(2, 5)
checksum_32 = field_checksum(data_64.astype(">f4"))
msg = "Downcasting array precision from float64 to float32 for save."
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisDefaultingWarning, msg):
checksum_64 = field_checksum(data_64.astype(">f8"))
self.assertEqual(checksum_32, checksum_64)
@@ -104,7 +105,7 @@ def test_masked_mdi_value_warning(self):
[1.0, field.bmdi, 3.0], dtype=np.float32
)
msg = "PPField data contains unmasked points"
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisMaskValueMatchWarning, msg):
with self.temp_filename(".pp") as temp_filename:
with open(temp_filename, "wb") as pp_file:
field.save(pp_file)
@@ -116,7 +117,7 @@ def test_unmasked_mdi_value_warning(self):
# Make float32 data, as float64 default produces an extra warning.
field.data = np.array([1.0, field.bmdi, 3.0], dtype=np.float32)
msg = "PPField data contains unmasked points"
- with self.assertWarnsRegex(UserWarning, msg):
+ with self.assertWarnsRegex(IrisMaskValueMatchWarning, msg):
with self.temp_filename(".pp") as temp_filename:
with open(temp_filename, "wb") as pp_file:
field.save(pp_file)
diff --git a/lib/iris/tests/unit/pandas/test_pandas.py b/lib/iris/tests/unit/pandas/test_pandas.py
index fd716bd7c99..d74d7cad9c8 100644
--- a/lib/iris/tests/unit/pandas/test_pandas.py
+++ b/lib/iris/tests/unit/pandas/test_pandas.py
@@ -1075,7 +1075,7 @@ def test_ancillary_variable(self):
def test_3d_with_2d_coord(self):
df = self._create_pandas(index_levels=3)
coord_shape = df.index.levshape[:2]
- coord_values = np.arange(np.product(coord_shape))
+ coord_values = np.arange(np.prod(coord_shape))
coord_name = "foo"
df[coord_name] = coord_values.repeat(df.index.levshape[-1])
result = iris.pandas.as_cubes(df, aux_coord_cols=[coord_name])
@@ -1089,7 +1089,7 @@ def test_3d_with_2d_coord(self):
def test_coord_varies_all_indices(self):
df = self._create_pandas(index_levels=3)
coord_shape = df.index.levshape
- coord_values = np.arange(np.product(coord_shape))
+ coord_values = np.arange(np.prod(coord_shape))
coord_name = "foo"
df[coord_name] = coord_values
result = iris.pandas.as_cubes(df, aux_coord_cols=[coord_name])
@@ -1105,7 +1105,7 @@ def test_category_coord(self):
# increment.
df = self._create_pandas(index_levels=2)
coord_shape = df.index.levshape
- coord_values = np.arange(np.product(coord_shape))
+ coord_values = np.arange(np.prod(coord_shape))
coord_name = "foo"
# Create a repeating value along a dimension.
diff --git a/lib/iris/tests/unit/plot/test__check_geostationary_coords_and_convert.py b/lib/iris/tests/unit/plot/test__check_geostationary_coords_and_convert.py
index 633dea85c47..a06a4373964 100644
--- a/lib/iris/tests/unit/plot/test__check_geostationary_coords_and_convert.py
+++ b/lib/iris/tests/unit/plot/test__check_geostationary_coords_and_convert.py
@@ -37,7 +37,7 @@ def setUp(self):
)
def _test(self, geostationary=True):
- # Re-usable test for when Geostationary is present OR absent.
+ # Reusable test for when Geostationary is present OR absent.
if geostationary:
# A Geostationary projection WILL be processed.
projection_spec = Geostationary
diff --git a/requirements/locks/py310-linux-64.lock b/requirements/locks/py310-linux-64.lock
index efa54318234..26559606227 100644
--- a/requirements/locks/py310-linux-64.lock
+++ b/requirements/locks/py310-linux-64.lock
@@ -1,6 +1,6 @@
# Generated by conda-lock.
# platform: linux-64
-# input_hash: bc0383c4702650016b286d9f1d4b405e53a5c85772e04622e3c10fd51465463a
+# input_hash: 94966cd7393527bff211c87589678b2ffe1697705267a20b2708a4cc27da5376
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.7.22-hbcca054_0.conda#a73ecd2988327ad4c8f2c331482917f2
@@ -9,18 +9,18 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed3
https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-h41732ed_0.conda#7aca3059a1729aa76c597603f10b0dd3
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_1.conda#acfb4817400db5804030a3a7ef7909a1
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.10-3_cp310.conda#4eb33d14d794b0f4be116443ffed3853
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_2.conda#9172c297304f2a20134fc56c97fbe229
+https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.10-4_cp310.conda#26322ec5d7712c3ded99dd656142b8ce
https://conda.anaconda.org/conda-forge/noarch/tzdata-2023c-h71feb2d_0.conda#939e3e74d8be4dac89ce83b20de2492a
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_1.conda#8bb001683321dcbde117a7337b5aace7
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_2.conda#e2042154faafe61969556f28bade94b9
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_1.conda#ff8999574b465089ba0aa25a5e865bd0
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_2.conda#c28003b0be0494f9a7664389146716ff
https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.10-hd590300_0.conda#75dae9a4201732aa78a530b826ee5fe0
https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
-https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.19.1-hd590300_0.conda#e8c18d865be43e2fb3f7a145b6adf1f5
+https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.20.1-hd590300_0.conda#6642e4faa4804be3a0e7edfefbd16595
https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.0-h59595ed_0.conda#3fdf79ef322c8379ae83be491d805369
https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
@@ -30,29 +30,29 @@ https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda#cc47e1
https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libaec-1.0.6-hcb278e6_1.conda#0f683578378cddb223e7fd24f785ab2a
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_0.conda#e805cbec4c29feb22e019245f7e47b6c
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.18-h0b41bf4_0.conda#6aa9c9de5542ecb07fdda9ca626252d8
+https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.2-h59595ed_1.conda#127b0be54c1c90760d7fe02ea7a56426
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_1.conda#aec6c91c7371c26392a06708a73c70e5
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.19-hd590300_0.conda#1635570038840ee3f9c71d22aa5b8b6d
https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.5.0-hcb278e6_1.conda#6305a3dd2752c76335295da4e581f2fd
https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_1.conda#a0d27fd5c6f05aa45e9602b1db49581c
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_2.conda#78fdab09d9138851dde2b5fe2a11019e
https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
-https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-2.1.5.1-hd590300_1.conda#323e90742f0f48fc22bea908735f55e6
+https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8
https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-hd590300_1.conda#854e3e1623b39777140f199c5f9ab952
https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.7-h27087fc_0.conda#f204c8ba400ec475452737094fb81d52
https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.1-hd590300_0.conda#82bf6f63eb15ef719b556b63feec3a77
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.2-hd590300_0.conda#30de3fd9b3b602f7473f30e684eeea8c
https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-hd590300_5.conda#f36c115f1ee199da648e0597ec2047ad
https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0
-https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.31.3-hcb278e6_0.conda#141a126675b6d1a4eabb111a4a353898
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.3-h59595ed_0.conda#bdadff838d5437aea83607ced8b37f75
https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.4-hcb278e6_0.conda#681105bccc2a3f7f1a837d47d39c9179
https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.2-hd590300_0.conda#e5ac5227582d6c83ccf247288c0eb095
-https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.3-hd590300_0.conda#7bb88ce04c8deb9f7d763ae04a1da72f
+https://conda.anaconda.org/conda-forge/linux-64/pixman-0.42.2-h59595ed_0.conda#700edd63ccd5fc66b70b1c028cea9a68
https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e6d228cd0bb74a51dd18f5bfce0b4115
https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -67,64 +67,65 @@ https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda#f0
https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h501b40f_6.conda#c3e9338e15d90106f467377017352b97
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_0.conda#43017394a280a42b48d11d2a6e169901
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_0.conda#8e3e1cb77c4b355a3776bdfb74095bed
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_1.conda#f07002e225d7a60a694d42a7bf5ff53f
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_1.conda#5fc11c6020d421960607d821310fcd4d
https://conda.anaconda.org/conda-forge/linux-64/libcap-2.69-h0f662aa_0.conda#25cb5999faa414e5ccb2c1388f62d3d5
https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d
https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_1.conda#394218a92951499aed2ab1bafb30b570
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_2.conda#e75a75a6eaf6f318dae2631158c46575
https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.47-h71f35ed_0.conda#c2097d0b46367996f09b4e8e4920384a
https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.52.0-h61bc06f_0.conda#613955a50485812985c059e7b269f42e
https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.0-h2797004_0.conda#903fa782a9067d5934210df6d79220f6
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.2-h2797004_0.conda#4b441a1ee22397d5a27dc1126b849edd
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe
+https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56
https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c
https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.11.5-h232c23b_1.conda#f3858448893839820d4bcfb14ad3ecdf
https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_4.conda#f6f0ac5665849afc0716213a6cff224d
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_5.conda#1e8ef4090ca4f0d66404a7441e1dbf3c
https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
-https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_1.conda#85552d64cb49f12781668779efc738ec
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-h2797004_0.conda#513336054f884f95d9fd925748f41ef3
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-h7391055_0.conda#93ee23f12bc2e684548181256edd2cf6
https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-hd590300_5.conda#68c34ec6149623be41a1933ab996a209
https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.5-hfc55251_0.conda#04b88013080254850d6c01ed54810589
https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.5-h0f2a231_0.conda#009521b7ed97cca25f8f997f9e745976
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_0.conda#aeafb07a327e3f14a796bf081ea07472
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_1.conda#39f910d205726805a958da408ca194ba
https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd95826dbd331ed1be26bdf401432844
https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-1.10.1-h166bdaf_0.tar.bz2#f967fc95089cd247ceed56eda31de3a9
https://conda.anaconda.org/conda-forge/linux-64/libglib-2.78.0-hebfc3b9_0.conda#e618003da3547216310088478e475945
https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.7-h5cf9203_3.conda#9efe82d44b76a7529a1d702e5a37752e
https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.24-pthreads_h413a1c8_0.conda#6e4ef6ca28655124dcde9bd500e44c32
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hbc2eb40_0.conda#38f84d395629e48b7c7b48a8ca740341
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.1-h8b53f26_1.conda#5b09e13d732dda1a2bc9adc711164f4d
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_4.conda#db7f2c877209ac620fcd1c3ce7407cf0
-https://conda.anaconda.org/conda-forge/linux-64/nss-3.92-h1d7d5a4_0.conda#22c89a3d87828fe925b310b9cdf0f574
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-ha9c0a0a_2.conda#55ed21669b2015f77c180feb1dd41930
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_5.conda#b72f016c910ff9295b1377d3e17da3f2
+https://conda.anaconda.org/conda-forge/linux-64/nss-3.94-h1d7d5a4_0.conda#7caef74bbfa730e014b20f0852068509
https://conda.anaconda.org/conda-forge/linux-64/python-3.10.12-hd12c33a_0_cpython.conda#eb6f1df105f37daedd6dca78523baa75
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.0-h2c6b66d_0.conda#713f9eac95d051abe14c3774376854fe
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.2-h2c6b66d_0.conda#c37b95bcd6c6833dacfd5df0ae2f4303
+https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-hd590300_1.conda#9bfac7ccd94d54fd21a0501296d60424
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h8ee46fc_1.conda#632413adcd8bc16b515cab87a2932913
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-hd590300_1.conda#e995b155d938b6779da6ace6c6b13816
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h8ee46fc_1.conda#90108a432fb5c6150ccfee3f03388656
-https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.6-h8ee46fc_0.conda#7590b76c3d11d21caa44f3fc38ac584a
+https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.7-h8ee46fc_0.conda#49e482d882669206653b095f5206c05b
https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.13-pyhd8ed1ab_0.conda#06006184e203b61d3525f90de394471e
-https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py310hff52083_1003.tar.bz2#8324f8fff866055d4b32eb25e091fe31
+https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.11.1-pyhd8ed1ab_0.tar.bz2#15109c4977d39ad7aa3423f57243e286
https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.1.0-pyhd8ed1ab_0.conda#0e8715bef534217eae333c53f645c9ed
https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_0.conda#3db48055eab680e43a122e2c7494e7ae
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hc6cd4ac_0.conda#fb6201eb1daa3a3a2f91a4833bdf27c7
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_1.conda#f27a24d46e3ea7b70a1f98e50c62508f
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hc6cd4ac_1.conda#1f95722c94f00b69af69a066c7433714
https://conda.anaconda.org/conda-forge/noarch/certifi-2023.7.22-pyhd8ed1ab_0.conda#7f3dbc9179b4dde7da98dfb151d0ad22
https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.2.0-pyhd8ed1ab_0.conda#313516e9a4b08b12dfb1e1cd390a96e3
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.0-pyhd8ed1ab_0.conda#fef8ef5f0a54546b9efee39468229917
https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca
https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.1-pyhd8ed1ab_0.conda#b325bfc4cff7d7f8a868f1f7ecc4ed16
https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
-https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
-https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.2-py310hc6cd4ac_0.conda#d1157aba60e67df614438afd5cd53564
+https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda#5cd86562580f274031ede6aa6aa24441
+https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.3-py310hc6cd4ac_0.conda#90bccd216944c486966c3846b339b42f
https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.7-pyhd8ed1ab_0.conda#12d8aae6994f342618443a8f05c652a0
https://conda.anaconda.org/conda-forge/linux-64/docutils-0.19-py310hff52083_1.tar.bz2#21b8fa2179290505e607f5ccd65b01b0
@@ -132,41 +133,41 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.1.3-pyhd8ed1ab_0.
https://conda.anaconda.org/conda-forge/noarch/execnet-2.0.2-pyhd8ed1ab_0.conda#67de0d8241e1060a479e3c37793e26f9
https://conda.anaconda.org/conda-forge/noarch/filelock-3.12.4-pyhd8ed1ab_0.conda#5173d4b8267a0699a43d73231e0b6596
https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.1-pyh1a96a4e_0.conda#d69753ff6ee3c84a6638921dd95db662
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h6b639ba_2.conda#ee8220db21db8094998005990418fe5b
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.2-pyh1a96a4e_0.conda#9d15cd3a0e944594ab528da37dc72ecc
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h829c605_4.conda#252a696860674caf7a855e16f680d63a
https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.78.0-hfc55251_0.conda#e10134de3558dd95abda6987b5548f4f
https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe
https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5
https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py310hd41b1e2_0.conda#741385a84f6a1b6623eb39226cc669e8
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-haa2dc70_1.conda#980d8aca0bc23ca73fa8caa3e7c84c28
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py310hd41b1e2_1.conda#b8d67603d43b23ce7e988a5d81a7ab79
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-hb7c19ff_3.conda#e96637dd92c5f340215c753a5c9a22d7
https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-18_linux64_openblas.conda#bcddbb497582ece559465b9cd11042e7
https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.7-default_h9986a30_3.conda#1720df000b48e31842500323cb7be18c
https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.3.0-hca28451_0.conda#4ab41bee09a2d2e08de5f09d6f1eef62
-https://conda.anaconda.org/conda-forge/linux-64/libpq-15.4-hfc447b1_0.conda#b9ce311e7aba8b5fc3122254f0a6e97e
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.4.0-hca28451_0.conda#1158ac1d2613b28685644931f11ee807
+https://conda.anaconda.org/conda-forge/linux-64/libpq-16.0-hfc447b1_1.conda#e4a9a5ba40123477db33e02a78dffb01
https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-254-h3516f8a_0.conda#df4b1cd0c91b4234fb02b5701a4cdddc
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.1-hbf2b3c1_0.conda#4963f3f12db45a576f2b8fbe9a0b8569
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.2-h658648e_1.conda#0ebb65e8d86843865796c7c95a941f34
https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py310h2372a71_0.conda#5597d9f9778af6883ae64f0e7d39416c
-https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.5-py310hdf3cbec_0.conda#5311a49aaea44b73935c84a6d9a68e5f
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py310h2372a71_1.conda#b74e07a054c479e45a83a83fc5be713c
+https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.6-py310hd41b1e2_0.conda#03255e1437f31f25ad95bb45c8b398bb
https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
-https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-hfec8fc6_2.conda#5ce6a42505c6e9e6151c54c3ec8d68ea
-https://conda.anaconda.org/conda-forge/noarch/packaging-23.1-pyhd8ed1ab_0.conda#91cda59e66e1e4afe9476f8ef98f5c30
+https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h488ebb8_3.conda#128c25b7fe6a25286a48f3a6a9b5b6f3
+https://conda.anaconda.org/conda-forge/noarch/packaging-23.2-pyhd8ed1ab_0.conda#79002079284aa895f883c6b7f3f88fd6
https://conda.anaconda.org/conda-forge/noarch/pluggy-1.3.0-pyhd8ed1ab_0.conda#2390bd10bed1f3fdc7a537fb5a447d8d
https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py310h1fa729e_0.conda#b0f0a014fc04012c05f39df15fe270ce
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py310h2372a71_1.conda#cb25177acf28cc35cfa6c1ac1c679e22
https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
https://conda.anaconda.org/conda-forge/noarch/pygments-2.16.1-pyhd8ed1ab_0.conda#40e5cb18165466773619e5c963f00a7b
https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.1-pyhd8ed1ab_0.conda#176f7d56f0cfe9008bdf1bccd7de02fb
https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.3-pyhd8ed1ab_0.conda#2590495f608a63625e165915fb4e2e34
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.3.0-py310h2372a71_0.conda#75e60ce53c01a121039b3050c9e1f759
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.4.1-py310h2372a71_0.conda#b631b889b0b4bc2fca7b8b977ca484b2
https://conda.anaconda.org/conda-forge/noarch/pytz-2023.3.post1-pyhd8ed1ab_0.conda#c93346b446cd08c169d843ae5fc0da97
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py310h2372a71_0.conda#511120451bf728d52bb37c73d4069e57
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py310h2372a71_1.conda#bb010e368de4940771368bc3dc4c63e7
https://conda.anaconda.org/conda-forge/noarch/setuptools-68.2.2-pyhd8ed1ab_0.conda#fc2166155db840c634a1291a5c35a709
https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
@@ -177,90 +178,90 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-2.0.0-pyhd8ed1ab_0.conda#f55
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py310h2372a71_0.conda#dfb49d3ac440e1a236080f9c300e642f
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py310h2372a71_1.conda#b23e0147fa5f7a9380e06334c7266ad5
https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.8.0-pyha770c72_0.conda#5b1be40a26d10a06f6d4f1f9e19fa0c7
-https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py310h5764c6d_0.tar.bz2#e972c5a1f472561cf4a91962cb01f4b4
+https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py310h2372a71_0.conda#72637c58d36d9475fda24700c9796f19
https://conda.anaconda.org/conda-forge/noarch/wheel-0.41.2-pyhd8ed1ab_0.conda#1ccd092478b3e0ee10d7a891adbf8a4f
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-h8ee46fc_1.conda#9d7bcddf49cbf727730af10e71022c73
-https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.39-hd590300_0.conda#d88c7fc8a11858fb14761832e4da1954
+https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.40-hd590300_0.conda#07c15d846a2e4d673da22cbd85fdb6d2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.11-hd590300_0.conda#ed67c36f215b310412b2af935bf3e530
https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_0.conda#cf30c2c15b82aacb07f9c09e28ff2275
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.16.2-pyhd8ed1ab_0.conda#2da0451b54c4563c32490cb1b7cf68a1
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.17.0-pyhd8ed1ab_0.conda#2e4d6bc0b14e10f895fc6791a7d9b26a
https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.4-pyhd8ed1ab_0.conda#46a2e6e3dfa718ce3492018d5a110dd6
-https://conda.anaconda.org/conda-forge/noarch/babel-2.12.1-pyhd8ed1ab_1.conda#ac432e732804a81ddcf29c92ead57cde
+https://conda.anaconda.org/conda-forge/noarch/babel-2.13.0-pyhd8ed1ab_0.conda#22541af7a9eb59fc6afcadb7ecdf9219
https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.2-pyha770c72_0.conda#a362ff7d976217f8fa78c0f1c4f59717
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h0c91306_1017.conda#3db543896d34fc6804ddfb9239dcb125
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py310h255011f_3.conda#800596144bb613cd7ac58b80900ce835
-https://conda.anaconda.org/conda-forge/linux-64/coverage-7.3.1-py310h2372a71_0.conda#0834a720fe60f511913ac52cd01e40dc
-https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py310h2372a71_0.conda#4efe3a76fe724778a7235a2046b53233
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.42.1-py310h2372a71_0.conda#1f18231ffab82f236ce074b2aaa07e54
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-h3faef2a_0.conda#f907bb958910dc404647326ca80c263e
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.16.0-py310h2fee648_0.conda#45846a970e71ac98fd327da5d40a0a2c
+https://conda.anaconda.org/conda-forge/linux-64/coverage-7.3.2-py310h2372a71_0.conda#33c03cd5711885c920ddff676fb84f98
+https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py310h2372a71_1.conda#a79a93c3912e9e9b0afd3bf58f2c01d7
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.43.1-py310h2372a71_0.conda#c7d552c32b87beb736c9658441bf93a9
https://conda.anaconda.org/conda-forge/linux-64/glib-2.78.0-hfc55251_0.conda#2f55a36b549f51a7e0c2b1e3c3f0ccd4
https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.2-nompi_h4f84152_100.conda#2de6a9bc8083b49f09b2f6eb28d3ba3c
https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.8.0-pyha770c72_0.conda#4e9f59a060c3be52bc4ddc46ee9b6946
https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-18_linux64_openblas.conda#93dd9ab275ad888ed8113953769af78c
https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.7-default_h7634d5b_3.conda#0922208521c0463e690bbaebba7eb551
-https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h74d50f4_7.conda#3453ac94a99ad9daf17e8a313d274567
+https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h119a65a_9.conda#cfebc557e54905dadc355c0e9f003004
https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-18_linux64_openblas.conda#a1244707531e5b143c420c70573c8ec5
-https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.5.0-h5d7e998_3.conda#c91ea308d7bf70b62ddda568478aa03b
+https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.6.0-h5d7e998_0.conda#d8edd0e29db6fb6b6988e1a28d35d994
https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.8.0-pyhd8ed1ab_0.conda#2a75b296096adabbabadd5e9782e5fcc
-https://conda.anaconda.org/conda-forge/noarch/partd-1.4.0-pyhd8ed1ab_1.conda#6ceb4e000cbe0b56b290180aea8520e8
-https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.0-py310h582fbeb_0.conda#adcc7ea52e4d39d0a93f6a2ef36c7fd4
+https://conda.anaconda.org/conda-forge/noarch/partd-1.4.1-pyhd8ed1ab_0.conda#acf4b7c0bcd5fa3b0e05801c4d2accd6
+https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.1-py310h01dd4db_2.conda#9ef290f84bf1f3932e9b42117d9364ff
https://conda.anaconda.org/conda-forge/noarch/pip-23.2.1-pyhd8ed1ab_0.conda#e2783aa3f9235225eec92f9081c5b801
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.2.1-ha643af7_0.conda#e992387307f4403ba0ec07d009032550
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.0-h1d62c97_1.conda#900fd11ac61d4415d515583fcb570207
https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-16.1-hb77b528_5.conda#ac902ff3c1c6d750dd0dfc93a974ab74
https://conda.anaconda.org/conda-forge/noarch/pytest-7.4.2-pyhd8ed1ab_0.conda#6dd662ff5ac9a783e5c940ce9f3fe649
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py310hc6cd4ac_0.conda#be1a7e420b7bac4ee02353d0e3161918
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py310hc6cd4ac_1.conda#c7936ec7db24bb913671a1bc5eb2b79d
https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.8.0-hd8ed1ab_0.conda#384462e63262a527bda564fa2d9126c0
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.4-pyhd8ed1ab_0.conda#18badd8fa3648d1beb1fcc7f2e0f756e
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.5-h98fc4e7_1.conda#483fe58e14ba244110cd1be2b771b70f
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.6-pyhd8ed1ab_0.conda#d5f8944ff9ab24a292511c83dce33dea
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.6-h98fc4e7_2.conda#1c95f7c612f9121353c4ef764678113e
https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-8.2.1-h3d44ed6_0.conda#98db5f8813f45e2b29766aff0e4a499c
https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.8.0-hd8ed1ab_0.conda#b279b07ce18058034e5b3606ba103a8b
https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h80fb2b6_112.conda#a19fa6cacf80c8a366572853d5890eb4
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.0-py310ha4c1d20_0.conda#1ac91334ffc1f3fd297319cd1c74b34e
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.0-py310hb13e2d6_0.conda#ac3b67e928cc71548efad9b522d42fef
https://conda.anaconda.org/conda-forge/noarch/pbr-5.11.1-pyhd8ed1ab_0.conda#5bde4ebca51438054099b9527c904ecb
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.10.0-pyhd8ed1ab_0.conda#0809187ef9b89a3d94a5c24d13936236
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.0-py310h24ef57a_1.conda#a689e86d7bbab67f889fc384aa72b088
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py310hc6cd4ac_4.conda#345beb10601d5360a15c033d68165a4f
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.11.0-pyhd8ed1ab_0.conda#8f567c0a74aa44cf732f15773b4083b0
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py310h32c33b7_2.conda#bfb5c8fe5b2cce3ca6140cbd61ecef3b
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py310hc6cd4ac_5.conda#ef5333594a958b25912002886b82b253
https://conda.anaconda.org/conda-forge/noarch/pytest-cov-4.1.0-pyhd8ed1ab_0.conda#06eb685a3a0b146347a58dda979485da
https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.3.1-pyhd8ed1ab_0.conda#816073bb54ef59f33f0f26c14f88311b
https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.1.0-pyhd8ed1ab_0.conda#6613dbb3b25cc648a107f33ca9f80fc1
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py310hbf28c38_3.tar.bz2#703ff1ac7d1b27fb5944b8052b5d1edb
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py310hde88566_1.tar.bz2#94ce7a76b0c912279f6958e0b6b21d2b
-https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py310hd41b1e2_0.conda#e00d52a8657a79b0a7c8c10559784759
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.2-pyhd8ed1ab_0.conda#cce7eeb7eda0124af186a5e9ce9b0fca
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.5-h8e1006c_1.conda#98206c865fccdea9723f0c6f9241a24f
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.29-pyhd8ed1ab_0.conda#5bdbb1cb692649720b60f261b41760cd
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_0.conda#3b8ef3a2d80f3d89d0ae7e3c975e6c57
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py310hd41b1e2_4.conda#35e87277fba9944b8a975113538bb5df
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py310h1f7b6fc_2.conda#7925aaa4330045bc32d334b20f446902
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py310hd41b1e2_1.conda#6a38f65d330b74495ad6990280486049
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.3-pyhd8ed1ab_0.conda#a7155483171dbc27a7385d1c26e779de
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.6-h8e1006c_2.conda#3d8e98279bad55287f2ef9047996f33c
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.30-pyhd8ed1ab_0.conda#b7a2e3bb89bda8c69839485c20aabadf
https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py310hde88566_1008.tar.bz2#f9dd8a7a2fcc23eb2cd95cd817c949e7
https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.1-nompi_hacb5139_102.conda#487a1c19dd3eacfd055ad614e9acde87
-https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.0-py310hcc13569_0.conda#6c92da4ec4e301d09a365c0584e632c8
+https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.1-py310hcc13569_1.conda#a64a2b4907b96d4bf3c9dab59563ab50
https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.14-ha41ecd1_2.conda#1a66c10f6a0da3dbd2f3a68127e7f6a0
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py310h0a54255_0.conda#b9e952fe3f7528ab603d2776175ba8d2
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.2-py310hb13e2d6_1.conda#4f522fc9cb8ecfa25e39f5c2ea65b16b
-https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py310h7dcad9a_2.conda#a46061c83ed37bfa05d1ee96ec2fbb08
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py310h1f7b6fc_1.conda#be6f0382440ccbf9fb01bb19ab1f1fc0
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.3-py310hb13e2d6_1.conda#4260b359d8fbeab4f789a8b0f968079f
+https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.2-py310h7dcad9a_0.conda#0d7c35fe5cc1f436e368ddd500deb979
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860
https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.24.4-pyhd8ed1ab_0.conda#c3feaf947264a59a125e8c26e98c3c5a
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py310h278f3c1_0.conda#f2d3f2542a2467f479e809ac7b901ac2
-https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.2-pyhd8ed1ab_0.conda#ddb4fd6105b4005b312625cef210ba67
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py310h1f7b6fc_3.conda#ce30848c8731fe993893a872218dd37a
+https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.3-pyhd8ed1ab_0.conda#543fafdd7b325bf16199235ee5f20622
https://conda.anaconda.org/conda-forge/linux-64/esmf-8.4.2-nompi_h9e768e6_3.conda#c330e87e698bae8e7381c0315cf25dd0
https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.56.3-h98fae49_0.conda#620e754f4344f4c27259ff460a2b9c50
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py310h62c0568_0.conda#0ba9c5af7a6cd0244a8ae2038c89317f
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py310hba70d50_102.conda#6025039727a049ab4c0f2aab842c01cb
-https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.4.0-pyha770c72_1.conda#3fb5ba328a77c9fd71197a46e7f2469a
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py310h278f3c1_0.conda#65d42fe14f56d55df8e93d67fa14c92d
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-hc47bfe8_16.conda#a8dd2dfcd570e3965c73be6c5e03e74f
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py310h62c0568_2.conda#5c0d101ef8fc542778aa80795a759d08
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py310hba70d50_103.conda#0850d2a119d51601b20c406a4909af4d
+https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.5.0-pyha770c72_0.conda#964e3d762e427661c59263435a14c492
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py310h1f7b6fc_1.conda#857b828a13cdddf568958f7575b25b22
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-h82b777d_17.conda#4f01e33dbb406085a16a2813ab067e95
https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.22.0-py310h7cbd5c2_0.conda#7bfbace0788f477da1c26e10a358692d
https://conda.anaconda.org/conda-forge/noarch/esmpy-8.4.2-pyhc1e730c_4.conda#ddcf387719b2e44df0cc4dd467643951
https://conda.anaconda.org/conda-forge/linux-64/graphviz-8.1.0-h28d9a01_0.conda#33628e0e3de7afd2c8172f76439894cb
https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py310h04931ad_4.conda#db878a0696f9a7980171fd3cf29cca22
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py310hff52083_0.conda#6c194758494847c927ad3bcf37fafa49
-https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.0-pyhd8ed1ab_0.conda#16cff214435f2a8163fbe67db9eafb96
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py310h04931ad_5.conda#f4fe7a6e3d7c78c9de048ea9dda21690
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py310hff52083_2.conda#cda26b4d722d7319ce66df50332ff09b
+https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.1-pyhd8ed1ab_0.conda#78153addf629c51fab775ef360012ca3
https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_0.conda#ac832cc43adc79118cf6e23f1f9b8995
https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.5.0-pyhd8ed1ab_0.conda#264b3c697fa9cdade87eb0abe4440d54
https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.14.0-pyhd8ed1ab_0.conda#b3788794f88c9512393032e448428261
@@ -270,4 +271,3 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.4-pyhd8
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.6-pyhd8ed1ab_0.conda#cf5c9649272c677a964a7313279e3a9b
https://conda.anaconda.org/conda-forge/noarch/sphinx-5.3.0-pyhd8ed1ab_0.tar.bz2#f9e1fcfe235d655900bfeb6aee426472
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.9-pyhd8ed1ab_0.conda#0612e497d7860728f2cda421ea2aec09
-
diff --git a/requirements/locks/py311-linux-64.lock b/requirements/locks/py311-linux-64.lock
index ff6f4ff76c5..0bbb6bfdcd2 100644
--- a/requirements/locks/py311-linux-64.lock
+++ b/requirements/locks/py311-linux-64.lock
@@ -1,6 +1,6 @@
# Generated by conda-lock.
# platform: linux-64
-# input_hash: e651bbc39258b157d888d85ff878fc5c3d62cc9a632c7b8bf515b62cd8d2da53
+# input_hash: 40113e38fffa3a31ce64e60231c756c740914d9f0444edaeecd07e598851abc8
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.7.22-hbcca054_0.conda#a73ecd2988327ad4c8f2c331482917f2
@@ -9,18 +9,18 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed3
https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-h41732ed_0.conda#7aca3059a1729aa76c597603f10b0dd3
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_1.conda#acfb4817400db5804030a3a7ef7909a1
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-3_cp311.conda#c2e2630ddb68cf52eec74dc7dfab20b5
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_2.conda#9172c297304f2a20134fc56c97fbe229
+https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-4_cp311.conda#d786502c97404c94d7d58d258a445a65
https://conda.anaconda.org/conda-forge/noarch/tzdata-2023c-h71feb2d_0.conda#939e3e74d8be4dac89ce83b20de2492a
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_1.conda#8bb001683321dcbde117a7337b5aace7
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_2.conda#e2042154faafe61969556f28bade94b9
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_1.conda#ff8999574b465089ba0aa25a5e865bd0
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_2.conda#c28003b0be0494f9a7664389146716ff
https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.10-hd590300_0.conda#75dae9a4201732aa78a530b826ee5fe0
https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
-https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.19.1-hd590300_0.conda#e8c18d865be43e2fb3f7a145b6adf1f5
+https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.20.1-hd590300_0.conda#6642e4faa4804be3a0e7edfefbd16595
https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.0-h59595ed_0.conda#3fdf79ef322c8379ae83be491d805369
https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
@@ -30,29 +30,29 @@ https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda#cc47e1
https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libaec-1.0.6-hcb278e6_1.conda#0f683578378cddb223e7fd24f785ab2a
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_0.conda#e805cbec4c29feb22e019245f7e47b6c
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.18-h0b41bf4_0.conda#6aa9c9de5542ecb07fdda9ca626252d8
+https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.2-h59595ed_1.conda#127b0be54c1c90760d7fe02ea7a56426
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_1.conda#aec6c91c7371c26392a06708a73c70e5
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.19-hd590300_0.conda#1635570038840ee3f9c71d22aa5b8b6d
https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.5.0-hcb278e6_1.conda#6305a3dd2752c76335295da4e581f2fd
https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_1.conda#a0d27fd5c6f05aa45e9602b1db49581c
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_2.conda#78fdab09d9138851dde2b5fe2a11019e
https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
-https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-2.1.5.1-hd590300_1.conda#323e90742f0f48fc22bea908735f55e6
+https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8
https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-hd590300_1.conda#854e3e1623b39777140f199c5f9ab952
https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.7-h27087fc_0.conda#f204c8ba400ec475452737094fb81d52
https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.1-hd590300_0.conda#82bf6f63eb15ef719b556b63feec3a77
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.2-hd590300_0.conda#30de3fd9b3b602f7473f30e684eeea8c
https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-hd590300_5.conda#f36c115f1ee199da648e0597ec2047ad
https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0
-https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.31.3-hcb278e6_0.conda#141a126675b6d1a4eabb111a4a353898
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.3-h59595ed_0.conda#bdadff838d5437aea83607ced8b37f75
https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.4-hcb278e6_0.conda#681105bccc2a3f7f1a837d47d39c9179
https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.2-hd590300_0.conda#e5ac5227582d6c83ccf247288c0eb095
-https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.3-hd590300_0.conda#7bb88ce04c8deb9f7d763ae04a1da72f
+https://conda.anaconda.org/conda-forge/linux-64/pixman-0.42.2-h59595ed_0.conda#700edd63ccd5fc66b70b1c028cea9a68
https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e6d228cd0bb74a51dd18f5bfce0b4115
https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -67,64 +67,65 @@ https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda#f0
https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h501b40f_6.conda#c3e9338e15d90106f467377017352b97
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_0.conda#43017394a280a42b48d11d2a6e169901
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_0.conda#8e3e1cb77c4b355a3776bdfb74095bed
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_1.conda#f07002e225d7a60a694d42a7bf5ff53f
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_1.conda#5fc11c6020d421960607d821310fcd4d
https://conda.anaconda.org/conda-forge/linux-64/libcap-2.69-h0f662aa_0.conda#25cb5999faa414e5ccb2c1388f62d3d5
https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d
https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_1.conda#394218a92951499aed2ab1bafb30b570
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_2.conda#e75a75a6eaf6f318dae2631158c46575
https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.47-h71f35ed_0.conda#c2097d0b46367996f09b4e8e4920384a
https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.52.0-h61bc06f_0.conda#613955a50485812985c059e7b269f42e
https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.0-h2797004_0.conda#903fa782a9067d5934210df6d79220f6
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.2-h2797004_0.conda#4b441a1ee22397d5a27dc1126b849edd
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe
+https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56
https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c
https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.11.5-h232c23b_1.conda#f3858448893839820d4bcfb14ad3ecdf
https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_4.conda#f6f0ac5665849afc0716213a6cff224d
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_5.conda#1e8ef4090ca4f0d66404a7441e1dbf3c
https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
-https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_1.conda#85552d64cb49f12781668779efc738ec
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-h2797004_0.conda#513336054f884f95d9fd925748f41ef3
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-h7391055_0.conda#93ee23f12bc2e684548181256edd2cf6
https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-hd590300_5.conda#68c34ec6149623be41a1933ab996a209
https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.5-hfc55251_0.conda#04b88013080254850d6c01ed54810589
https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.5-h0f2a231_0.conda#009521b7ed97cca25f8f997f9e745976
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_0.conda#aeafb07a327e3f14a796bf081ea07472
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_1.conda#39f910d205726805a958da408ca194ba
https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd95826dbd331ed1be26bdf401432844
https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-1.10.1-h166bdaf_0.tar.bz2#f967fc95089cd247ceed56eda31de3a9
https://conda.anaconda.org/conda-forge/linux-64/libglib-2.78.0-hebfc3b9_0.conda#e618003da3547216310088478e475945
https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.7-h5cf9203_3.conda#9efe82d44b76a7529a1d702e5a37752e
https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.24-pthreads_h413a1c8_0.conda#6e4ef6ca28655124dcde9bd500e44c32
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hbc2eb40_0.conda#38f84d395629e48b7c7b48a8ca740341
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.1-h8b53f26_1.conda#5b09e13d732dda1a2bc9adc711164f4d
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_4.conda#db7f2c877209ac620fcd1c3ce7407cf0
-https://conda.anaconda.org/conda-forge/linux-64/nss-3.92-h1d7d5a4_0.conda#22c89a3d87828fe925b310b9cdf0f574
-https://conda.anaconda.org/conda-forge/linux-64/python-3.11.5-hab00c5b_0_cpython.conda#f0288cb82594b1cbc71111d1cd3c5422
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.0-h2c6b66d_0.conda#713f9eac95d051abe14c3774376854fe
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-ha9c0a0a_2.conda#55ed21669b2015f77c180feb1dd41930
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_5.conda#b72f016c910ff9295b1377d3e17da3f2
+https://conda.anaconda.org/conda-forge/linux-64/nss-3.94-h1d7d5a4_0.conda#7caef74bbfa730e014b20f0852068509
+https://conda.anaconda.org/conda-forge/linux-64/python-3.11.6-hab00c5b_0_cpython.conda#b0dfbe2fcbfdb097d321bfd50ecddab1
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.2-h2c6b66d_0.conda#c37b95bcd6c6833dacfd5df0ae2f4303
+https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-hd590300_1.conda#9bfac7ccd94d54fd21a0501296d60424
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h8ee46fc_1.conda#632413adcd8bc16b515cab87a2932913
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-hd590300_1.conda#e995b155d938b6779da6ace6c6b13816
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h8ee46fc_1.conda#90108a432fb5c6150ccfee3f03388656
-https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.6-h8ee46fc_0.conda#7590b76c3d11d21caa44f3fc38ac584a
+https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.7-h8ee46fc_0.conda#49e482d882669206653b095f5206c05b
https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.13-pyhd8ed1ab_0.conda#06006184e203b61d3525f90de394471e
-https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py311h38be061_1003.tar.bz2#0ab8f8f0cae99343907fe68cda11baea
+https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.11.1-pyhd8ed1ab_0.tar.bz2#15109c4977d39ad7aa3423f57243e286
https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.1.0-pyhd8ed1ab_0.conda#0e8715bef534217eae333c53f645c9ed
https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_0.conda#3db48055eab680e43a122e2c7494e7ae
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_0.conda#b8128d083dbf6abd472b1a3e98b0b83d
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_1.conda#f27a24d46e3ea7b70a1f98e50c62508f
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_1.conda#cce9e7c3f1c307f2a5fb08a2922d6164
https://conda.anaconda.org/conda-forge/noarch/certifi-2023.7.22-pyhd8ed1ab_0.conda#7f3dbc9179b4dde7da98dfb151d0ad22
https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.2.0-pyhd8ed1ab_0.conda#313516e9a4b08b12dfb1e1cd390a96e3
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.0-pyhd8ed1ab_0.conda#fef8ef5f0a54546b9efee39468229917
https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca
https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.1-pyhd8ed1ab_0.conda#b325bfc4cff7d7f8a868f1f7ecc4ed16
https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
-https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
-https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.2-py311hb755f60_0.conda#81d4eacf7eb2d40beee33aa71e8f94ad
+https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda#5cd86562580f274031ede6aa6aa24441
+https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.3-py311hb755f60_0.conda#c54d71e8031a10d08f2e87ff81821588
https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.7-pyhd8ed1ab_0.conda#12d8aae6994f342618443a8f05c652a0
https://conda.anaconda.org/conda-forge/linux-64/docutils-0.19-py311h38be061_1.tar.bz2#599159b0740e9b82e7eef0e8471be3c2
@@ -132,41 +133,41 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.1.3-pyhd8ed1ab_0.
https://conda.anaconda.org/conda-forge/noarch/execnet-2.0.2-pyhd8ed1ab_0.conda#67de0d8241e1060a479e3c37793e26f9
https://conda.anaconda.org/conda-forge/noarch/filelock-3.12.4-pyhd8ed1ab_0.conda#5173d4b8267a0699a43d73231e0b6596
https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.1-pyh1a96a4e_0.conda#d69753ff6ee3c84a6638921dd95db662
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h6b639ba_2.conda#ee8220db21db8094998005990418fe5b
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.2-pyh1a96a4e_0.conda#9d15cd3a0e944594ab528da37dc72ecc
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h829c605_4.conda#252a696860674caf7a855e16f680d63a
https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.78.0-hfc55251_0.conda#e10134de3558dd95abda6987b5548f4f
https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe
https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5
https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py311h9547e67_0.conda#f53903649188b99e6b44c560c69f5b23
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-haa2dc70_1.conda#980d8aca0bc23ca73fa8caa3e7c84c28
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py311h9547e67_1.conda#2c65bdf442b0d37aad080c8a4e0d452f
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-hb7c19ff_3.conda#e96637dd92c5f340215c753a5c9a22d7
https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-18_linux64_openblas.conda#bcddbb497582ece559465b9cd11042e7
https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.7-default_h9986a30_3.conda#1720df000b48e31842500323cb7be18c
https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.3.0-hca28451_0.conda#4ab41bee09a2d2e08de5f09d6f1eef62
-https://conda.anaconda.org/conda-forge/linux-64/libpq-15.4-hfc447b1_0.conda#b9ce311e7aba8b5fc3122254f0a6e97e
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.4.0-hca28451_0.conda#1158ac1d2613b28685644931f11ee807
+https://conda.anaconda.org/conda-forge/linux-64/libpq-16.0-hfc447b1_1.conda#e4a9a5ba40123477db33e02a78dffb01
https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-254-h3516f8a_0.conda#df4b1cd0c91b4234fb02b5701a4cdddc
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.1-hbf2b3c1_0.conda#4963f3f12db45a576f2b8fbe9a0b8569
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.2-h658648e_1.conda#0ebb65e8d86843865796c7c95a941f34
https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py311h459d7ec_0.conda#9904dc4adb5d547cb21e136f98cb24b0
-https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.5-py311ha3edf6b_0.conda#7415f24f8c44e44152623d93c5015000
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py311h459d7ec_1.conda#71120b5155a0c500826cf81536721a15
+https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.6-py311h9547e67_0.conda#e826b71bf3dc8c91ee097663e2bcface
https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
-https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-hfec8fc6_2.conda#5ce6a42505c6e9e6151c54c3ec8d68ea
-https://conda.anaconda.org/conda-forge/noarch/packaging-23.1-pyhd8ed1ab_0.conda#91cda59e66e1e4afe9476f8ef98f5c30
+https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h488ebb8_3.conda#128c25b7fe6a25286a48f3a6a9b5b6f3
+https://conda.anaconda.org/conda-forge/noarch/packaging-23.2-pyhd8ed1ab_0.conda#79002079284aa895f883c6b7f3f88fd6
https://conda.anaconda.org/conda-forge/noarch/pluggy-1.3.0-pyhd8ed1ab_0.conda#2390bd10bed1f3fdc7a537fb5a447d8d
https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py311h2582759_0.conda#a90f8e278c1cd7064b2713e6b7db87e6
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py311h459d7ec_1.conda#490d7fa8675afd1aa6f1b2332d156a45
https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
https://conda.anaconda.org/conda-forge/noarch/pygments-2.16.1-pyhd8ed1ab_0.conda#40e5cb18165466773619e5c963f00a7b
https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.1-pyhd8ed1ab_0.conda#176f7d56f0cfe9008bdf1bccd7de02fb
https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.3-pyhd8ed1ab_0.conda#2590495f608a63625e165915fb4e2e34
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.3.0-py311h459d7ec_0.conda#87b306459b81b7a7aaad37222d537a4f
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.4.1-py311h459d7ec_0.conda#60b5332b3989fda37884b92c7afd6a91
https://conda.anaconda.org/conda-forge/noarch/pytz-2023.3.post1-pyhd8ed1ab_0.conda#c93346b446cd08c169d843ae5fc0da97
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py311h459d7ec_0.conda#30eaaf31141e785a445bf1ede6235fe3
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py311h459d7ec_1.conda#52719a74ad130de8fb5d047dc91f247a
https://conda.anaconda.org/conda-forge/noarch/setuptools-68.2.2-pyhd8ed1ab_0.conda#fc2166155db840c634a1291a5c35a709
https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
@@ -177,89 +178,89 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-2.0.0-pyhd8ed1ab_0.conda#f55
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py311h459d7ec_0.conda#7d9a31416c18704f55946ff7cf8da5dc
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py311h459d7ec_1.conda#a700fcb5cedd3e72d0c75d095c7a6eda
https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.8.0-pyha770c72_0.conda#5b1be40a26d10a06f6d4f1f9e19fa0c7
https://conda.anaconda.org/conda-forge/noarch/wheel-0.41.2-pyhd8ed1ab_0.conda#1ccd092478b3e0ee10d7a891adbf8a4f
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-h8ee46fc_1.conda#9d7bcddf49cbf727730af10e71022c73
-https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.39-hd590300_0.conda#d88c7fc8a11858fb14761832e4da1954
+https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.40-hd590300_0.conda#07c15d846a2e4d673da22cbd85fdb6d2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.11-hd590300_0.conda#ed67c36f215b310412b2af935bf3e530
https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_0.conda#cf30c2c15b82aacb07f9c09e28ff2275
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.16.2-pyhd8ed1ab_0.conda#2da0451b54c4563c32490cb1b7cf68a1
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.17.0-pyhd8ed1ab_0.conda#2e4d6bc0b14e10f895fc6791a7d9b26a
https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.4-pyhd8ed1ab_0.conda#46a2e6e3dfa718ce3492018d5a110dd6
-https://conda.anaconda.org/conda-forge/noarch/babel-2.12.1-pyhd8ed1ab_1.conda#ac432e732804a81ddcf29c92ead57cde
+https://conda.anaconda.org/conda-forge/noarch/babel-2.13.0-pyhd8ed1ab_0.conda#22541af7a9eb59fc6afcadb7ecdf9219
https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.2-pyha770c72_0.conda#a362ff7d976217f8fa78c0f1c4f59717
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h0c91306_1017.conda#3db543896d34fc6804ddfb9239dcb125
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py311h409f033_3.conda#9025d0786dbbe4bc91fd8e85502decce
-https://conda.anaconda.org/conda-forge/linux-64/coverage-7.3.1-py311h459d7ec_0.conda#d23df37f3a595e8ffca99642ab6df3eb
-https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py311h459d7ec_0.conda#5c416db47b7816e437eaf0d46e5c3a3d
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.42.1-py311h459d7ec_0.conda#fc327c0ea015db3b6484eabb37d44e60
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-h3faef2a_0.conda#f907bb958910dc404647326ca80c263e
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.16.0-py311hb3a22ac_0.conda#b3469563ac5e808b0cd92810d0697043
+https://conda.anaconda.org/conda-forge/linux-64/coverage-7.3.2-py311h459d7ec_0.conda#7b3145fed7adc7c63a0e08f6f29f5480
+https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py311h459d7ec_1.conda#afe341dbe834ae76d2c23157ff00e633
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.43.1-py311h459d7ec_0.conda#ac995b680de3bdce2531c553b27dfe7e
https://conda.anaconda.org/conda-forge/linux-64/glib-2.78.0-hfc55251_0.conda#2f55a36b549f51a7e0c2b1e3c3f0ccd4
https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.2-nompi_h4f84152_100.conda#2de6a9bc8083b49f09b2f6eb28d3ba3c
https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.8.0-pyha770c72_0.conda#4e9f59a060c3be52bc4ddc46ee9b6946
https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-18_linux64_openblas.conda#93dd9ab275ad888ed8113953769af78c
https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.7-default_h7634d5b_3.conda#0922208521c0463e690bbaebba7eb551
-https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h74d50f4_7.conda#3453ac94a99ad9daf17e8a313d274567
+https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h119a65a_9.conda#cfebc557e54905dadc355c0e9f003004
https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-18_linux64_openblas.conda#a1244707531e5b143c420c70573c8ec5
-https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.5.0-h5d7e998_3.conda#c91ea308d7bf70b62ddda568478aa03b
+https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.6.0-h5d7e998_0.conda#d8edd0e29db6fb6b6988e1a28d35d994
https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.8.0-pyhd8ed1ab_0.conda#2a75b296096adabbabadd5e9782e5fcc
-https://conda.anaconda.org/conda-forge/noarch/partd-1.4.0-pyhd8ed1ab_1.conda#6ceb4e000cbe0b56b290180aea8520e8
-https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.0-py311h0b84326_0.conda#4b24acdc1fbbae9da03147e7d2cf8c8a
+https://conda.anaconda.org/conda-forge/noarch/partd-1.4.1-pyhd8ed1ab_0.conda#acf4b7c0bcd5fa3b0e05801c4d2accd6
+https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.1-py311ha6c5da5_2.conda#d6de249502f16ac151fcef9f743937b9
https://conda.anaconda.org/conda-forge/noarch/pip-23.2.1-pyhd8ed1ab_0.conda#e2783aa3f9235225eec92f9081c5b801
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.2.1-ha643af7_0.conda#e992387307f4403ba0ec07d009032550
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.0-h1d62c97_1.conda#900fd11ac61d4415d515583fcb570207
https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-16.1-hb77b528_5.conda#ac902ff3c1c6d750dd0dfc93a974ab74
https://conda.anaconda.org/conda-forge/noarch/pytest-7.4.2-pyhd8ed1ab_0.conda#6dd662ff5ac9a783e5c940ce9f3fe649
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py311hb755f60_0.conda#17d25ab64a32872b349579fdb07bbdb2
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py311hb755f60_1.conda#e09eb6aad3607fb6f2c071a2c6a26e1d
https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.8.0-hd8ed1ab_0.conda#384462e63262a527bda564fa2d9126c0
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.4-pyhd8ed1ab_0.conda#18badd8fa3648d1beb1fcc7f2e0f756e
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.5-h98fc4e7_1.conda#483fe58e14ba244110cd1be2b771b70f
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.6-pyhd8ed1ab_0.conda#d5f8944ff9ab24a292511c83dce33dea
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.6-h98fc4e7_2.conda#1c95f7c612f9121353c4ef764678113e
https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-8.2.1-h3d44ed6_0.conda#98db5f8813f45e2b29766aff0e4a499c
https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.8.0-hd8ed1ab_0.conda#b279b07ce18058034e5b3606ba103a8b
https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h80fb2b6_112.conda#a19fa6cacf80c8a366572853d5890eb4
https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.0-py311h64a7726_0.conda#bf16a9f625126e378302f08e7ed67517
https://conda.anaconda.org/conda-forge/noarch/pbr-5.11.1-pyhd8ed1ab_0.conda#5bde4ebca51438054099b9527c904ecb
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.10.0-pyhd8ed1ab_0.conda#0809187ef9b89a3d94a5c24d13936236
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.0-py311ha169711_1.conda#92633556d37e88ce45193374d408072c
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py311hb755f60_4.conda#3cff4c98f775ff6439b95bb7917702e9
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.11.0-pyhd8ed1ab_0.conda#8f567c0a74aa44cf732f15773b4083b0
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py311h1facc83_2.conda#8298afb85a731b02dac82e02b6e13ae0
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py311hb755f60_5.conda#e4d262cc3600e70b505a6761d29f6207
https://conda.anaconda.org/conda-forge/noarch/pytest-cov-4.1.0-pyhd8ed1ab_0.conda#06eb685a3a0b146347a58dda979485da
https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.3.1-pyhd8ed1ab_0.conda#816073bb54ef59f33f0f26c14f88311b
https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.1.0-pyhd8ed1ab_0.conda#6613dbb3b25cc648a107f33ca9f80fc1
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311h4dd048b_3.tar.bz2#dbfea4376856bf7bd2121e719cf816e5
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py311h4c7f6c3_1.tar.bz2#c7e54004ffd03f8db0a58ab949f2a00b
-https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py311h9547e67_0.conda#db5b3b0093d0d4565e5c89578108402e
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.2-pyhd8ed1ab_0.conda#cce7eeb7eda0124af186a5e9ce9b0fca
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.5-h8e1006c_1.conda#98206c865fccdea9723f0c6f9241a24f
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.29-pyhd8ed1ab_0.conda#5bdbb1cb692649720b60f261b41760cd
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_0.conda#3b8ef3a2d80f3d89d0ae7e3c975e6c57
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311h9547e67_4.conda#586da7df03b68640de14dc3e8bcbf76f
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py311h1f0f07a_2.conda#571c0c47e8dbcf03577935ac818b6696
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py311h9547e67_1.conda#52d3de443952d33c5cee6b24b172ce96
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.3-pyhd8ed1ab_0.conda#a7155483171dbc27a7385d1c26e779de
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.6-h8e1006c_2.conda#3d8e98279bad55287f2ef9047996f33c
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.30-pyhd8ed1ab_0.conda#b7a2e3bb89bda8c69839485c20aabadf
https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py311h4c7f6c3_1008.tar.bz2#5998dff78c3b82a07ad77f2ae1ec1c44
https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.1-nompi_hacb5139_102.conda#487a1c19dd3eacfd055ad614e9acde87
-https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.0-py311h320fe9a_0.conda#7f35501e126df510b250ad893482ef45
+https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.1-py311h320fe9a_1.conda#a4371a95a8ae703a22949af28467b93d
https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.14-ha41ecd1_2.conda#1a66c10f6a0da3dbd2f3a68127e7f6a0
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py311hcb2cf0a_0.conda#272ca0c28df344037ba2c4982d4e4791
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.2-py311h64a7726_1.conda#58af16843fc4469770bdbaf45d3a19de
-https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py311he06c224_2.conda#10a1953d2f74d292b5de093ceea104b2
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py311h1f0f07a_1.conda#86b71ff85f3e4c8a98b5bace6d9c4565
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.3-py311h64a7726_1.conda#e4b4d3b764e2d029477d0db88248a8b5
+https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.2-py311he06c224_0.conda#c90e2469d7512f3bba893533a82d7a02
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860
https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.24.4-pyhd8ed1ab_0.conda#c3feaf947264a59a125e8c26e98c3c5a
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py311h1f0f07a_0.conda#43a71a823583d75308eaf3a06c8f150b
-https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.2-pyhd8ed1ab_0.conda#ddb4fd6105b4005b312625cef210ba67
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py311h1f0f07a_3.conda#4ac4de995f18d232af077e7743568b97
+https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.3-pyhd8ed1ab_0.conda#543fafdd7b325bf16199235ee5f20622
https://conda.anaconda.org/conda-forge/linux-64/esmf-8.4.2-nompi_h9e768e6_3.conda#c330e87e698bae8e7381c0315cf25dd0
https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.56.3-h98fae49_0.conda#620e754f4344f4c27259ff460a2b9c50
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py311h54ef318_0.conda#b67672c2f39ef2912a1814e29e42c7ca
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py311he8ad708_102.conda#b48083ba918347f30efa94f7dc694919
-https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.4.0-pyha770c72_1.conda#3fb5ba328a77c9fd71197a46e7f2469a
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py311h1f0f07a_0.conda#3a00b1b08d8c01b1a3bfa686b9152df2
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-hc47bfe8_16.conda#a8dd2dfcd570e3965c73be6c5e03e74f
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py311h54ef318_2.conda#5655371cc61b8c31c369a7e709acb294
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py311he8ad708_103.conda#97b45ba4ff4e46a07dd6c60040256538
+https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.5.0-pyha770c72_0.conda#964e3d762e427661c59263435a14c492
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py311h1f0f07a_1.conda#cd36a89a048ad2bcc6d8b43f648fb1d0
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-h82b777d_17.conda#4f01e33dbb406085a16a2813ab067e95
https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.22.0-py311h320fe9a_0.conda#1271b2375735e2aaa6d6770dbe2ad087
https://conda.anaconda.org/conda-forge/noarch/esmpy-8.4.2-pyhc1e730c_4.conda#ddcf387719b2e44df0cc4dd467643951
https://conda.anaconda.org/conda-forge/linux-64/graphviz-8.1.0-h28d9a01_0.conda#33628e0e3de7afd2c8172f76439894cb
https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py311hf0fb5b6_4.conda#afe5363b88d2e97266063558a6599bd0
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py311h38be061_0.conda#8148b139a0560666d661cf1d179a0cca
-https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.0-pyhd8ed1ab_0.conda#16cff214435f2a8163fbe67db9eafb96
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py311hf0fb5b6_5.conda#ec7e45bc76d9d0b69a74a2075932b8e8
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py311h38be061_2.conda#0289918d4a09bbd0b85fd23ddf1c3ac1
+https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.1-pyhd8ed1ab_0.conda#78153addf629c51fab775ef360012ca3
https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_0.conda#ac832cc43adc79118cf6e23f1f9b8995
https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.5.0-pyhd8ed1ab_0.conda#264b3c697fa9cdade87eb0abe4440d54
https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.14.0-pyhd8ed1ab_0.conda#b3788794f88c9512393032e448428261
@@ -269,4 +270,3 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.4-pyhd8
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.6-pyhd8ed1ab_0.conda#cf5c9649272c677a964a7313279e3a9b
https://conda.anaconda.org/conda-forge/noarch/sphinx-5.3.0-pyhd8ed1ab_0.tar.bz2#f9e1fcfe235d655900bfeb6aee426472
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.9-pyhd8ed1ab_0.conda#0612e497d7860728f2cda421ea2aec09
-
diff --git a/requirements/locks/py39-linux-64.lock b/requirements/locks/py39-linux-64.lock
index fc574db4f36..167fc29e4c9 100644
--- a/requirements/locks/py39-linux-64.lock
+++ b/requirements/locks/py39-linux-64.lock
@@ -1,6 +1,6 @@
# Generated by conda-lock.
# platform: linux-64
-# input_hash: 8b81c2e9972c5059e1b9013a49eddbd4697c92807d6f5d5282350b6c6d0dc518
+# input_hash: cc8b627bc99f75128e66e8d5f19fad191f76de7f27898db96e0eef7d6dc6e83a
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.7.22-hbcca054_0.conda#a73ecd2988327ad4c8f2c331482917f2
@@ -9,18 +9,18 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed3
https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-h41732ed_0.conda#7aca3059a1729aa76c597603f10b0dd3
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_1.conda#acfb4817400db5804030a3a7ef7909a1
-https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-3_cp39.conda#0dd193187d54e585cac7eab942a8847e
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_2.conda#9172c297304f2a20134fc56c97fbe229
+https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-4_cp39.conda#bfe4b3259a8ac6cdf0037752904da6a7
https://conda.anaconda.org/conda-forge/noarch/tzdata-2023c-h71feb2d_0.conda#939e3e74d8be4dac89ce83b20de2492a
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_1.conda#8bb001683321dcbde117a7337b5aace7
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_2.conda#e2042154faafe61969556f28bade94b9
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_1.conda#ff8999574b465089ba0aa25a5e865bd0
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_2.conda#c28003b0be0494f9a7664389146716ff
https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.10-hd590300_0.conda#75dae9a4201732aa78a530b826ee5fe0
https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
-https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.19.1-hd590300_0.conda#e8c18d865be43e2fb3f7a145b6adf1f5
+https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.20.1-hd590300_0.conda#6642e4faa4804be3a0e7edfefbd16595
https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.0-h59595ed_0.conda#3fdf79ef322c8379ae83be491d805369
https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
@@ -30,29 +30,29 @@ https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda#cc47e1
https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libaec-1.0.6-hcb278e6_1.conda#0f683578378cddb223e7fd24f785ab2a
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_0.conda#e805cbec4c29feb22e019245f7e47b6c
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.18-h0b41bf4_0.conda#6aa9c9de5542ecb07fdda9ca626252d8
+https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.2-h59595ed_1.conda#127b0be54c1c90760d7fe02ea7a56426
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hd590300_1.conda#aec6c91c7371c26392a06708a73c70e5
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.19-hd590300_0.conda#1635570038840ee3f9c71d22aa5b8b6d
https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.5.0-hcb278e6_1.conda#6305a3dd2752c76335295da4e581f2fd
https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_1.conda#a0d27fd5c6f05aa45e9602b1db49581c
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_2.conda#78fdab09d9138851dde2b5fe2a11019e
https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
-https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-2.1.5.1-hd590300_1.conda#323e90742f0f48fc22bea908735f55e6
+https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8
https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
-https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
+https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-hd590300_1.conda#854e3e1623b39777140f199c5f9ab952
https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.7-h27087fc_0.conda#f204c8ba400ec475452737094fb81d52
https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.1-hd590300_0.conda#82bf6f63eb15ef719b556b63feec3a77
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.3.2-hd590300_0.conda#30de3fd9b3b602f7473f30e684eeea8c
https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-hd590300_5.conda#f36c115f1ee199da648e0597ec2047ad
https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0
-https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.31.3-hcb278e6_0.conda#141a126675b6d1a4eabb111a4a353898
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.3-h59595ed_0.conda#bdadff838d5437aea83607ced8b37f75
https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.4-hcb278e6_0.conda#681105bccc2a3f7f1a837d47d39c9179
https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1
-https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.2-hd590300_0.conda#e5ac5227582d6c83ccf247288c0eb095
-https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
+https://conda.anaconda.org/conda-forge/linux-64/openssl-3.1.3-hd590300_0.conda#7bb88ce04c8deb9f7d763ae04a1da72f
+https://conda.anaconda.org/conda-forge/linux-64/pixman-0.42.2-h59595ed_0.conda#700edd63ccd5fc66b70b1c028cea9a68
https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e6d228cd0bb74a51dd18f5bfce0b4115
https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -67,64 +67,65 @@ https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.2-hd590300_0.conda#f0
https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h501b40f_6.conda#c3e9338e15d90106f467377017352b97
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_0.conda#43017394a280a42b48d11d2a6e169901
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_0.conda#8e3e1cb77c4b355a3776bdfb74095bed
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_1.conda#f07002e225d7a60a694d42a7bf5ff53f
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_1.conda#5fc11c6020d421960607d821310fcd4d
https://conda.anaconda.org/conda-forge/linux-64/libcap-2.69-h0f662aa_0.conda#25cb5999faa414e5ccb2c1388f62d3d5
https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d
https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_1.conda#394218a92951499aed2ab1bafb30b570
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_2.conda#e75a75a6eaf6f318dae2631158c46575
https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.47-h71f35ed_0.conda#c2097d0b46367996f09b4e8e4920384a
https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.52.0-h61bc06f_0.conda#613955a50485812985c059e7b269f42e
https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.0-h2797004_0.conda#903fa782a9067d5934210df6d79220f6
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.43.2-h2797004_0.conda#4b441a1ee22397d5a27dc1126b849edd
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe
+https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56
https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c
https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.11.5-h232c23b_1.conda#f3858448893839820d4bcfb14ad3ecdf
https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_4.conda#f6f0ac5665849afc0716213a6cff224d
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.33-hf1915f5_5.conda#1e8ef4090ca4f0d66404a7441e1dbf3c
https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
-https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_1.conda#85552d64cb49f12781668779efc738ec
+https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-h2797004_0.conda#513336054f884f95d9fd925748f41ef3
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-h7391055_0.conda#93ee23f12bc2e684548181256edd2cf6
https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-hd590300_5.conda#68c34ec6149623be41a1933ab996a209
https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.5-hfc55251_0.conda#04b88013080254850d6c01ed54810589
https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.5-h0f2a231_0.conda#009521b7ed97cca25f8f997f9e745976
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_0.conda#aeafb07a327e3f14a796bf081ea07472
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_1.conda#39f910d205726805a958da408ca194ba
https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd95826dbd331ed1be26bdf401432844
https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-1.10.1-h166bdaf_0.tar.bz2#f967fc95089cd247ceed56eda31de3a9
https://conda.anaconda.org/conda-forge/linux-64/libglib-2.78.0-hebfc3b9_0.conda#e618003da3547216310088478e475945
https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.7-h5cf9203_3.conda#9efe82d44b76a7529a1d702e5a37752e
https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.24-pthreads_h413a1c8_0.conda#6e4ef6ca28655124dcde9bd500e44c32
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hbc2eb40_0.conda#38f84d395629e48b7c7b48a8ca740341
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.5.1-h8b53f26_1.conda#5b09e13d732dda1a2bc9adc711164f4d
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_4.conda#db7f2c877209ac620fcd1c3ce7407cf0
-https://conda.anaconda.org/conda-forge/linux-64/nss-3.92-h1d7d5a4_0.conda#22c89a3d87828fe925b310b9cdf0f574
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.6.0-ha9c0a0a_2.conda#55ed21669b2015f77c180feb1dd41930
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.33-hca2cd23_5.conda#b72f016c910ff9295b1377d3e17da3f2
+https://conda.anaconda.org/conda-forge/linux-64/nss-3.94-h1d7d5a4_0.conda#7caef74bbfa730e014b20f0852068509
https://conda.anaconda.org/conda-forge/linux-64/python-3.9.18-h0755675_0_cpython.conda#3ede353bc605068d9677e700b1847382
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.0-h2c6b66d_0.conda#713f9eac95d051abe14c3774376854fe
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.43.2-h2c6b66d_0.conda#c37b95bcd6c6833dacfd5df0ae2f4303
+https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-hd590300_1.conda#9bfac7ccd94d54fd21a0501296d60424
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h8ee46fc_1.conda#632413adcd8bc16b515cab87a2932913
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-hd590300_1.conda#e995b155d938b6779da6ace6c6b13816
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h8ee46fc_1.conda#90108a432fb5c6150ccfee3f03388656
-https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.6-h8ee46fc_0.conda#7590b76c3d11d21caa44f3fc38ac584a
+https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.7-h8ee46fc_0.conda#49e482d882669206653b095f5206c05b
https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.13-pyhd8ed1ab_0.conda#06006184e203b61d3525f90de394471e
-https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py39hf3d152e_1003.tar.bz2#5e8330e806e50bd6137ebd125f4bc1bb
+https://conda.anaconda.org/conda-forge/noarch/antlr-python-runtime-4.11.1-pyhd8ed1ab_0.tar.bz2#15109c4977d39ad7aa3423f57243e286
https://conda.anaconda.org/conda-forge/noarch/asv_runner-0.1.0-pyhd8ed1ab_0.conda#0e8715bef534217eae333c53f645c9ed
https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_0.conda#3db48055eab680e43a122e2c7494e7ae
-https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39h3d6467e_0.conda#8a1b6b1f5e230aaf6408d6b0aef3492f
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_1.conda#f27a24d46e3ea7b70a1f98e50c62508f
+https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39h3d6467e_1.conda#c48418c8b35f1d59ae9ae1174812b40a
https://conda.anaconda.org/conda-forge/noarch/certifi-2023.7.22-pyhd8ed1ab_0.conda#7f3dbc9179b4dde7da98dfb151d0ad22
https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
-https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.2.0-pyhd8ed1ab_0.conda#313516e9a4b08b12dfb1e1cd390a96e3
+https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.0-pyhd8ed1ab_0.conda#fef8ef5f0a54546b9efee39468229917
https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca
https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.1-pyhd8ed1ab_0.conda#b325bfc4cff7d7f8a868f1f7ecc4ed16
https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
-https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
-https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.2-py39h3d6467e_0.conda#f90bb794d0f7463fbe28596796aa0100
+https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda#5cd86562580f274031ede6aa6aa24441
+https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.3-py39h3d6467e_0.conda#13febcb5470ba004eeb3e7883fa66e79
https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.7-pyhd8ed1ab_0.conda#12d8aae6994f342618443a8f05c652a0
https://conda.anaconda.org/conda-forge/linux-64/docutils-0.19-py39hf3d152e_1.tar.bz2#adb733ec2ee669f6d010758d054da60f
@@ -132,41 +133,41 @@ https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.1.3-pyhd8ed1ab_0.
https://conda.anaconda.org/conda-forge/noarch/execnet-2.0.2-pyhd8ed1ab_0.conda#67de0d8241e1060a479e3c37793e26f9
https://conda.anaconda.org/conda-forge/noarch/filelock-3.12.4-pyhd8ed1ab_0.conda#5173d4b8267a0699a43d73231e0b6596
https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.1-pyh1a96a4e_0.conda#d69753ff6ee3c84a6638921dd95db662
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h6b639ba_2.conda#ee8220db21db8094998005990418fe5b
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.9.2-pyh1a96a4e_0.conda#9d15cd3a0e944594ab528da37dc72ecc
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h829c605_4.conda#252a696860674caf7a855e16f680d63a
https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.78.0-hfc55251_0.conda#e10134de3558dd95abda6987b5548f4f
https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda#4d8df0b0db060d33c9a702ada998a8fe
https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5
https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py39h7633fee_0.conda#3822b0ae733e022c10469c0e46bdddc4
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-haa2dc70_1.conda#980d8aca0bc23ca73fa8caa3e7c84c28
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.5-py39h7633fee_1.conda#c9f74d717e5a2847a9f8b779c54130f2
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.15-hb7c19ff_3.conda#e96637dd92c5f340215c753a5c9a22d7
https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-18_linux64_openblas.conda#bcddbb497582ece559465b9cd11042e7
https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.7-default_h9986a30_3.conda#1720df000b48e31842500323cb7be18c
https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.3.0-hca28451_0.conda#4ab41bee09a2d2e08de5f09d6f1eef62
-https://conda.anaconda.org/conda-forge/linux-64/libpq-15.4-hfc447b1_0.conda#b9ce311e7aba8b5fc3122254f0a6e97e
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.4.0-hca28451_0.conda#1158ac1d2613b28685644931f11ee807
+https://conda.anaconda.org/conda-forge/linux-64/libpq-16.0-hfc447b1_1.conda#e4a9a5ba40123477db33e02a78dffb01
https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-254-h3516f8a_0.conda#df4b1cd0c91b4234fb02b5701a4cdddc
-https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.1-hbf2b3c1_0.conda#4963f3f12db45a576f2b8fbe9a0b8569
+https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.2-h658648e_1.conda#0ebb65e8d86843865796c7c95a941f34
https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py39hd1e30aa_0.conda#9c858d105816f454c6b64f3e19184b60
-https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.5-py39h4b4f3f3_0.conda#413374bab5022a5199c5dd89aef75df5
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.3-py39hd1e30aa_1.conda#ee2b4665b852ec6ff2758f3c1b91233d
+https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.6-py39h7633fee_0.conda#e39816a8abd539079a9d0b3c9045b2cb
https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
-https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-hfec8fc6_2.conda#5ce6a42505c6e9e6151c54c3ec8d68ea
-https://conda.anaconda.org/conda-forge/noarch/packaging-23.1-pyhd8ed1ab_0.conda#91cda59e66e1e4afe9476f8ef98f5c30
+https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h488ebb8_3.conda#128c25b7fe6a25286a48f3a6a9b5b6f3
+https://conda.anaconda.org/conda-forge/noarch/packaging-23.2-pyhd8ed1ab_0.conda#79002079284aa895f883c6b7f3f88fd6
https://conda.anaconda.org/conda-forge/noarch/pluggy-1.3.0-pyhd8ed1ab_0.conda#2390bd10bed1f3fdc7a537fb5a447d8d
https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py39h72bdee0_0.conda#1d54d3a75c3192ab7655d9c3d16809f1
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.5-py39hd1e30aa_1.conda#c2e412b0f11e5983bcfc35d9beb91ecb
https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
https://conda.anaconda.org/conda-forge/noarch/pygments-2.16.1-pyhd8ed1ab_0.conda#40e5cb18165466773619e5c963f00a7b
https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.1.1-pyhd8ed1ab_0.conda#176f7d56f0cfe9008bdf1bccd7de02fb
https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.3-pyhd8ed1ab_0.conda#2590495f608a63625e165915fb4e2e34
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.3.0-py39hd1e30aa_0.conda#41841cc1d7387bb7a30cdde4d88afbf4
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.4.1-py39hd1e30aa_0.conda#756cb152772a225587a05ca0ec68fc08
https://conda.anaconda.org/conda-forge/noarch/pytz-2023.3.post1-pyhd8ed1ab_0.conda#c93346b446cd08c169d843ae5fc0da97
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py39hd1e30aa_0.conda#ccecb3196b3678e9b5fc8441d681c203
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py39hd1e30aa_1.conda#37218233bcdc310e4fde6453bc1b40d8
https://conda.anaconda.org/conda-forge/noarch/setuptools-68.2.2-pyhd8ed1ab_0.conda#fc2166155db840c634a1291a5c35a709
https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
@@ -177,90 +178,90 @@ https://conda.anaconda.org/conda-forge/noarch/tblib-2.0.0-pyhd8ed1ab_0.conda#f55
https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py39hd1e30aa_0.conda#ee7f18d58a96b04fdbd2e55f7694ae0d
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.3.3-py39hd1e30aa_1.conda#cbe186eefb0bcd91e8f47c3908489874
https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.8.0-pyha770c72_0.conda#5b1be40a26d10a06f6d4f1f9e19fa0c7
-https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py39hb9d737c_0.tar.bz2#230d65004135bf312504a1bbcb0c7a08
+https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39hd1e30aa_0.conda#1da984bbb6e765743e13388ba7b7b2c8
https://conda.anaconda.org/conda-forge/noarch/wheel-0.41.2-pyhd8ed1ab_0.conda#1ccd092478b3e0ee10d7a891adbf8a4f
https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-h8ee46fc_1.conda#9d7bcddf49cbf727730af10e71022c73
-https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.39-hd590300_0.conda#d88c7fc8a11858fb14761832e4da1954
+https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.40-hd590300_0.conda#07c15d846a2e4d673da22cbd85fdb6d2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h0b41bf4_2.conda#82b6df12252e6f32402b96dacc656fec
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.11-hd590300_0.conda#ed67c36f215b310412b2af935bf3e530
https://conda.anaconda.org/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_0.conda#cf30c2c15b82aacb07f9c09e28ff2275
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.16.2-pyhd8ed1ab_0.conda#2da0451b54c4563c32490cb1b7cf68a1
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.17.0-pyhd8ed1ab_0.conda#2e4d6bc0b14e10f895fc6791a7d9b26a
https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.4-pyhd8ed1ab_0.conda#46a2e6e3dfa718ce3492018d5a110dd6
-https://conda.anaconda.org/conda-forge/noarch/babel-2.12.1-pyhd8ed1ab_1.conda#ac432e732804a81ddcf29c92ead57cde
+https://conda.anaconda.org/conda-forge/noarch/babel-2.13.0-pyhd8ed1ab_0.conda#22541af7a9eb59fc6afcadb7ecdf9219
https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.2-pyha770c72_0.conda#a362ff7d976217f8fa78c0f1c4f59717
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h0c91306_1017.conda#3db543896d34fc6804ddfb9239dcb125
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py39he91dace_3.conda#20080319ef73fbad74dcd6d62f2a3ffe
-https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py39hd1e30aa_0.conda#434246edfc30e20c0847d4c2caff0a53
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.42.1-py39hd1e30aa_0.conda#de06dc7edaddbd3b60c050f3a95d6fe6
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-h3faef2a_0.conda#f907bb958910dc404647326ca80c263e
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.16.0-py39h7a31438_0.conda#ac992767d7f8ed2cb27e71e78f0fb2d7
+https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py39hd1e30aa_1.conda#e5b62f0c1f96413116f16d33973f1a44
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.43.1-py39hd1e30aa_0.conda#74b032179f7782051800908cb2250132
https://conda.anaconda.org/conda-forge/linux-64/glib-2.78.0-hfc55251_0.conda#2f55a36b549f51a7e0c2b1e3c3f0ccd4
https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.2-nompi_h4f84152_100.conda#2de6a9bc8083b49f09b2f6eb28d3ba3c
https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-6.8.0-pyha770c72_0.conda#4e9f59a060c3be52bc4ddc46ee9b6946
-https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.0.1-pyhd8ed1ab_0.conda#d978c61aa5fc2c69380d53ad56b5ae86
+https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.1.0-pyhd8ed1ab_0.conda#48b0d98e0c0ec810d3ccc2a0926c8c0e
https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-18_linux64_openblas.conda#93dd9ab275ad888ed8113953769af78c
https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.7-default_h7634d5b_3.conda#0922208521c0463e690bbaebba7eb551
-https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h74d50f4_7.conda#3453ac94a99ad9daf17e8a313d274567
+https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h119a65a_9.conda#cfebc557e54905dadc355c0e9f003004
https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-18_linux64_openblas.conda#a1244707531e5b143c420c70573c8ec5
-https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.5.0-h5d7e998_3.conda#c91ea308d7bf70b62ddda568478aa03b
+https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.6.0-h5d7e998_0.conda#d8edd0e29db6fb6b6988e1a28d35d994
https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.8.0-pyhd8ed1ab_0.conda#2a75b296096adabbabadd5e9782e5fcc
-https://conda.anaconda.org/conda-forge/noarch/partd-1.4.0-pyhd8ed1ab_1.conda#6ceb4e000cbe0b56b290180aea8520e8
-https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.0-py39haaeba84_0.conda#f97a95fab7c69678ebf6b57396b1323e
+https://conda.anaconda.org/conda-forge/noarch/partd-1.4.1-pyhd8ed1ab_0.conda#acf4b7c0bcd5fa3b0e05801c4d2accd6
+https://conda.anaconda.org/conda-forge/linux-64/pillow-10.0.1-py39had0adad_2.conda#4d5990bb620ed36b10a528324d9b75e3
https://conda.anaconda.org/conda-forge/noarch/pip-23.2.1-pyhd8ed1ab_0.conda#e2783aa3f9235225eec92f9081c5b801
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.2.1-ha643af7_0.conda#e992387307f4403ba0ec07d009032550
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.0-h1d62c97_1.conda#900fd11ac61d4415d515583fcb570207
https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-16.1-hb77b528_5.conda#ac902ff3c1c6d750dd0dfc93a974ab74
https://conda.anaconda.org/conda-forge/noarch/pytest-7.4.2-pyhd8ed1ab_0.conda#6dd662ff5ac9a783e5c940ce9f3fe649
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py39h3d6467e_0.conda#4eaef850715aff114e2126a2f1a7b1f0
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.11-py39h3d6467e_1.conda#39d2473881976eeb57c09c106d2d9fc3
https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.8.0-hd8ed1ab_0.conda#384462e63262a527bda564fa2d9126c0
-https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.4-pyhd8ed1ab_0.conda#18badd8fa3648d1beb1fcc7f2e0f756e
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.5-h98fc4e7_1.conda#483fe58e14ba244110cd1be2b771b70f
+https://conda.anaconda.org/conda-forge/noarch/urllib3-2.0.6-pyhd8ed1ab_0.conda#d5f8944ff9ab24a292511c83dce33dea
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.22.6-h98fc4e7_2.conda#1c95f7c612f9121353c4ef764678113e
https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-8.2.1-h3d44ed6_0.conda#98db5f8813f45e2b29766aff0e4a499c
-https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.0.1-pyhd8ed1ab_0.conda#54661981fd331e20847d8a49543dd9af
+https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.1.0-pyhd8ed1ab_0.conda#6a62c2cc25376a0d050b3d1d221c3ee9
https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-6.8.0-hd8ed1ab_0.conda#b279b07ce18058034e5b3606ba103a8b
https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h80fb2b6_112.conda#a19fa6cacf80c8a366572853d5890eb4
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.0-py39h6183b62_0.conda#a50279322335a176d74ed167f9ce468b
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.0-py39h474f0d3_0.conda#62f1d2e05327bf62728afa448f2a9261
https://conda.anaconda.org/conda-forge/noarch/pbr-5.11.1-pyhd8ed1ab_0.conda#5bde4ebca51438054099b9527c904ecb
-https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.10.0-pyhd8ed1ab_0.conda#0809187ef9b89a3d94a5c24d13936236
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.0-py39h5ed0f51_1.conda#9c455b3b3b55f13b2094932740cd3efb
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py39h3d6467e_4.conda#b83a218fa97e9963c858d0db651a7506
+https://conda.anaconda.org/conda-forge/noarch/platformdirs-3.11.0-pyhd8ed1ab_0.conda#8f567c0a74aa44cf732f15773b4083b0
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py39hce394fd_2.conda#cb5ecd8db6d8ca8b9f281658a8512433
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py39h3d6467e_5.conda#93aff412f3e49fdb43361c0215cbd72d
https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.3.1-pyhd8ed1ab_0.conda#816073bb54ef59f33f0f26c14f88311b
https://conda.anaconda.org/conda-forge/noarch/requests-2.31.0-pyhd8ed1ab_0.conda#a30144e4156cdbb236f99ebb49828f8b
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.1.0-pyhd8ed1ab_0.conda#6613dbb3b25cc648a107f33ca9f80fc1
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py39hf939315_3.tar.bz2#0f11bcdf9669a5ae0f39efd8c830209a
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py39h2ae25f5_1.tar.bz2#c943fb9a2818ecc5be1e0ecc8b7738f1
-https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py39h7633fee_0.conda#b673f03c191683996e66c881f90aff2b
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.2-pyhd8ed1ab_0.conda#cce7eeb7eda0124af186a5e9ce9b0fca
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.5-h8e1006c_1.conda#98206c865fccdea9723f0c6f9241a24f
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.29-pyhd8ed1ab_0.conda#5bdbb1cb692649720b60f261b41760cd
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-8.0.4-pyhd8ed1ab_0.conda#3b8ef3a2d80f3d89d0ae7e3c975e6c57
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py39h7633fee_4.conda#b66595fbda99771266f042f42c7457be
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py39h44dd56e_2.conda#bb788b462770a49433d7412e7881d917
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.1.1-py39h7633fee_1.conda#33afb3357cd0d120ecb26778d37579e4
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2023.9.3-pyhd8ed1ab_0.conda#a7155483171dbc27a7385d1c26e779de
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.22.6-h8e1006c_2.conda#3d8e98279bad55287f2ef9047996f33c
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.30-pyhd8ed1ab_0.conda#b7a2e3bb89bda8c69839485c20aabadf
https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py39h2ae25f5_1008.tar.bz2#d90acb3804f16c63eb6726652e4e25b3
https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.1-nompi_hacb5139_102.conda#487a1c19dd3eacfd055ad614e9acde87
-https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.0-py39hddac248_0.conda#0a3624f600f51df010a274176e356ac5
+https://conda.anaconda.org/conda-forge/linux-64/pandas-2.1.1-py39hddac248_1.conda#f32809db710b8aac48fbc14c13058530
https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.14-ha41ecd1_2.conda#1a66c10f6a0da3dbd2f3a68127e7f6a0
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py39h389d5f1_0.conda#9eeb2b2549f836ca196c6cbd22344122
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.2-py39h474f0d3_1.conda#f62409d868e23c1f97ae2b0db5658385
-https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.1-py39h1bc45ef_2.conda#d79ed0ee1738151284ebd97092a6a210
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.4.1-py39h44dd56e_1.conda#d037c20e3da2e85f03ebd20ad480c359
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.3-py39h474f0d3_1.conda#55441724fedb3042d38ffa5220f00804
+https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.2-py39h1bc45ef_0.conda#ca067895d22f8a0d38f225a95184858e
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-apidoc-0.3.0-py_1.tar.bz2#855b087883443abb10f5faf6eef40860
https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.24.4-pyhd8ed1ab_0.conda#c3feaf947264a59a125e8c26e98c3c5a
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py39h0f8d45d_0.conda#180d4312005bc93f257e2997a8ee41cb
-https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.2-pyhd8ed1ab_0.conda#ddb4fd6105b4005b312625cef210ba67
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py39h44dd56e_3.conda#cbc2fe7741df3546448a534827238c32
+https://conda.anaconda.org/conda-forge/noarch/distributed-2023.9.3-pyhd8ed1ab_0.conda#543fafdd7b325bf16199235ee5f20622
https://conda.anaconda.org/conda-forge/linux-64/esmf-8.4.2-nompi_h9e768e6_3.conda#c330e87e698bae8e7381c0315cf25dd0
https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.56.3-h98fae49_0.conda#620e754f4344f4c27259ff460a2b9c50
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py39he9076e7_0.conda#a529a20267af9f085c7f991cae79fef2
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py39h4282601_102.conda#05390bd5ad0ddc2f719392d087673344
-https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.4.0-pyha770c72_1.conda#3fb5ba328a77c9fd71197a46e7f2469a
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py39h0f8d45d_0.conda#74b1d479057aa11a70779c83262df85e
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-hc47bfe8_16.conda#a8dd2dfcd570e3965c73be6c5e03e74f
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.8.0-py39he9076e7_2.conda#404144d0628ebbbbd56d161c677cc71b
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.4-nompi_py39h4282601_103.conda#c61de71bd3099973376aa370e3a0b39e
+https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.5.0-pyha770c72_0.conda#964e3d762e427661c59263435a14c492
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py39h44dd56e_1.conda#90c5165691fdcb5a9f43907e32ea48b4
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-h82b777d_17.conda#4f01e33dbb406085a16a2813ab067e95
https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.22.0-py39h40cae4c_0.conda#24b4bf92e26a46217e37e5928927116b
https://conda.anaconda.org/conda-forge/noarch/esmpy-8.4.2-pyhc1e730c_4.conda#ddcf387719b2e44df0cc4dd467643951
https://conda.anaconda.org/conda-forge/linux-64/graphviz-8.1.0-h28d9a01_0.conda#33628e0e3de7afd2c8172f76439894cb
https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py39h52134e7_4.conda#e12391692d70732bf1df08b7ecf40095
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py39hf3d152e_0.conda#e348333b50ff1f978f3d6af24512de0b
-https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.0-pyhd8ed1ab_0.conda#16cff214435f2a8163fbe67db9eafb96
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py39h52134e7_5.conda#e1f148e57d071b09187719df86f513c1
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.8.0-py39hf3d152e_2.conda#ffe5ae58957da676064e2ce5d039d259
+https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.1-pyhd8ed1ab_0.conda#78153addf629c51fab775ef360012ca3
https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_0.conda#ac832cc43adc79118cf6e23f1f9b8995
https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.5.0-pyhd8ed1ab_0.conda#264b3c697fa9cdade87eb0abe4440d54
https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.14.0-pyhd8ed1ab_0.conda#b3788794f88c9512393032e448428261
@@ -270,4 +271,3 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.4-pyhd8
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.6-pyhd8ed1ab_0.conda#cf5c9649272c677a964a7313279e3a9b
https://conda.anaconda.org/conda-forge/noarch/sphinx-5.3.0-pyhd8ed1ab_0.tar.bz2#f9e1fcfe235d655900bfeb6aee426472
https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.9-pyhd8ed1ab_0.conda#0612e497d7860728f2cda421ea2aec09
-
diff --git a/requirements/py310.yml b/requirements/py310.yml
index fd549a9cf73..b01586aac98 100644
--- a/requirements/py310.yml
+++ b/requirements/py310.yml
@@ -18,7 +18,7 @@ dependencies:
- libnetcdf !=4.9.1
- matplotlib >=3.5
- netcdf4
- - numpy >=1.21, !=1.24.3
+ - numpy >1.21, !=1.24.3
- python-xxhash
- pyproj
- scipy
diff --git a/requirements/py311.yml b/requirements/py311.yml
index a883e5d87ab..286fe74a339 100644
--- a/requirements/py311.yml
+++ b/requirements/py311.yml
@@ -18,7 +18,7 @@ dependencies:
- libnetcdf !=4.9.1
- matplotlib >=3.5
- netcdf4
- - numpy >=1.21, !=1.24.3
+ - numpy >1.21, !=1.24.3
- python-xxhash
- pyproj
- scipy
diff --git a/requirements/py39.yml b/requirements/py39.yml
index 5b3c17510ee..f534aef4f38 100644
--- a/requirements/py39.yml
+++ b/requirements/py39.yml
@@ -18,7 +18,7 @@ dependencies:
- libnetcdf !=4.9.1
- matplotlib >=3.5
- netcdf4
- - numpy >=1.21, !=1.24.3
+ - numpy >1.21, !=1.24.3
- python-xxhash
- pyproj
- scipy
diff --git a/requirements/pypi-core.txt b/requirements/pypi-core.txt
index 7937f73b4f5..e286bb97bca 100644
--- a/requirements/pypi-core.txt
+++ b/requirements/pypi-core.txt
@@ -5,8 +5,8 @@ dask[array]>=2022.9.0
# libnetcdf!=4.9.1 (not available on PyPI)
matplotlib>=3.5
netcdf4
-numpy>=1.21,!=1.24.3
+numpy>1.21,!=1.24.3
pyproj
scipy
shapely!=1.8.3
-xxhash
\ No newline at end of file
+xxhash
diff --git a/tools/release_do_nothing.py b/tools/release_do_nothing.py
index 5d7dd2abf26..afe12a662dc 100755
--- a/tools/release_do_nothing.py
+++ b/tools/release_do_nothing.py
@@ -279,6 +279,11 @@ def finalise_whats_new(
whatsnew_title += " [release candidate]"
# TODO: automate
message = f"In {rsts.release.name}: set the page title to:\n{whatsnew_title}\n"
+ if not is_release_candidate:
+ message += (
+ "\nBe sure to remove any existing mentions of release "
+ "candidate from the title.\n"
+ )
_wait_for_done(message)
message = (