diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e63924c78f..ded673f008 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: no-commit-to-branch - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.9" + rev: "v0.1.11" hooks: - id: ruff types: [file, python] @@ -45,7 +45,7 @@ repos: additional_dependencies: [tomli] - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 types: [file, python] diff --git a/benchmarks/asv_delegated_conda.py b/benchmarks/asv_delegated_conda.py index 1f91533524..c8070b063a 100644 --- a/benchmarks/asv_delegated_conda.py +++ b/benchmarks/asv_delegated_conda.py @@ -4,8 +4,7 @@ # See LICENSE in the root of the repository for full licensing details. """ASV plug-in providing an alternative :class:`asv.plugins.conda.Conda` subclass. -ASV plug-in providing an alternative :class:`asv.plugins.conda.Conda` -subclass that manages the Conda environment via custom user scripts. +Manages the Conda environment via custom user scripts. """ diff --git a/benchmarks/benchmarks/cperf/load.py b/benchmarks/benchmarks/cperf/load.py index 0b83725411..cafc4631c0 100644 --- a/benchmarks/benchmarks/cperf/load.py +++ b/benchmarks/benchmarks/cperf/load.py @@ -10,7 +10,7 @@ @on_demand_benchmark class SingleDiagnosticLoad(SingleDiagnosticMixin): def time_load(self, _, __, ___): - """Time load, the 'real world comparison'. + """Perform a 'real world comparison'. * UM coords are always realised (DimCoords). * LFRic coords are not realised by default (MeshCoords). diff --git a/benchmarks/benchmarks/sperf/equality.py b/benchmarks/benchmarks/sperf/equality.py index a878b5567e..339687a22c 100644 --- a/benchmarks/benchmarks/sperf/equality.py +++ b/benchmarks/benchmarks/sperf/equality.py @@ -12,7 +12,7 @@ class CubeEquality(FileMixin): r"""Benchmark time and memory costs. Benchmark time and memory costs of comparing :class:`~iris.cube.Cube`\\ s - with attached :class:`~iris.experimental.ugrid.mesh.Mesh`\\ es. + with attached :class:`~iris.experimental.ugrid.mesh.Mesh`\\ es. Uses :class:`FileMixin` as the realistic case will be comparing :class:`~iris.cube.Cube`\\ s that have been loaded from file. diff --git a/docs/src/conf.py b/docs/src/conf.py index 97be2776ef..0fa493bde1 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -291,7 +291,6 @@ def _dotv(version): html_theme_options = { "footer_start": ["copyright", "sphinx-version"], "footer_end": ["custom_footer"], - "collapse_navigation": True, "navigation_depth": 3, "show_prev_next": True, "navbar_align": "content", diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index d7e9a40648..dfc1019683 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -24,7 +24,7 @@ This document explains the changes made to Iris for this release 📢 Announcements ================ -#. `@lbdreyer`_ relicensed Iris from LGPL-3 to BSD-3. (:pull: `5577`) +#. `@lbdreyer`_ relicensed Iris from LGPL-3 to BSD-3. (:pull:`5577`) #. `@HGWright`_, `@bjlittle`_ and `@trexfeathers`_ (reviewers) added a CITATION.cff file to Iris and updated the :ref:`citation documentation ` @@ -83,6 +83,10 @@ This document explains the changes made to Iris for this release #. `@rcomer`_ and `@trexfeathers`_ (reviewer) added handling for realization coordinates when saving pp files (:issue:`4747`, :pull:`5568`) +#. `@ESadek-MO`_ has updated + :mod:`iris.fileformats._nc_load_rules.helpers` to lessen warning duplication. + (:issue:`5536`, :pull:`5685`) + 💣 Incompatible Changes ======================= @@ -142,6 +146,17 @@ This document explains the changes made to Iris for this release #. `@bouweandela`_ updated all hyperlinks to https. (:pull:`5621`) +#. `@ESadek-MO`_ created an index page for :ref:`further_topics_index`, and + relocated all 'Technical Papers' into + :ref:`further_topics_index`. (:pull:`5602`) + +#. `@trexfeathers`_ made drop-down icons visible to show which pages link to + 'sub-pages'. (:pull:`5684`) + +#. `@trexfeathers`_ improved the documentation of acceptable + :class:`~iris.cube.Cube` standard names in + :func:`iris.analysis.calculus.curl`. (:pull:`5680`) + 💼 Internal =========== diff --git a/lib/iris/_concatenate.py b/lib/iris/_concatenate.py index fce7d0bde9..b7811bfaac 100644 --- a/lib/iris/_concatenate.py +++ b/lib/iris/_concatenate.py @@ -387,9 +387,6 @@ class _CubeSignature: def __init__(self, cube): """Represent the cube metadata and associated coordinate metadata. - Represents the cube metadata and associated coordinate metadata that - allows suitable cubes for concatenation to be identified. - Parameters ---------- cube : :class:`iris.cube.Cube` @@ -733,10 +730,7 @@ class _ProtoCube: """Framework for concatenating multiple source-cubes over one common dimension.""" def __init__(self, cube): - """Create a new _ProtoCube and record the cube as a source-cube. - - Create a new _ProtoCube from the given cube and record the cube - as a source-cube. + """Create a new _ProtoCube from the given cube and record the cube as a source-cube. Parameters ---------- diff --git a/lib/iris/_data_manager.py b/lib/iris/_data_manager.py index d05128d0bb..6db1d16889 100644 --- a/lib/iris/_data_manager.py +++ b/lib/iris/_data_manager.py @@ -285,7 +285,7 @@ def copy(self, data=None): return self._deepcopy(memo, data=data) def core_data(self): - """If real data is being managed, return the numpy.ndarray or numpy.ma.core.MaskedArray. + """Provide real data or lazy data. If real data is being managed, then return the :class:`~numpy.ndarray` or :class:`numpy.ma.core.MaskedArray`. Otherwise, return the lazy diff --git a/lib/iris/_lazy_data.py b/lib/iris/_lazy_data.py index a37466a930..b430a4d682 100644 --- a/lib/iris/_lazy_data.py +++ b/lib/iris/_lazy_data.py @@ -42,7 +42,7 @@ def is_lazy_data(data): def is_lazy_masked_data(data): - """Is it 'lazy' data array and the underlying array is of masked type. + """Determine whether managed data is lazy and masked. Return True if the argument is both an Iris 'lazy' data array and the underlying array is of masked type. Otherwise return False. diff --git a/lib/iris/_merge.py b/lib/iris/_merge.py index 73a35ad698..7705dd6bea 100644 --- a/lib/iris/_merge.py +++ b/lib/iris/_merge.py @@ -859,6 +859,10 @@ def _build_separable_group( ): """Update the space with the first separable consistent group. + Update the space with the first separable consistent group that + satisfies a valid functional relationship with all other candidate + dimensions in the group. + For example, the group ABCD and separable consistent group CD, if A = f(C, D) and B = f(C, D) then update the space with "A: (C, D), B: (C, D), C: None, D: None". Where "A: (C, D)" means @@ -1518,7 +1522,7 @@ def name_in_independents(): self._shape.extend(signature.data_shape) def _get_cube(self, data): - """Return fully constructed, containing all its coordinates and metadata. + """Generate fully constructed cube. Return a fully constructed cube for the given data, containing all its coordinates and metadata. diff --git a/lib/iris/_representation/cube_printout.py b/lib/iris/_representation/cube_printout.py index 6cab096d0a..47c0b8b631 100644 --- a/lib/iris/_representation/cube_printout.py +++ b/lib/iris/_representation/cube_printout.py @@ -9,7 +9,7 @@ class Table: - """A container of text strings in rows + columns. + """A container of text strings in rows and columns. A container of text strings in rows + columns, that can format its content into a string per row, with contents in columns of fixed width. @@ -118,7 +118,7 @@ def __str__(self): class CubePrinter: - """An object created from a :class:`iris._representation.CubeSummary`. + """An object created from a cube summary. An object created from a :class:`iris._representation.CubeSummary`, which provides diff --git a/lib/iris/analysis/_interpolation.py b/lib/iris/analysis/_interpolation.py index 9051f380ce..59863e3485 100644 --- a/lib/iris/analysis/_interpolation.py +++ b/lib/iris/analysis/_interpolation.py @@ -57,7 +57,7 @@ def convert_date(date): def extend_circular_coord(coord, points): - """Return coordinates points with a shape extended by one. + """Return coordinate points with a shape extended by one. This is common when dealing with circular coordinates. @@ -68,7 +68,7 @@ def extend_circular_coord(coord, points): def extend_circular_coord_and_data(coord, data, coord_dim): - """Return coordinate points and data array with a shape extended by one in the coord_dim axis. + """Return coordinate points and data with a shape extended by one in the provided axis. Return coordinate points and a data array with a shape extended by one in the coord_dim axis. This is common when dealing with circular @@ -467,7 +467,7 @@ def _interpolated_dtype(self, dtype): return result def _points(self, sample_points, data, data_dims=None): - """Interpolate values at the specified list of orthogonal (coord, points) pairs. + """Interpolate at the specified points. Interpolate the given data values at the specified list of orthogonal (coord, points) pairs. diff --git a/lib/iris/analysis/_regrid.py b/lib/iris/analysis/_regrid.py index f6bc677cc0..321194d1fc 100644 --- a/lib/iris/analysis/_regrid.py +++ b/lib/iris/analysis/_regrid.py @@ -431,7 +431,7 @@ def _get_horizontal_coord(cube, axis): return coords[0] def __call__(self, src): - """Regrid :class:`~iris.cube.Cube` onto the target grid :class:`_CurvilinearRegridder`. + """Regrid onto the target grid. Regrid the supplied :class:`~iris.cube.Cube` on to the target grid of this :class:`_CurvilinearRegridder`. @@ -555,7 +555,7 @@ def extrapolation_mode(self): @staticmethod def _sample_grid(src_coord_system, grid_x_coord, grid_y_coord): - """Convert the rectilinear grid coordinates to a curvilinear grid. + """Convert the rectilinear grid to a curvilinear grid. Convert the rectilinear grid coordinates to a curvilinear grid in the source coordinate system. @@ -852,7 +852,7 @@ def _check_units(self, coord): raise ValueError(msg) def __call__(self, src): - """Regrid :class:`~iris.cube.Cube` onto target grid of :class:`RectilinearRegridder`. + """Regrid onto target grid. Regrid this :class:`~iris.cube.Cube` on to the target grid of this :class:`RectilinearRegridder`. diff --git a/lib/iris/analysis/calculus.py b/lib/iris/analysis/calculus.py index 4dd3171fae..aaee22d9c8 100644 --- a/lib/iris/analysis/calculus.py +++ b/lib/iris/analysis/calculus.py @@ -25,7 +25,7 @@ from iris.exceptions import IrisUserWarning from iris.util import delta -__all__ = ["cube_delta", "curl", "differentiate"] +__all__ = ["DIRECTIONAL_NAMES", "cube_delta", "curl", "differentiate"] def _construct_delta_coord(coord): @@ -471,6 +471,9 @@ def curl(i_cube, j_cube, k_cube=None): Calculate the 2-dimensional or 3-dimensional spherical or cartesian curl of the given vector of cubes. + The cube standard names must match one of the combinations in + :data:`DIRECTIONAL_NAMES`. + As well as the standard x and y coordinates, this function requires each cube to possess a vertical or z-like coordinate (representing some form of height or pressure). This can be a scalar or dimension coordinate. @@ -734,12 +737,27 @@ def curl(i_cube, j_cube, k_cube=None): return result +#: Acceptable X-Y-Z standard name combinations that +#: :func:`curl` can use (via :func:`spatial_vectors_with_phenom_name`). +DIRECTIONAL_NAMES: tuple[tuple[str, str, str], ...] = ( + ("u", "v", "w"), + ("x", "y", "z"), + ("i", "j", "k"), + ("eastward", "northward", "upward"), + ("easterly", "northerly", "vertical"), + ("easterly", "northerly", "radial"), +) + + def spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube=None): """Given spatially dependent cubes, return a list of the spatial coordinate names. Given 2 or 3 spatially dependent cubes, return a list of the spatial coordinate names with appropriate phenomenon name. + The cube standard names must match one of the combinations in + :data:`DIRECTIONAL_NAMES`. + This routine is designed to identify the vector quantites which each of the cubes provided represent and return a list of their 3d spatial dimension names and associated phenomenon. @@ -757,15 +775,6 @@ def spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube=None): """ - directional_names = ( - ("u", "v", "w"), - ("x", "y", "z"), - ("i", "j", "k"), - ("eastward", "northward", "upward"), - ("easterly", "northerly", "vertical"), - ("easterly", "northerly", "radial"), - ) - # Create a list of the standard_names of our incoming cubes # (excluding the k_cube if it is None). cube_standard_names = [ @@ -795,7 +804,7 @@ def spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube=None): # Get the appropriate direction list from the cube_directions we # have got from the standard name. direction = None - for possible_direction in directional_names: + for possible_direction in DIRECTIONAL_NAMES: # If this possible direction (minus the k_cube if it is none) # matches direction from the given cubes use it. if possible_direction[0 : len(cube_directions)] == cube_directions: @@ -804,7 +813,7 @@ def spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube=None): # If we didn't get a match, raise an Exception if direction is None: direction_string = "; ".join( - ", ".join(possible_direction) for possible_direction in directional_names + ", ".join(possible_direction) for possible_direction in DIRECTIONAL_NAMES ) raise ValueError( "{} are not recognised vector cube_directions. " diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py index 5205dc7b40..58c10c1926 100644 --- a/lib/iris/analysis/cartography.py +++ b/lib/iris/analysis/cartography.py @@ -83,7 +83,7 @@ def wrap_lons(lons, base, period): def unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat): - """Convert arrays of rotated-pole to unrotated arrays of longitudes and latitudes. + """Convert rotated-pole to unrotated longitudes and latitudes. ``pole_lat`` should describe the location of the rotated pole that describes the arrays of rotated-pole longitudes and latitudes. @@ -133,7 +133,7 @@ def unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat): def rotate_pole(lons, lats, pole_lon, pole_lat): - """Convert arrays of longitudes and latitudes to arrays of rotated-pole longitudes and latitudes. + """Convert unrotated longitudes and latitudes to rotated-pole. The values of ``pole_lon`` and ``pole_lat`` should describe the rotated pole that the arrays of longitudes and @@ -496,7 +496,7 @@ def area_weights(cube, normalize=False): def cosine_latitude_weights(cube): - r"""Return an array of latitude weights, with the same dimensions as the cube. + r"""Calculate cosine latitude weights, with the same dimensions as the cube. Return an array of latitude weights, with the same dimensions as the cube. The weights are the cosine of latitude. @@ -996,7 +996,7 @@ def _crs_distance_differentials(crs, x, y): def _transform_distance_vectors(u_dist, v_dist, ds, dx2, dy2): - """Transform distance vectors from one coordinate reference system to another. + """Transform distance vectors to another coordinate reference system. Transform distance vectors from one coordinate reference system to another, preserving magnitude and physical direction. diff --git a/lib/iris/analysis/geometry.py b/lib/iris/analysis/geometry.py index 3cac7b0a9b..a76498bf45 100644 --- a/lib/iris/analysis/geometry.py +++ b/lib/iris/analysis/geometry.py @@ -18,7 +18,7 @@ def _extract_relevant_cube_slice(cube, geometry): - """Given a shapely geometry object. + """Calculate geometry intersection with spatial region defined by cube. This helper method returns the tuple (subcube, x_coord_of_subcube, y_coord_of_subcube, diff --git a/lib/iris/analysis/maths.py b/lib/iris/analysis/maths.py index bcf064ad94..216d7e3892 100644 --- a/lib/iris/analysis/maths.py +++ b/lib/iris/analysis/maths.py @@ -971,7 +971,7 @@ def _broadcast_cube_coord_data(cube, other, operation_name, dim=None): def _sanitise_metadata(cube, unit): - """Clear the necessary or unsupported metadata from the resultant cube. + """Clear appropriate metadata from the resultant cube. As part of the maths metadata contract, clear the necessary or unsupported metadata from the resultant cube of the maths operation. diff --git a/lib/iris/analysis/trajectory.py b/lib/iris/analysis/trajectory.py index 4d767de1e1..9adb409970 100644 --- a/lib/iris/analysis/trajectory.py +++ b/lib/iris/analysis/trajectory.py @@ -530,7 +530,7 @@ def _cartesian_sample_points(sample_points, sample_point_coord_names): def _nearest_neighbour_indices_ndcoords(cube, sample_points, cache=None): - """Return indices to select data value(s) closest to the given coordinate point values. + """Calculate the cube nearest neighbour indices for the samples. Return the indices to select the data value(s) closest to the given coordinate point values. diff --git a/lib/iris/common/lenient.py b/lib/iris/common/lenient.py index e3b6fe4035..4822d88b92 100644 --- a/lib/iris/common/lenient.py +++ b/lib/iris/common/lenient.py @@ -89,7 +89,7 @@ def func(): @wraps(func) def lenient_client_inner_naked(*args, **kwargs): - """Closure wrapper function to register the wrapped function/method as active. + """Closure wrapper function. Closure wrapper function to register the wrapped function/method as active at runtime before executing it. @@ -111,7 +111,7 @@ def lenient_client_inner_naked(*args, **kwargs): def lenient_client_outer(func): @wraps(func) def lenient_client_inner(*args, **kwargs): - """Closure wrapper function to register the wrapped function/method. + """Closure wrapper function. Closure wrapper function to register the wrapped function/method as active at runtime before executing it. @@ -129,7 +129,7 @@ def lenient_client_inner(*args, **kwargs): def _lenient_service(*dargs): - """Allow a function/method to declare that it supports lenient behaviour as a service. + """Implement the lenient service protocol. Decorator that allows a function/method to declare that it supports lenient behaviour as a service. @@ -289,7 +289,7 @@ def _init(self): @contextmanager def context(self, **kwargs): - """Return a context manager which allows temporary modification of the lenient option. + """Context manager supporting temporary modification of lenient state. Return a context manager which allows temporary modification of the lenient option state within the scope of the context manager. @@ -447,7 +447,7 @@ def __setitem__(self, name, value): @contextmanager def context(self, *args, **kwargs): - """Context manager which allows temporary modification of the lenient option state. + """Context manager supporting temporary modification of lenient state. Return a context manager which allows temporary modification of the lenient option state for the active thread. @@ -542,7 +542,7 @@ def enable(self, state): self.__dict__["enable"] = state def register_client(self, func, services, append=False): - """Add the provided mapping of lenient client function/method. + """Add the lenient client to service mapping. Add the provided mapping of lenient client function/method to required lenient service function/methods. diff --git a/lib/iris/common/metadata.py b/lib/iris/common/metadata.py index 704a4df4ad..92c3d34f3e 100644 --- a/lib/iris/common/metadata.py +++ b/lib/iris/common/metadata.py @@ -94,7 +94,7 @@ def hexdigest(item): class _NamedTupleMeta(ABCMeta): - """Meta-class to support the convenience of creating a namedtuple from names/members. + """Meta-class convenience for creating a namedtuple. Meta-class to support the convenience of creating a namedtuple from names/members of the metadata class hierarchy. @@ -599,7 +599,7 @@ def combine(self, other, lenient=None): @lenient_service def difference(self, other, lenient=None): - """Return a new metadata instance created by performing a difference. + """Perform lenient metadata difference operation. Return a new metadata instance created by performing a difference comparison between each of the associated metadata members. @@ -653,7 +653,7 @@ def equal(self, other, lenient=None): @classmethod def from_metadata(cls, other): - """Convert the provided metadata instance from a different type to this metadata type. + """Convert metadata instance to this metadata type. Convert the provided metadata instance from a different type to this metadata type, using only the relevant metadata members. @@ -726,7 +726,7 @@ def _check(item): @classmethod def token(cls, name): - """Determine whether the provided name is a valid NetCDF name. + """Verify validity of provided NetCDF name. Determine whether the provided name is a valid NetCDF name and thus safe to represent a single parsable token. @@ -1561,7 +1561,7 @@ def values(self): def metadata_manager_factory(cls, **kwargs): - """Class instance factory function responsible for manufacturing metadata instances. + """Manufacturing metadata instances. A class instance factory function responsible for manufacturing metadata instances dynamically at runtime. diff --git a/lib/iris/common/resolve.py b/lib/iris/common/resolve.py index cd30842ad1..1ded7e2603 100644 --- a/lib/iris/common/resolve.py +++ b/lib/iris/common/resolve.py @@ -2,7 +2,7 @@ # # 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. -"""Provide analysis, identification and combination of metadata common between two cube. +"""Resolve metadata common between two cubes. Provides the infrastructure to support the analysis, identification and combination of metadata common between two :class:`~iris.cube.Cube` @@ -99,7 +99,7 @@ def create_coord(self, metadata): class Resolve: - """Used by Iris solely during cube maths. + """Resolve the metadata of two cubes into one cube. At present, :class:`~iris.common.resolve.Resolve` is used by Iris solely during cube maths to combine a left-hand :class:`~iris.cube.Cube` @@ -212,7 +212,7 @@ class Resolve: """ # noqa: D214, D406, D407, D410, D411 def __init__(self, lhs=None, rhs=None): - """Resolve the provided ``lhs`` cube operand and ``rhs`` cube operand. + """Resolve the cube operands. Resolve the provided ``lhs`` :class:`~iris.cube.Cube` operand and ``rhs`` :class:`~iris.cube.Cube` operand to determine the metadata @@ -329,7 +329,7 @@ def __init__(self, lhs=None, rhs=None): self(lhs, rhs) def __call__(self, lhs, rhs): - """Resolve the ``lhs`` cube operand and ``rhs`` cube operand metadata. + """Resolve the cube operands. Resolve the ``lhs`` :class:`~iris.cube.Cube` operand and ``rhs`` :class:`~iris.cube.Cube` operand metadata. @@ -386,7 +386,7 @@ def __call__(self, lhs, rhs): return self def _as_compatible_cubes(self): - """Determine whether the ``src`` and ``tgt`` can be transposed and/or broadcast. + """Transpose and/or broadcast operands. Determine whether the ``src`` and ``tgt`` :class:`~iris.cube.Cube` can be transposed and/or broadcast successfully together. @@ -497,7 +497,7 @@ def _aux_coverage( common_aux_metadata, common_scalar_metadata, ): - """Determine the dimensions covered by each of the local and common auxiliary coordinates. + """Perform auxiliary coordinate coverage. Determine the dimensions covered by each of the local and common auxiliary coordinates of the provided :class:`~iris.cube.Cube`. @@ -566,7 +566,7 @@ def _aux_coverage( @staticmethod def _aux_mapping(src_coverage, tgt_coverage): - """Establish the mapping of dimensions from the ``src`` to ``tgt``. + """Perform auxiliary coordinate dimension mapping. Establish the mapping of dimensions from the ``src`` to ``tgt`` :class:`~iris.cube.Cube` using the auxiliary coordinate metadata @@ -634,7 +634,7 @@ def _aux_mapping(src_coverage, tgt_coverage): @staticmethod def _categorise_items(cube): - """Inspect the provided :class:`~iris.cube.Cube` and group its coordinates. + """Categorise the cube metadata. Inspect the provided :class:`~iris.cube.Cube` and group its coordinates and associated metadata into dimension, auxiliary and @@ -684,7 +684,7 @@ def _create_prepared_item( bounds=None, container=None, ): - """Create a :class:`~iris.common.resolve._PreparedItem`. + """Package metadata in preparation for resolution. Convenience method that creates a :class:`~iris.common.resolve._PreparedItem` containing the data and metadata required to construct and attach a coordinate @@ -808,7 +808,7 @@ def _show(items, heading): @staticmethod def _dim_coverage(cube, cube_items_dim, common_dim_metadata): - """Determine the dimensions covered by each of the local and common dimension coordinates. + """Perform dimension coordinate coverage. Determine the dimensions covered by each of the local and common dimension coordinates of the provided :class:`~iris.cube.Cube`. @@ -860,7 +860,7 @@ def _dim_coverage(cube, cube_items_dim, common_dim_metadata): @staticmethod def _dim_mapping(src_coverage, tgt_coverage): - """Establish the mapping of dimensions from the ``src`` to ``tgt``. + """Perform dimension coordinate dimension mapping. Establish the mapping of dimensions from the ``src`` to ``tgt`` :class:`~iris.cube.Cube` using the dimension coordinate metadata @@ -919,7 +919,7 @@ def _free_mapping( src_aux_coverage, tgt_aux_coverage, ): - """Attempt to update the :attr:`~iris.common.resolve.Resolve.mapping`. + """Associate free dimensions to covered dimensions. Attempt to update the :attr:`~iris.common.resolve.Resolve.mapping` with ``src`` to ``tgt`` :class:`~iris.cube.Cube` mappings from unmapped ``src`` @@ -1095,7 +1095,7 @@ def _pop(item, items): logger.debug(f"mapping free dimensions gives, mapping={self.mapping}") def _metadata_coverage(self): - """Determine the dimensions using the pre-categorised metadata. + """Determine free and covered dimensions. Using the pre-categorised metadata of the cubes, determine the dimensions covered by their associated dimension and auxiliary coordinates, and which @@ -1143,7 +1143,7 @@ def _metadata_coverage(self): ) def _metadata_mapping(self): - """Ensure that each ``src`` :class:`~iris.cube.Cube` dimension is mapped. + """Identify equivalent dimensions using metadata. Ensure that each ``src`` :class:`~iris.cube.Cube` dimension is mapped to an associated ``tgt`` :class:`~iris.cube.Cube` dimension using the common dim and aux coordinate metadata. @@ -1253,7 +1253,7 @@ def _metadata_mapping(self): self._as_compatible_cubes() def _metadata_prepare(self): - """Populate the `prepared_category` and `prepared_factories`. + """Consolidate metadata for resolved cube. Populate the :attr:`~iris.common.resolve.Resolve.prepared_category` and :attr:`~iris.common.resolve.Resolve.prepared_factories` with the necessary metadata to be constructed @@ -1315,7 +1315,7 @@ def _metadata_prepare(self): self._prepare_factory_payload(src_cube, src_category_local) def _metadata_resolve(self): - """Categorise the coordinate metadata of the cubes into three distinct groups. + """Categorise the coordinate metadata. Categorise the coordinate metadata of the cubes into three distinct groups; metadata from coordinates only available (local) on the LHS @@ -1425,7 +1425,7 @@ def _prepare_common_aux_payload( prepared_items, ignore_mismatch=None, ): - """Populate the ``prepared_items`` with a :class:`~iris.common.resolve._PreparedItem`. + """Consolidate common auxiliary coordinate metadata. Populate the ``prepared_items`` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata for each auxiliary coordinate to be constructed and attached to the @@ -1550,7 +1550,7 @@ def _prepare_common_aux_payload( def _prepare_common_dim_payload( self, src_coverage, tgt_coverage, ignore_mismatch=None ): - """Populate the ``items_dim`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items`. + """Consolidate common dimension coordinate metadata. Populate the ``items_dim`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata for @@ -1689,7 +1689,7 @@ def _get_prepared_item( return result def _prepare_factory_payload(self, cube, category_local, from_src=True): - """Populate the :attr:`~iris.common.resolve.Resolve.prepared_factories`. + """Consolidate common factory metadata. Populate the :attr:`~iris.common.resolve.Resolve.prepared_factories` with a :class:`~iris.common.resolve._PreparedFactory` containing the necessary @@ -1768,7 +1768,7 @@ def _prepare_factory_payload(self, cube, category_local, from_src=True): logger.debug(dmsg) def _prepare_local_payload_aux(self, src_aux_coverage, tgt_aux_coverage): - """Populate the ``items_aux`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items`. + """Consolidate local auxiliary coordinate metadata. Populate the ``items_aux`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata for each @@ -1845,7 +1845,7 @@ def _prepare_local_payload_aux(self, src_aux_coverage, tgt_aux_coverage): logger.debug(dmsg) def _prepare_local_payload_dim(self, src_dim_coverage, tgt_dim_coverage): - """Populate the ``items_dim`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items`. + """Consolidate local dimension coordinate metadata. Populate the ``items_dim`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata for each @@ -1924,7 +1924,7 @@ def _prepare_local_payload_dim(self, src_dim_coverage, tgt_dim_coverage): self.prepared_category.items_dim.append(prepared_item) def _prepare_local_payload_scalar(self, src_aux_coverage, tgt_aux_coverage): - """Populate the ``items_scalar`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items`. + """Consolidate local scalar coordinate metadata. Populate the ``items_scalar`` member of :attr:`~iris.common.resolve.Resolve.prepared_category_items` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata for each @@ -1980,7 +1980,7 @@ def _prepare_local_payload( tgt_dim_coverage, tgt_aux_coverage, ): - """Populate the :attr:`~iris.common.resolve.Resolve.prepared_category_items`. + """Consolidate the local metadata. Populate the :attr:`~iris.common.resolve.Resolve.prepared_category_items` with a :class:`~iris.common.resolve._PreparedItem` containing the necessary metadata from the ``src`` @@ -2015,7 +2015,7 @@ def _prepare_local_payload( def _prepare_points_and_bounds( self, src_coord, tgt_coord, src_dims, tgt_dims, ignore_mismatch=None ): - """Compare the points and bounds coordinates to ensure that they are equivalent. + """Consolidate points and bounds. Compare the points and bounds of the ``src`` and ``tgt`` coordinates to ensure that they are equivalent, taking into account broadcasting when appropriate. @@ -2289,7 +2289,7 @@ def _tgt_cube_prepare(self, data): cube.remove_ancillary_variable(av) def cube(self, data, in_place=False): - """Create the resultant cube using operands. + """Create the resultant resolved cube. Create the resultant :class:`~iris.cube.Cube` from the resolved ``lhs`` and ``rhs`` :class:`~iris.cube.Cube` operands, using the provided @@ -2456,7 +2456,7 @@ def cube(self, data, in_place=False): @property def mapped(self): - """Boolean state representing whether **all** ``src`` dimensions have been associated. + """Whether all ``src`` dimensions have been mapped. Boolean state representing whether **all** ``src`` :class:`~iris.cube.Cube` dimensions have been associated with relevant ``tgt`` diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index b9c1aefaf4..adc9895ed9 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -384,7 +384,7 @@ def semi_major_axis(self): @semi_major_axis.setter def semi_major_axis(self, value): - """semi_major_axis. + """Assign semi_major_axis. Setting this property to a different value invalidates the current datum (if any) because a datum encodes a specific semi-major axis. This also @@ -406,7 +406,7 @@ def semi_minor_axis(self): @semi_minor_axis.setter def semi_minor_axis(self, value): - """semi_minor_axis. + """Assign semi_minor_axis. Setting this property to a different value invalidates the current datum (if any) because a datum encodes a specific semi-minor axis. This also @@ -428,7 +428,7 @@ def inverse_flattening(self): @inverse_flattening.setter def inverse_flattening(self, value): - """inverse_flattening. + """Assign inverse_flattening. Setting this property to a different value does not affect the behaviour of this object any further than the value of this property. @@ -453,7 +453,7 @@ def datum(self): @datum.setter def datum(self, value): - """datum. + """Assign datum. Setting this property to a different value invalidates the current values of the ellipsoid measurements because a datum encodes its own diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 97c87a22fe..0e62e3ebba 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -783,7 +783,7 @@ def shape(self): return self._values_dm.shape def xml_element(self, doc): - """Create :class:`xml.dom.minidom.Element` that describes :class:`_DimensionalMetadata`. + """Create XML element. Create the :class:`xml.dom.minidom.Element` that describes this :class:`_DimensionalMetadata`. @@ -963,12 +963,12 @@ def lazy_data(self): return super()._lazy_values() def core_data(self): - """Core data array at the core of this ancillary variable. + """Data array at the core of this ancillary variable. The data array at the core of this ancillary variable, which may be a NumPy array or a dask array. - """ + """ # noqa: D401 return super()._core_values() def has_lazy_data(self): @@ -1191,7 +1191,7 @@ def _get_2d_coord_bound_grid(bounds): class Cell(namedtuple("Cell", ["point", "bound"])): - """An immutable representation of a single cell of a coordinate. + """A coordinate cell containing a single point, or point and bounds. An immutable representation of a single cell of a coordinate, including the sample point and/or boundary position. @@ -1304,7 +1304,7 @@ def __ne__(self, other): return result def __common_cmp__(self, other, operator_method): - """Perform common methods for rich comparison operators. + """Common equality comparison. Common method called by the rich comparison operators. The method of checking equality depends on the type of the object to be compared. @@ -1312,7 +1312,7 @@ def __common_cmp__(self, other, operator_method): Cell vs Cell comparison is used to define a strict order. Non-Cell vs Cell comparison is used to define Constraint matching. - """ + """ # noqa: D401 if (isinstance(other, list) and len(other) == 1) or ( isinstance(other, np.ndarray) and other.shape == (1,) ): @@ -1579,7 +1579,7 @@ def points(self, points): @property def bounds(self): - """The coordinate bounds values, as a NumPy array. + """Coordinate bounds values. The coordinate bounds values, as a NumPy array, or None if no bound values are defined. @@ -1620,7 +1620,7 @@ def coord_system(self, value): @property def climatological(self): - """A boolean that controls whether the coordinate is a climatological time axis. + """Flag for representing a climatological time axis. A boolean that controls whether the coordinate is a climatological time axis, in which case the bounds represent a climatological period @@ -1715,7 +1715,7 @@ def core_points(self): return super()._core_values() def core_bounds(self): - """Core bounds. The points array at the core of this coord, which may be a NumPy array or a dask array.""" + """Core bounds. The points array at the core of this coord, which may be a NumPy array or a dask array.""" result = None if self.has_bounds(): result = self._bounds_dm.core_data() @@ -1728,7 +1728,7 @@ def has_lazy_points(self): return super()._has_lazy_values() def has_lazy_bounds(self): - """Return a boolean whether the coord's bounds array is a lazy dask array or not. + """Whether coordinate bounds are lazy. Return a boolean indicating whether the coord's bounds array is a lazy dask array or not. @@ -1915,7 +1915,7 @@ def mod360_adjust(compare_axis): return contiguous, diffs def is_contiguous(self, rtol=1e-05, atol=1e-08): - """Check if the Coord is bounded with contiguous bounds. + """Whether coordinate has contiguous bounds. Return True if, and only if, this Coord is bounded with contiguous bounds to within the specified relative and absolute tolerances. @@ -1947,7 +1947,7 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08): return contiguous def contiguous_bounds(self): - """Return the N+1 bound values for a contiguous bounded 1D coordinate. + """Contiguous bounds of 1D coordinate. Return the N+1 bound values for a contiguous bounded 1D coordinate of length N, or the (N+1, M+1) bound values for a contiguous bounded 2D @@ -2043,7 +2043,7 @@ def is_compatible(self, other, ignore=None): @property def bounds_dtype(self): - """The NumPy dtype of the coord's bounds. + """The NumPy dtype of the coordinates bounds. The NumPy dtype of the coord's bounds. Will be `None` if the coord does not have bounds. @@ -2067,7 +2067,7 @@ def has_bounds(self): return self._bounds_dm is not None def cell(self, index): - """Return the single :class:`Cell` instance from slicing the points/bounds. + """Point/bound cell at the given coordinate index. Return the single :class:`Cell` instance which results from slicing the points/bounds with the given index. diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 7da2c30840..5997eaacf5 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -2777,7 +2777,7 @@ def aux_factories(self): return tuple(self._aux_factories) def summary(self, shorten=False, name_padding=35): - """Summary (string) of the Cube. + """Summary of the Cube. String summary of the Cube with name+units, a list of dim coord names versus length and, optionally, a summary of all other components. diff --git a/lib/iris/experimental/regrid.py b/lib/iris/experimental/regrid.py index 8df0894619..74b356722f 100644 --- a/lib/iris/experimental/regrid.py +++ b/lib/iris/experimental/regrid.py @@ -51,7 +51,7 @@ def regrid_area_weighted_rectilinear_src_and_grid(src_cube, grid_cube, mdtol=0): - """Regrid using using the area weighted mean of data values. + """Regrid using the area weighted mean of data values. Return a new cube with data values calculated using the area weighted mean of data values from src_grid regridded onto the horizontal grid of @@ -521,7 +521,7 @@ def regrid_reference_surface( return result def __call__(self, src_cube): - """Regrid this :class:`~iris.cube.Cube` on to the target grid. + """Regrid to the target grid. Regrid this :class:`~iris.cube.Cube` on to the target grid of this :class:`UnstructuredProjectedRegridder`. @@ -617,7 +617,7 @@ class ProjectedUnstructuredLinear: """ def __init__(self, projection=None): - """Linear regridding scheme that uses scipy.interpolate.griddata. + """Linear regridding scheme. Linear regridding scheme that uses scipy.interpolate.griddata on projected unstructured data. diff --git a/lib/iris/experimental/stratify.py b/lib/iris/experimental/stratify.py index 0d2fd47fb4..3e5fb1da9f 100644 --- a/lib/iris/experimental/stratify.py +++ b/lib/iris/experimental/stratify.py @@ -49,7 +49,7 @@ def _copy_coords_without_z_dim(src, tgt, z_dim): def relevel(cube, src_levels, tgt_levels, axis=None, interpolator=None): - """Interpolate the cube onto the specified target levels. + """Perform vertical interpolation. Interpolate the cube onto the specified target levels, given the source levels of the cube. diff --git a/lib/iris/experimental/ugrid/load.py b/lib/iris/experimental/ugrid/load.py index 5e2b131426..b8665a499e 100644 --- a/lib/iris/experimental/ugrid/load.py +++ b/lib/iris/experimental/ugrid/load.py @@ -50,7 +50,7 @@ class _WarnComboCfDefaultingIgnoring(_WarnComboCfDefaulting, IrisIgnoringWarning class ParseUGridOnLoad(threading.local): def __init__(self): - """Flag for whether to use the experimental UGRID-aware version of Iris NetCDF loading. + """Thead-safe state to enable UGRID-aware NetCDF loading. A flag for dictating whether to use the experimental UGRID-aware version of Iris NetCDF loading. Object is thread-safe. diff --git a/lib/iris/experimental/ugrid/mesh.py b/lib/iris/experimental/ugrid/mesh.py index 39aec04380..147b1fd513 100644 --- a/lib/iris/experimental/ugrid/mesh.py +++ b/lib/iris/experimental/ugrid/mesh.py @@ -84,7 +84,7 @@ class Connectivity(_DimensionalMetadata): - """A CF-UGRID topology connectivity, describes the topological relationship. + """CF-UGRID topology. A CF-UGRID topology connectivity, describing the topological relationship between two types of mesh element. One or more connectivities make up a diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py index 5f91103067..f656667e63 100644 --- a/lib/iris/fileformats/_nc_load_rules/helpers.py +++ b/lib/iris/fileformats/_nc_load_rules/helpers.py @@ -2,7 +2,7 @@ # # 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. -"""Pure-Python 'helper' functions which were previously included in the Pyke rules database. +"""Helper functions for NetCDF loading rules. All the pure-Python 'helper' functions which were previously included in the Pyke rules database 'fc_rules_cf.krb'. @@ -260,7 +260,7 @@ class _WarnComboIgnoringCfLoad( 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 methods. + """Split a CF cell_methods. Split a CF cell_methods attribute string into a list of zero or more cell methods, each of which is then parsed with a regex to return a list of match @@ -335,7 +335,7 @@ class UnknownCellMethodWarning(iris.exceptions.IrisUnknownCellMethodWarning): pass -def parse_cell_methods(nc_cell_methods): +def parse_cell_methods(nc_cell_methods, cf_name=None): """Parse a CF cell_methods attribute string into a tuple of zero or more CellMethod instances. Parameters @@ -354,6 +354,7 @@ def parse_cell_methods(nc_cell_methods): results are not affected. """ + msg = None cell_methods = [] if nc_cell_methods is not None: for m in _split_cell_methods(nc_cell_methods): @@ -365,10 +366,16 @@ def parse_cell_methods(nc_cell_methods): method_words = method.split() if method_words[0].lower() not in _CM_KNOWN_METHODS: msg = "NetCDF variable contains unknown cell method {!r}" - warnings.warn( - msg.format("{}".format(method_words[0])), - category=UnknownCellMethodWarning, - ) + msg = msg.format(method_words[0]) + if cf_name: + name = "{}".format(cf_name) + msg = msg.replace("variable", "variable {!r}".format(name)) + else: + warnings.warn( + msg, + category=UnknownCellMethodWarning, + ) + msg = None d[_CM_METHOD] = method name = d[_CM_NAME] name = name.replace(" ", "") @@ -426,6 +433,9 @@ def parse_cell_methods(nc_cell_methods): comments=d[_CM_COMMENT], ) cell_methods.append(cell_method) + # only prints one warning, rather than each loop + if msg: + warnings.warn(msg, category=UnknownCellMethodWarning) return tuple(cell_methods) @@ -456,21 +466,7 @@ def build_cube_metadata(engine): # Incorporate cell methods nc_att_cell_methods = getattr(cf_var, CF_ATTR_CELL_METHODS, None) - with warnings.catch_warnings(record=True) as warning_records: - cube.cell_methods = parse_cell_methods(nc_att_cell_methods) - # Filter to get the warning we are interested in. - warning_records = [ - record - for record in warning_records - if issubclass(record.category, UnknownCellMethodWarning) - ] - if len(warning_records) > 0: - # Output an enhanced warning message. - warn_record = warning_records[0] - name = "{}".format(cf_var.cf_name) - msg = warn_record.message.args[0] - msg = msg.replace("variable", "variable {!r}".format(name)) - warnings.warn(message=msg, category=UnknownCellMethodWarning) + cube.cell_methods = parse_cell_methods(nc_att_cell_methods, cf_var.cf_name) # Set the cube global attributes. for attr_name, attr_value in cf_var.cf_group.global_attributes.items(): @@ -486,7 +482,7 @@ def build_cube_metadata(engine): ################################################################################ def _get_ellipsoid(cf_grid_var): - """Return a :class:`iris.coord_systems.GeogCS` using the relevant properties. + """Build a :class:`iris.coord_systems.GeogCS`. Return a :class:`iris.coord_systems.GeogCS` using the relevant properties of `cf_grid_var`. Returns None if no relevant properties are specified. @@ -1533,7 +1529,7 @@ def has_supported_mercator_parameters(engine, cf_name): ################################################################################ def has_supported_polar_stereographic_parameters(engine, cf_name): - """Determine whether the CF grid mapping variable has the supported values. + """Determine whether CF grid mapping variable supports Polar Stereographic. Determine whether the CF grid mapping variable has the supported values for the parameters of the Polar Stereographic projection. diff --git a/lib/iris/fileformats/_structured_array_identification.py b/lib/iris/fileformats/_structured_array_identification.py index 22d589aef1..8dada77458 100644 --- a/lib/iris/fileformats/_structured_array_identification.py +++ b/lib/iris/fileformats/_structured_array_identification.py @@ -4,15 +4,9 @@ # See LICENSE in the root of the repository for full licensing details. r"""Identification of multi-dimensional structure in a flat sequence of homogeneous objects. -One application of this is to efficiently identify a higher dimensional -structure from a sorted sequence of PPField instances; for an example, given -a list of 12 PPFields, identification that there are 3 unique "time" values -and 4 unique "height" values where time and height are linearly independent -means that we could construct a resulting cube with a shape of -``(3, 4) + ``. - The purpose of this module is to provide utilities for the identification of multi-dimensional structure in a flat sequence of homogeneous objects. + One application of this is to efficiently identify a higher dimensional structure from a sorted sequence of PPField instances; for an example, given a list of 12 PPFields, identification that there are 3 unique "time" values @@ -100,7 +94,7 @@ def __new__(cls, stride, unique_ordered_values): @property def size(self): - """The ``size`` attribute is the number of the unique values in the original array. + """Number of unique values in the original array. The ``size`` attribute is the number of the unique values in the original array. It is **not** the length of the original array. @@ -124,7 +118,7 @@ def __ne__(self, other): return not (self == other) def construct_array(self, size): - """Inverse operation of :func:`ArrayStructure.from_array`. + """Build 1D array. The inverse operation of :func:`ArrayStructure.from_array`, returning a 1D array of the given length with the appropriate repetition diff --git a/lib/iris/fileformats/cf.py b/lib/iris/fileformats/cf.py index 98c37801d5..d6dab22305 100644 --- a/lib/iris/fileformats/cf.py +++ b/lib/iris/fileformats/cf.py @@ -134,7 +134,7 @@ def identify(self, variables, ignore=None, target=None, warn=True): pass def spans(self, cf_variable): - """Determine the dimensionality of this variable is a subset. + """Determine dimensionality coverage. Determine whether the dimensionality of this variable is a subset of the specified target variable. @@ -383,7 +383,7 @@ def identify(cls, variables, ignore=None, target=None, warn=True): return result def spans(self, cf_variable): - """Determine the dimensionality of this variable is a subset. + """Determine dimensionality coverage. Determine whether the dimensionality of this variable is a subset of the specified target variable. @@ -459,7 +459,7 @@ def identify(cls, variables, ignore=None, target=None, warn=True): return result def spans(self, cf_variable): - """Determine whether the dimensionality of this variable is a subset. + """Determine dimensionality coverage. Determine whether the dimensionality of this variable is a subset of the specified target variable. @@ -801,7 +801,7 @@ def cf_label_dimensions(self, cf_data_var): ) def spans(self, cf_variable): - """Determine if dimensionality is a subset of the specified target variable. + """Determine dimensionality coverage. Determine whether the dimensionality of this variable is a subset of the specified target variable. diff --git a/lib/iris/fileformats/name.py b/lib/iris/fileformats/name.py index 36f5656c0f..bc1bb690c2 100644 --- a/lib/iris/fileformats/name.py +++ b/lib/iris/fileformats/name.py @@ -6,7 +6,7 @@ def _get_NAME_loader(filename): - """Return the appropriate load function. + """Return a NAME load function. Return the appropriate load function for a NAME file based on the contents of its header. diff --git a/lib/iris/fileformats/name_loaders.py b/lib/iris/fileformats/name_loaders.py index 989aecc80f..3e337383cb 100644 --- a/lib/iris/fileformats/name_loaders.py +++ b/lib/iris/fileformats/name_loaders.py @@ -193,7 +193,7 @@ def _build_lat_lon_for_NAME_timeseries(column_headings): def _calc_integration_period(time_avgs): - """Return a list of datetime.timedelta objects determined from the provided list. + """Calculate averaging/integration time periods. Return a list of datetime.timedelta objects determined from the provided list of averaging/integration period column headings. @@ -396,7 +396,7 @@ def _cf_height_from_name(z_coord, lower_bound=None, upper_bound=None): def _generate_cubes(header, column_headings, coords, data_arrays, cell_methods=None): - """Yield :class:`iris.cube.Cube` instances. + """Generate NAME cubes. Yield :class:`iris.cube.Cube` instances given the headers, column headings, coords and data_arrays extracted @@ -560,7 +560,7 @@ def _generate_cubes(header, column_headings, coords, data_arrays, cell_methods=N def _build_cell_methods(av_or_ints, coord): - """Build a list of :class:`iris.coords.CellMethod`. + """Create cell-methods. Return a list of :class:`iris.coords.CellMethod` instances based on the provided list of column heading entries and the @@ -601,7 +601,7 @@ def _build_cell_methods(av_or_ints, coord): def load_NAMEIII_field(filename): - """Load NAME III file returning a generator of :class:`iris.cube.Cube` instances. + """Load NAME III cubes. Load a NAME III grid output file returning a generator of :class:`iris.cube.Cube` instances. diff --git a/lib/iris/fileformats/pp.py b/lib/iris/fileformats/pp.py index f65de5fb04..fa95d0dab1 100644 --- a/lib/iris/fileformats/pp.py +++ b/lib/iris/fileformats/pp.py @@ -273,7 +273,7 @@ class STASH(collections.namedtuple("STASH", "model section item")): __slots__ = () def __new__(cls, model, section, item): - """STASH __init__. + """Create namedtuple STASH instance. Args ---- @@ -658,7 +658,7 @@ def __ne__(self, other): def _data_bytes_to_shaped_array( data_bytes, lbpack, boundary_packing, data_shape, data_type, mdi, mask=None ): - """Convert the already read binary data payload into a numpy array. + """Convert binary payload into a numpy array. Convert the already read binary data payload into a numpy array, unpacking and decompressing as per the F3 specification. @@ -845,7 +845,7 @@ def _pp_attribute_names(header_defn): class PPField(metaclass=ABCMeta): - """Heneric class for PP fields. + """Base class for PP fields. A generic class for PP fields - not specific to a particular header release number. @@ -880,7 +880,7 @@ def __init__(self, header=None): self.raw_lbpack = header[self.HEADER_DICT["lbpack"][0]] def __getattr__(self, key): - """Support deferred attribute creation. + """Return the value of the key. Method supports deferred attribute creation, which offers a significant loading optimisation, particularly when not all attributes @@ -1716,8 +1716,9 @@ def _create_field_data(field, data_shape, land_mask_field=None): """Modify a field's ``_data`` attribute. Modify a field's ``_data`` attribute either by: - * converting a 'deferred array bytes' tuple into a lazy array, - * converting LoadedArrayBytes into an actual numpy array. + + * converting a 'deferred array bytes' tuple into a lazy array, + * converting LoadedArrayBytes into an actual numpy array. If 'land_mask_field' is passed (not None), then it contains the associated landmask, which is also a field : Its data array is used as a template for diff --git a/lib/iris/fileformats/rules.py b/lib/iris/fileformats/rules.py index a9c7c377a4..bf6ae4de34 100644 --- a/lib/iris/fileformats/rules.py +++ b/lib/iris/fileformats/rules.py @@ -100,7 +100,7 @@ def scalar_cell_method(cube, method, coord_name): def has_aux_factory(cube, aux_factory_class): - """Try to find an class:`~iris.aux_factory.AuxCoordFactory`. + """Determine class:`~iris.aux_factory.AuxCoordFactory` availability within cube. Try to find an class:`~iris.aux_factory.AuxCoordFactory` instance of the specified type on the cube. @@ -113,7 +113,7 @@ def has_aux_factory(cube, aux_factory_class): def aux_factory(cube, aux_factory_class): - """Return class:`~iris.aux_factory.AuxCoordFactory` instance of the specified type from a cube. + """Retrieve class:`~iris.aux_factory.AuxCoordFactory` instance from cube. Return the class:`~iris.aux_factory.AuxCoordFactory` instance of the specified type from a cube. @@ -199,7 +199,7 @@ def _regrid_to_target(src_cube, target_coords, target_cube): def _ensure_aligned(regrid_cache, src_cube, target_cube): - """Return a cube suitable for use as an AuxCoord on target. + """Ensure dimension compatible cubes are spatially aligned. Returns a version of `src_cube` suitable for use as an AuxCoord on `target_cube`, or None if no version can be made. diff --git a/lib/iris/iterate.py b/lib/iris/iterate.py index 4022012f44..be2a436a5e 100644 --- a/lib/iris/iterate.py +++ b/lib/iris/iterate.py @@ -166,7 +166,7 @@ def izip(*cubes, **kwargs): class _ZipSlicesIterator(Iterator): - """Extension to support iteration over a collection of cubes in step. + """Support iteration over a collection of cubes. Extension to _SlicesIterator (see cube.py) to support iteration over a collection of cubes in step. diff --git a/lib/iris/tests/integration/netcdf/test_general.py b/lib/iris/tests/integration/netcdf/test_general.py index 8c27742185..751c160805 100644 --- a/lib/iris/tests/integration/netcdf/test_general.py +++ b/lib/iris/tests/integration/netcdf/test_general.py @@ -484,5 +484,40 @@ def test_path_string_save_same(self): self.assertCDL(tempfile_frompath) +@tests.skip_data +class TestWarningRepeats(tests.IrisTest): + def test_datum_once(self): + """Tests for warnings being duplicated. + + Notes + ----- + This test relies on `iris.load` throwing a warning. This warning might + be removed in the future, in which case `assert len(record) == 2 should` + be change to `assert len(record) == 1`. + + toa_brightness_temperature.nc has an AuxCoord with lazy data, and triggers a + specific part of dask which contains a `catch_warnings()` call which + causes warnings to be repeated, and so has been removed from the + `fnames` list until a solution is found for such a file. + + """ + # + fnames = [ + "false_east_north_merc.nc", + "non_unit_scale_factor_merc.nc", + # toa_brightness_temperature.nc, + ] + fpaths = [ + tests.get_data_path(("NetCDF", "mercator", fname)) for fname in fnames + ] + + with warnings.catch_warnings(record=True) as record: + warnings.simplefilter("default") + for fpath in fpaths: + iris.load(fpath) + warnings.warn("Dummy warning", category=iris.exceptions.IrisUserWarning) + assert len(record) == 2 + + if __name__ == "__main__": tests.main() diff --git a/lib/iris/util.py b/lib/iris/util.py index eeb8b54e73..878c62e2f1 100644 --- a/lib/iris/util.py +++ b/lib/iris/util.py @@ -710,7 +710,7 @@ def is_tuple_style_index(key): def _build_full_slice_given_keys(keys, ndim): - """Bild an equivalent tuple of keys which span ndims. + """Build an equivalent tuple of keys which span ndims. Given the keys passed to a __getitem__ call, build an equivalent tuple of keys which span ndims. @@ -865,7 +865,7 @@ def _wrap_function_for_method(function, docstring=None): class _MetaOrderedHashable(ABCMeta): - """Esures that non-abstract subclasses are given a default __init__ method. + """Ensures that non-abstract subclasses are given a default __init__ method. A metaclass that ensures that non-abstract subclasses of _OrderedHashable without an explicit __init__ method are given a default __init__ method @@ -1256,7 +1256,7 @@ def squeeze(cube): def file_is_newer_than(result_path, source_paths): - """Determine if the 'result' file has a later modification. + """Determine if the 'result' file was modified last. Return whether the 'result' file has a later modification time than all of the 'source' files. @@ -1521,7 +1521,7 @@ def _is_circular(points, modulus, bounds=None): def promote_aux_coord_to_dim_coord(cube, name_or_coord): - r"""Promotes an AuxCoord on the cube to a DimCoord. + r"""Promote an auxiliary to a dimension coordinate on the cube. This AuxCoord must be associated with a single cube dimension. If the AuxCoord is associated with a dimension that already has a DimCoord, that @@ -1736,10 +1736,6 @@ def _meshgrid(*xi, **kwargs): associated input 1D coordinate. This is not the case prior to numpy v1.13, where the output dtype is cast up to its highest resolution, regardlessly. - @numpy v1.13, the dtype of each output n-D coordinate is the same as its - associated input 1D coordinate. This is not the case prior to numpy v1.13, - where the output dtype is cast up to its highest resolution, regardlessly. - Reference: https://github.com/numpy/numpy/pull/5302 """ @@ -1751,7 +1747,7 @@ def _meshgrid(*xi, **kwargs): def find_discontiguities(cube, rel_tol=1e-5, abs_tol=1e-8): - """Search the 'x' and 'y' coord on the cube for discontiguities. + """Identify spatial discontiguities. Searches the 'x' and 'y' coord on the cube for discontiguities in the bounds array, returned as a boolean array (True for all cells which are diff --git a/setup.py b/setup.py index 419001145b..7947643982 100644 --- a/setup.py +++ b/setup.py @@ -25,12 +25,12 @@ def run(self): def custom_command(cmd, help=""): - """Generate a custom command. + """Factory function to generate a custom command. Custom command will add additional behaviour to build the CF standard names module. - """ + """ # noqa: D401 class CustomCommand(cmd): description = help or cmd.description