Skip to content

Commit

Permalink
Allowing exemption to axis guessing on coords (#5551)
Browse files Browse the repository at this point in the history
* allowing excemption to axis guessing on coords

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updating pr

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove from metadata

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove merge clash

* adding review comments

* more review changes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* parametrise and add tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix last test

* addressing review comments

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix test failure

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add whatsnew and conftest files

* fix sentence

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix flake8

* fix last test

* update whatsnew

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
HGWright and pre-commit-ci[bot] committed Nov 17, 2023
1 parent 80c1792 commit 7770518
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 5 deletions.
6 changes: 5 additions & 1 deletion docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ This document explains the changes made to Iris for this release

✨ Features
===========

#. `@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`)
Expand All @@ -44,6 +44,10 @@ This document explains the changes made to Iris for this release
Winter - December to February) will be assigned to the preceding year (e.g.
the year of December) instead of the following year (the default behaviour).
(:pull:`5573`)

#. `@HGWright`_ added :attr:`~iris.coords.Coord.ignore_axis` to allow manual
intervention preventing :func:`~iris.util.guess_coord_axis` from acting on a
coordinate. (:pull:`5551`)


🐛 Bugs Fixed
Expand Down
38 changes: 35 additions & 3 deletions lib/iris/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import iris.time
import iris.util

#: The default value for ignore_axis which controls guess_coord_axis' behaviour
DEFAULT_IGNORE_AXIS = False


class _DimensionalMetadata(CFVariableMixin, metaclass=ABCMeta):
"""
Expand Down Expand Up @@ -860,7 +863,6 @@ def xml_element(self, doc):
element.setAttribute(
"climatological", str(self.climatological)
)

if self.attributes:
attributes_element = doc.createElement("attributes")
for name in sorted(self.attributes.keys()):
Expand Down Expand Up @@ -1593,6 +1595,8 @@ def __init__(
self.bounds = bounds
self.climatological = climatological

self._ignore_axis = DEFAULT_IGNORE_AXIS

def copy(self, points=None, bounds=None):
"""
Returns a copy of this coordinate.
Expand Down Expand Up @@ -1625,6 +1629,10 @@ def copy(self, points=None, bounds=None):
# self.
new_coord.bounds = bounds

# The state of ignore_axis is controlled by the coordinate rather than
# the metadata manager
new_coord.ignore_axis = self.ignore_axis

return new_coord

@classmethod
Expand All @@ -1644,7 +1652,14 @@ def from_coord(cls, coord):
if issubclass(cls, DimCoord):
# DimCoord introduces an extra constructor keyword.
kwargs["circular"] = getattr(coord, "circular", False)
return cls(**kwargs)

new_coord = cls(**kwargs)

# The state of ignore_axis is controlled by the coordinate rather than
# the metadata manager
new_coord.ignore_axis = coord.ignore_axis

return new_coord

@property
def points(self):
Expand Down Expand Up @@ -1736,6 +1751,24 @@ def climatological(self, value):

self._metadata_manager.climatological = value

@property
def ignore_axis(self):
"""
A boolean that controls whether guess_coord_axis acts on this
coordinate.
Defaults to False, and when set to True it will be skipped by
guess_coord_axis.
"""
return self._ignore_axis

@ignore_axis.setter
def ignore_axis(self, value):
if not isinstance(value, bool):
emsg = "'ignore_axis' can only be set to 'True' or 'False'"
raise ValueError(emsg)
self._ignore_axis = value

def lazy_points(self):
"""
Return a lazy array representing the coord points.
Expand Down Expand Up @@ -2694,7 +2727,6 @@ def __init__(
Will set to True when a climatological time axis is loaded
from NetCDF.
Always False if no bounds exist.
"""
# Configure the metadata manager.
self._metadata_manager = metadata_manager_factory(DimCoordMetadata)
Expand Down
14 changes: 14 additions & 0 deletions lib/iris/tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Unit tests fixture infra-structure."""
import pytest

import iris


@pytest.fixture
def sample_coord():
sample_coord = iris.coords.DimCoord(points=(1, 2, 3, 4, 5))
return sample_coord
34 changes: 34 additions & 0 deletions lib/iris/tests/unit/coords/test_Coord.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import dask.array as da
import numpy as np
import pytest

import iris
from iris.coords import AuxCoord, Coord, DimCoord
Expand Down Expand Up @@ -1149,6 +1150,39 @@ def test_change_units(self):
self.assertFalse(coord.climatological)


class TestIgnoreAxis:
def test_default(self, sample_coord):
assert sample_coord.ignore_axis is False

def test_set_true(self, sample_coord):
sample_coord.ignore_axis = True
assert sample_coord.ignore_axis is True

def test_set_random_value(self, sample_coord):
with pytest.raises(
ValueError,
match=r"'ignore_axis' can only be set to 'True' or 'False'",
):
sample_coord.ignore_axis = "foo"

@pytest.mark.parametrize(
"ignore_axis, copy_or_from, result",
[
(True, "copy", True),
(True, "from_coord", True),
(False, "copy", False),
(False, "from_coord", False),
],
)
def test_copy_coord(self, ignore_axis, copy_or_from, result, sample_coord):
sample_coord.ignore_axis = ignore_axis
if copy_or_from == "copy":
new_coord = sample_coord.copy()
elif copy_or_from == "from_coord":
new_coord = sample_coord.from_coord(sample_coord)
assert new_coord.ignore_axis is result


class Test___init____abstractmethod(tests.IrisTest):
def test(self):
emsg = (
Expand Down
50 changes: 50 additions & 0 deletions lib/iris/tests/unit/util/test_guess_coord_axis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Test function :func:`iris.util.guess_coord_axis`."""

import pytest

from iris.util import guess_coord_axis


class TestGuessCoord:
@pytest.mark.parametrize(
"coordinate, axis",
[
("longitude", "X"),
("grid_longitude", "X"),
("projection_x_coordinate", "X"),
("latitude", "Y"),
("grid_latitude", "Y"),
("projection_y_coordinate", "Y"),
],
)
def test_coord(self, coordinate, axis, sample_coord):
sample_coord.standard_name = coordinate
assert guess_coord_axis(sample_coord) == axis

@pytest.mark.parametrize(
"units, axis",
[
("hPa", "Z"),
("days since 1970-01-01 00:00:00", "T"),
],
)
def test_units(self, units, axis, sample_coord):
sample_coord.units = units
assert guess_coord_axis(sample_coord) == axis

@pytest.mark.parametrize(
"ignore_axis, result",
[
(True, None),
(False, "X"),
],
)
def test_ignore_axis(self, ignore_axis, result, sample_coord):
sample_coord.standard_name = "longitude"
sample_coord.ignore_axis = ignore_axis

assert guess_coord_axis(sample_coord) == result
9 changes: 8 additions & 1 deletion lib/iris/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,17 @@ def guess_coord_axis(coord):
This function maintains laziness when called; it does not realise data.
See more at :doc:`/userguide/real_and_lazy_data`.
The ``guess_coord_axis`` behaviour can be skipped by setting the coordinate property ``ignore_axis``
to ``False``.
"""

axis = None

if coord.standard_name in (
if hasattr(coord, "ignore_axis") and coord.ignore_axis is True:
return axis

elif coord.standard_name in (
"longitude",
"grid_longitude",
"projection_x_coordinate",
Expand Down

0 comments on commit 7770518

Please sign in to comment.