Skip to content

Commit 6675cd8

Browse files
committed
filter_cf improvements.
1 parent 20fbeed commit 6675cd8

File tree

4 files changed

+80
-69
lines changed

4 files changed

+80
-69
lines changed

lib/iris/common/__init__.py

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from .metadata import *
1515
from .mixin import *
1616
from .resolve import *
17+
from ..util import guess_coord_axis
1718

1819

1920
def filter_cf(
@@ -23,10 +24,17 @@ def filter_cf(
2324
long_name=None,
2425
var_name=None,
2526
attributes=None,
27+
axis=None,
2628
):
2729
"""
28-
Filter a list of :class:`iris.common.CFVariableMixin` subclasses to fit
29-
the given criteria.
30+
Filter a collection of objects by their metadata to fit the given metadata
31+
criteria. Criteria be one or both of: specific properties / other objects
32+
carrying metadata to be matched.
33+
34+
Args:
35+
36+
* instances
37+
An iterable of objects to be filtered.
3038
3139
Kwargs:
3240
@@ -36,15 +44,15 @@ def filter_cf(
3644
(a) a :attr:`standard_name`, :attr:`long_name`, or
3745
:attr:`var_name`. Defaults to value of `default`
3846
(which itself defaults to `unknown`) as defined in
39-
:class:`iris.common.CFVariableMixin`.
47+
:class:`~iris.common.CFVariableMixin`.
4048
4149
(b) a 'coordinate' instance with metadata equal to that of
4250
the desired coordinates. Accepts either a
43-
:class:`iris.coords.DimCoord`, :class:`iris.coords.AuxCoord`,
44-
:class:`iris.aux_factory.AuxCoordFactory`,
45-
:class:`iris.common.CoordMetadata` or
46-
:class:`iris.common.DimCoordMetadata` or
47-
:class:`iris.experimental.ugrid.ConnectivityMetadata`.
51+
:class:`~iris.coords.DimCoord`, :class:`~iris.coords.AuxCoord`,
52+
:class:`~iris.aux_factory.AuxCoordFactory`,
53+
:class:`~iris.common.CoordMetadata` or
54+
:class:`~iris.common.DimCoordMetadata` or
55+
:class:`~iris.experimental.ugrid.ConnectivityMetadata`.
4856
* standard_name
4957
The CF standard name of the desired coordinate. If None, does not
5058
check for standard name.
@@ -57,40 +65,44 @@ def filter_cf(
5765
* attributes
5866
A dictionary of attributes desired on the coordinates. If None,
5967
does not check for attributes.
68+
* axis
69+
The desired coordinate axis, see
70+
:func:`~iris.util.guess_coord_axis`. If None, does not check for
71+
axis. Accepts the values 'X', 'Y', 'Z' and 'T' (case-insensitive).
72+
73+
Returns:
74+
A list of the objects supplied in the ``instances`` argument, limited
75+
to only those that matched the given criteria.
6076
6177
"""
6278
name = None
63-
instance = None
79+
obj = None
6480

6581
if isinstance(item, str):
6682
name = item
6783
else:
68-
instance = item
84+
obj = item
6985

7086
result = instances
7187

7288
if name is not None:
73-
result = [
74-
instance_ for instance_ in result if instance_.name() == name
75-
]
89+
result = [instance for instance in result if instance.name() == name]
7690

7791
if standard_name is not None:
7892
result = [
79-
instance_
80-
for instance_ in result
81-
if instance_.standard_name == standard_name
93+
instance
94+
for instance in result
95+
if instance.standard_name == standard_name
8296
]
8397

8498
if long_name is not None:
8599
result = [
86-
instance_
87-
for instance_ in result
88-
if instance_.long_name == long_name
100+
instance for instance in result if instance.long_name == long_name
89101
]
90102

91103
if var_name is not None:
92104
result = [
93-
instance_ for instance_ in result if instance_.var_name == var_name
105+
instance for instance in result if instance.var_name == var_name
94106
]
95107

96108
if attributes is not None:
@@ -101,28 +113,36 @@ def filter_cf(
101113
)
102114
raise ValueError(msg)
103115

104-
def attr_filter(instance_):
116+
def attr_filter(instance):
105117
return all(
106-
k in instance_.attributes
107-
and metadata._hexdigest(instance_.attributes[k])
118+
k in instance.attributes
119+
and metadata._hexdigest(instance.attributes[k])
108120
== metadata._hexdigest(v)
109121
for k, v in attributes.items()
110122
)
111123

112-
result = [instance_ for instance_ in result if attr_filter(instance_)]
124+
result = [instance for instance in result if attr_filter(instance)]
125+
126+
if axis is not None:
127+
axis = axis.upper()
128+
result = [
129+
instance
130+
for instance in result
131+
if guess_coord_axis(instance) == axis
132+
]
113133

114-
if instance is not None:
115-
if hasattr(instance, "__class__") and issubclass(
116-
instance.__class__, BaseMetadata
134+
if obj is not None:
135+
if hasattr(obj, "__class__") and issubclass(
136+
obj.__class__, BaseMetadata
117137
):
118-
target_metadata = instance
138+
target_metadata = obj
119139
else:
120-
target_metadata = instance.metadata
140+
target_metadata = obj.metadata
121141

122142
result = [
123-
instance_
124-
for instance_ in result
125-
if instance_.metadata == target_metadata
143+
instance
144+
for instance in result
145+
if instance.metadata == target_metadata
126146
]
127147

128148
return result

lib/iris/cube.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,17 +1653,9 @@ def coords(
16531653
long_name=long_name,
16541654
var_name=var_name,
16551655
attributes=attributes,
1656+
axis=axis,
16561657
)
16571658

1658-
if axis is not None:
1659-
axis = axis.upper()
1660-
guess_axis = iris.util.guess_coord_axis
1661-
coords_and_factories = [
1662-
coord_
1663-
for coord_ in coords_and_factories
1664-
if guess_axis(coord_) == axis
1665-
]
1666-
16671659
if coord_system is not None:
16681660
coords_and_factories = [
16691661
coord_

lib/iris/experimental/ugrid.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,21 +1540,14 @@ def populated_coords(coords_tuple):
15401540
dmsg = "Ignoring request to filter non-existent 'face_coords'"
15411541
logger.debug(dmsg, extra=dict(cls=self.__class__.__name__))
15421542

1543-
if axis is not None:
1544-
axis = axis.upper()
1545-
members = [
1546-
instance_
1547-
for instance_ in members
1548-
if guess_coord_axis(instance_) == axis
1549-
]
1550-
15511543
result = filter_cf(
15521544
members,
15531545
item=item,
15541546
standard_name=standard_name,
15551547
long_name=long_name,
15561548
var_name=var_name,
15571549
attributes=attributes,
1550+
axis=axis,
15581551
)
15591552

15601553
# Use the results to filter the _members dict for returning.
@@ -1829,38 +1822,36 @@ def filters(
18291822

18301823
if cf_role is not None:
18311824
members = [
1832-
instance_
1833-
for instance_ in members
1834-
if instance_.cf_role == cf_role
1825+
instance for instance in members if instance.cf_role == cf_role
18351826
]
18361827

1837-
def location_filter(instances_, parameter_, location_name_):
1838-
if parameter_ is False:
1839-
members_ = [
1840-
instance_
1841-
for instance_ in instances_
1842-
if location_name_
1843-
not in (instance_.src_location, instance_.tgt_location)
1828+
def location_filter(instances, loc_arg, loc_name):
1829+
if loc_arg is False:
1830+
filtered = [
1831+
instance
1832+
for instance in instances
1833+
if loc_name
1834+
not in (instance.src_location, instance.tgt_location)
18441835
]
1845-
elif parameter_ is None:
1846-
members_ = instances_
1836+
elif loc_arg is None:
1837+
filtered = instances
18471838
else:
18481839
# Interpret any other value as =True.
1849-
members_ = [
1850-
instance_
1851-
for instance_ in instances_
1852-
if location_name_
1853-
in (instance_.src_location, instance_.tgt_location)
1840+
filtered = [
1841+
instance
1842+
for instance in instances
1843+
if loc_name
1844+
in (instance.src_location, instance.tgt_location)
18541845
]
18551846

1856-
return members_
1847+
return filtered
18571848

1858-
for parameter, location_name in (
1849+
for arg, loc in (
18591850
(node, "node"),
18601851
(edge, "edge"),
18611852
(face, "face"),
18621853
):
1863-
members = location_filter(members, parameter, location_name)
1854+
members = location_filter(members, arg, loc)
18641855

18651856
# No need to actually modify filtering behaviour - already won't return
18661857
# any face cf-roles if none are present.

lib/iris/tests/unit/common/test_filter_cf.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ def test_invalid_attributes(self):
9696
attributes="one",
9797
)
9898

99+
def test_axis(self):
100+
axis_lon = Mock(standard_name="longitude")
101+
axis_lat = Mock(standard_name="latitude")
102+
input_list = [axis_lon, axis_lat]
103+
result = filter_cf(input_list, axis="x")
104+
self.assertIn(axis_lon, result)
105+
self.assertNotIn(axis_lat, result)
106+
99107
def test_multiple_args(self):
100108
coord_one = Mock(__class__=AuxCoord, long_name="one")
101109
coord_two = Mock(__class__=AuxCoord, long_name="two")

0 commit comments

Comments
 (0)