Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions docs/src/further_topics/metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ actual `data attribute`_ names of the metadata members on the Iris class.
metadata members are Iris specific terms, rather than recognised `CF Conventions`_
terms.

.. note::

:class:`~iris.cube.Cube` :attr:`~iris.cube.Cube.attributes` implement the
concept of dataset-level and variable-level attributes, to enable correct
NetCDF loading and saving (see :class:`~iris.cube.CubeAttrsDict` and NetCDF
:func:`~iris.fileformats.netcdf.saver.save` for more). ``attributes`` on
the other classes do not have this distinction, but the ``attributes``
members of ALL the classes still have the same interface, and can be
compared.


Common Metadata API
===================
Expand Down Expand Up @@ -128,7 +138,9 @@ For example, given the following :class:`~iris.cube.Cube`,
source 'Data from Met Office Unified Model 6.05'

We can easily get all of the associated metadata of the :class:`~iris.cube.Cube`
using the ``metadata`` property:
using the ``metadata`` property (note the specialised
:class:`~iris.cube.CubeAttrsDict` for the :attr:`~iris.cube.Cube.attributes`,
as mentioned earlier):

>>> cube.metadata
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes=CubeAttrsDict(globals={'Conventions': 'CF-1.5'}, locals={'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}), cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
Expand Down Expand Up @@ -701,7 +713,7 @@ which is replaced with a **different value**,
>>> metadata != cube.metadata
True
>>> metadata.combine(cube.metadata) # doctest: +SKIP
CubeMetadata(standard_name=None, long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'Model scenario': 'A1B', 'STASH': STASH(model=1, section=3, item=236), 'source': 'Data from Met Office Unified Model 6.05'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
CubeMetadata(standard_name=None, long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05', 'Conventions': 'CF-1.5'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))

The ``combine`` method combines metadata by performing a **strict** comparison
between each of the associated metadata member values,
Expand Down
5 changes: 4 additions & 1 deletion docs/src/userguide/iris_cubes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ A cube consists of:
data dimensions as the coordinate has dimensions.

* an attributes dictionary which, other than some protected CF names, can
hold arbitrary extra metadata.
hold arbitrary extra metadata. This implements the concept of dataset-level
and variable-level attributes when loading and and saving NetCDF files (see
:class:`~iris.cube.CubeAttrsDict` and NetCDF
:func:`~iris.fileformats.netcdf.saver.save` for more).
* a list of cell methods to represent operations which have already been
applied to the data (e.g. "mean over time")
* a list of coordinate "factories" used for deriving coordinates from the
Expand Down
9 changes: 9 additions & 0 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ This document explains the changes made to Iris for this release
✨ Features
===========

#. `@pp-mo`_, `@lbdreyer`_ and `@trexfeathers`_ improved
:class:`~iris.cube.Cube` :attr:`~iris.cube.Cube.attributes` handling to
better preserve the distinction between dataset-level and variable-level
attributes, allowing file-Cube-file round-tripping of NetCDF attributes. See
:class:`~iris.cube.CubeAttrsDict` and NetCDF
:func:`~iris.fileformats.netcdf.saver.save` for more. (:pull:`5152`,
`split attributes project`_)

#. `@rcomer`_ rewrote :func:`~iris.util.broadcast_to_shape` so it now handles
lazy data. (:pull:`5307`)

Expand Down Expand Up @@ -94,3 +102,4 @@ This document explains the changes made to Iris for this release

.. comment
Whatsnew resources in alphabetical order:
.. _split attributes project: https://github.com/orgs/SciTools/projects/5?pane=info
7 changes: 6 additions & 1 deletion lib/iris/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ class CubeAttrsDict(MutableMapping):

>>> from iris.cube import Cube
>>> cube = Cube([0])
>>> # CF defines 'history' as global by default.
>>> cube.attributes.update({"history": "from test-123", "mycode": 3})
>>> print(cube.attributes)
{'history': 'from test-123', 'mycode': 3}
Expand All @@ -843,6 +844,9 @@ class CubeAttrsDict(MutableMapping):

"""

# TODO: Create a 'further topic' / 'tech paper' on NetCDF I/O, including
# discussion of attribute handling.

def __init__(
self,
combined: Optional[Union[Mapping, str]] = "__unspecified",
Expand All @@ -853,7 +857,7 @@ def __init__(
Create a cube attributes dictionary.

We support initialisation from a single generic mapping input, using the default
global/local assignment rules explained at :meth:`__setatrr__`, or from
global/local assignment rules explained at :meth:`__setattr__`, or from
two separate mappings. Two separate dicts can be passed in the ``locals``
and ``globals`` args, **or** via a ``combined`` arg which has its own
``.globals`` and ``.locals`` properties -- so this allows passing an existing
Expand All @@ -878,6 +882,7 @@ def __init__(
--------

>>> from iris.cube import CubeAttrsDict
>>> # CF defines 'history' as global by default.
>>> CubeAttrsDict({'history': 'data-story', 'comment': 'this-cube'})
CubeAttrsDict(globals={'history': 'data-story'}, locals={'comment': 'this-cube'})

Expand Down