diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000000..d4aedd3955 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,227 @@ +# Reference: +# - https://cirrus-ci.org/guide/writing-tasks/ +# - https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks +# - https://cirrus-ci.org/guide/linux/ +# - https://cirrus-ci.org/guide/macOS/ +# - https://cirrus-ci.org/guide/windows/ +# - https://hub.docker.com/_/gcc/ +# - https://hub.docker.com/_/python/ + +# +# Global defaults. +# +container: + image: python:3.8 + cpu: 2 + memory: 4G + + +env: + # Maximum cache period (in weeks) before forcing a new cache upload. + CACHE_PERIOD: "2" + # Increment the build number to force new cartopy cache upload. + CARTOPY_CACHE_BUILD: "0" + # Increment the build number to force new conda cache upload. + CONDA_CACHE_BUILD: "0" + # Increment the build number to force new nox cache upload. + NOX_CACHE_BUILD: "0" + # Increment the build number to force new pip cache upload. + PIP_CACHE_BUILD: "0" + # Pip package to be upgraded/installed. + PIP_CACHE_PACKAGES: "pip setuptools wheel nox" + # Git commit hash for iris test data. + IRIS_TEST_DATA_REF: "fffb9b14b9cb472c5eb2ebb7fd19acb7f6414a30" + # Base directory for the iris-test-data. + IRIS_TEST_DATA_DIR: ${HOME}/iris-test-data + + +# +# Linting +# +lint_task: + auto_cancellation: true + name: "${CIRRUS_OS}: flake8 and black" + pip_cache: + folder: ~/.cache/pip + fingerprint_script: + - echo "${CIRRUS_TASK_NAME}" + - echo "$(date +%Y).$(($(date +%U) / ${CACHE_PERIOD})):${PIP_CACHE_BUILD} ${PIP_CACHE_PACKAGES}" + lint_script: + - pip list + - python -m pip install --retries 3 --upgrade ${PIP_CACHE_PACKAGES} + - pip list + - nox --session flake8 + - nox --session black + + +# +# YAML alias for common linux test infra-structure. +# +linux_task_template: &LINUX_TASK_TEMPLATE + auto_cancellation: true + env: + IRIS_REPO_DIR: ${CIRRUS_WORKING_DIR} + PATH: ${HOME}/miniconda/bin:${PATH} + SITE_CFG: ${CIRRUS_WORKING_DIR}/lib/iris/etc/site.cfg + conda_cache: + folder: ${HOME}/miniconda + fingerprint_script: + - wget --quiet https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh + - echo "${CIRRUS_OS} $(sha256sum miniconda.sh)" + - echo "$(date +%Y).$(($(date +%U) / ${CACHE_PERIOD})):${CONDA_CACHE_BUILD}" + populate_script: + - bash miniconda.sh -b -p ${HOME}/miniconda + - conda config --set always_yes yes --set changeps1 no + - conda config --set show_channel_urls True + - conda config --add channels conda-forge + - conda update --quiet --name base conda + - conda install --quiet --name base nox pip + cartopy_cache: + folder: ${HOME}/.local/share/cartopy + fingerprint_script: + - echo "${CIRRUS_OS}" + - echo "$(date +%Y).$(($(date +%U) / ${CACHE_PERIOD})):${CARTOPY_CACHE_BUILD}" + nox_cache: + folder: ${CIRRUS_WORKING_DIR}/.nox + fingerprint_script: + - echo "${CIRRUS_TASK_NAME}" + - echo "$(date +%Y).$(($(date +%U) / ${CACHE_PERIOD})):${NOX_CACHE_BUILD}" + - sha256sum ${CIRRUS_WORKING_DIR}/requirements/ci/py$(echo ${PY_VER} | tr -d ".").yml + + +# +# Testing Minimal (Linux) +# +linux_minimal_task: + matrix: + env: + PY_VER: 3.6 + env: + PY_VER: 3.7 + name: "${CIRRUS_OS}: py${PY_VER} tests (minimal)" + container: + image: gcc:latest + cpu: 2 + memory: 4G + << : *LINUX_TASK_TEMPLATE + tests_script: + - echo "[Resources]" > ${SITE_CFG} + - echo "doc_dir = ${CIRRUS_WORKING_DIR}/docs/iris" >> ${SITE_CFG} + - nox --session tests + + +# +# Testing Full (Linux) +# +linux_task: + matrix: + env: + PY_VER: 3.6 + env: + PY_VER: 3.7 + name: "${CIRRUS_OS}: py${PY_VER} tests (full)" + container: + image: gcc:latest + cpu: 6 + memory: 8G + data_cache: + folder: ${IRIS_TEST_DATA_DIR} + fingerprint_script: + - echo "${IRIS_TEST_DATA_REF}" + populate_script: + - wget --quiet https://github.com/SciTools/iris-test-data/archive/${IRIS_TEST_DATA_REF}.zip -O iris-test-data.zip + - unzip -q iris-test-data.zip + - mv iris-test-data-$(echo "${IRIS_TEST_DATA_REF}" | sed "s/^v//") ${IRIS_TEST_DATA_DIR} + << : *LINUX_TASK_TEMPLATE + tests_script: + - echo "[Resources]" > ${SITE_CFG} + - echo "test_data_dir = ${IRIS_TEST_DATA_DIR}/test_data" >> ${SITE_CFG} + - echo "doc_dir = ${CIRRUS_WORKING_DIR}/docs/iris" >> ${SITE_CFG} + - nox --session tests + + +# +# Testing Documentation Gallery (Linux) +# +gallery_task: + matrix: + env: + PY_VER: 3.6 + env: + PY_VER: 3.7 + name: "${CIRRUS_OS}: py${PY_VER} doc tests (gallery)" + container: + image: gcc:latest + cpu: 2 + memory: 4G + data_cache: + folder: ${IRIS_TEST_DATA_DIR} + fingerprint_script: + - echo "${IRIS_TEST_DATA_REF}" + populate_script: + - wget --quiet https://github.com/SciTools/iris-test-data/archive/${IRIS_TEST_DATA_REF}.zip -O iris-test-data.zip + - unzip -q iris-test-data.zip + - mv iris-test-data-$(echo "${IRIS_TEST_DATA_REF}" | sed "s/^v//") ${IRIS_TEST_DATA_DIR} + << : *LINUX_TASK_TEMPLATE + tests_script: + - echo "[Resources]" > ${SITE_CFG} + - echo "test_data_dir = ${IRIS_TEST_DATA_DIR}/test_data" >> ${SITE_CFG} + - echo "doc_dir = ${CIRRUS_WORKING_DIR}/docs/iris" >> ${SITE_CFG} + - nox --session gallery + + +# +# Testing Documentation (Linux) +# +doctest_task: + matrix: + env: + PY_VER: 3.7 + name: "${CIRRUS_OS}: py${PY_VER} doc tests" + container: + image: gcc:latest + cpu: 2 + memory: 4G + env: + MPL_RC_DIR: ${HOME}/.config/matplotlib + MPL_RC_FILE: ${HOME}/.config/matplotlib/matplotlibrc + data_cache: + folder: ${IRIS_TEST_DATA_DIR} + fingerprint_script: + - echo "${IRIS_TEST_DATA_REF}" + populate_script: + - wget --quiet https://github.com/SciTools/iris-test-data/archive/${IRIS_TEST_DATA_REF}.zip -O iris-test-data.zip + - unzip -q iris-test-data.zip + - mv iris-test-data-$(echo "${IRIS_TEST_DATA_REF}" | sed "s/^v//") ${IRIS_TEST_DATA_DIR} + << : *LINUX_TASK_TEMPLATE + tests_script: + - echo "[Resources]" > ${SITE_CFG} + - echo "test_data_dir = ${IRIS_TEST_DATA_DIR}/test_data" >> ${SITE_CFG} + - echo "doc_dir = ${CIRRUS_WORKING_DIR}/docs/iris" >> ${SITE_CFG} + - mkdir -p ${MPL_RC_DIR} + - echo "backend : agg" > ${MPL_RC_FILE} + - echo "image.cmap : viridis" >> ${MPL_RC_FILE} + - nox --session doctest + + +# +# Testing Documentation Link Check (Linux) +# +link_task: + matrix: + env: + PY_VER: 3.7 + name: "${CIRRUS_OS}: py${PY_VER} doc link check" + container: + image: gcc:latest + cpu: 2 + memory: 4G + env: + MPL_RC_DIR: ${HOME}/.config/matplotlib + MPL_RC_FILE: ${HOME}/.config/matplotlib/matplotlibrc + << : *LINUX_TASK_TEMPLATE + tests_script: + - mkdir -p ${MPL_RC_DIR} + - echo "backend : agg" > ${MPL_RC_FILE} + - echo "image.cmap : viridis" >> ${MPL_RC_FILE} + - nox --session linkcheck diff --git a/.gitignore b/.gitignore index d589c306fe..618913e7ec 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ var sdist develop-eggs .installed.cfg +.nox # Installer logs pip-log.txt diff --git a/.stickler.yml b/.stickler.yml deleted file mode 100644 index 6edee0f6a5..0000000000 --- a/.stickler.yml +++ /dev/null @@ -1,4 +0,0 @@ -linters: - flake8: - python: 3 - config: ./.flake8 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ab1accab4a..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,169 +0,0 @@ -# Please update the test data git references below if appropriate. -# -# Note: Contrary to the travis documentation, -# http://about.travis-ci.org/docs/user/languages/python/#Travis-CI-Uses-Isolated-virtualenvs -# we will use conda to give us a much faster setup time. - -language: minimal -dist: xenial - -env: - global: - # The decryption key for the encrypted .github/deploy_key.scitools-docs.enc. - - secure: "N9/qBUT5CqfC7KQBDy5mIWZcGNuUJk3e/qmKJpotWYV+zwOI4GghJsRce6nFnlRiwl65l5oBEcvf3+sBvUfbZqh7U0MdHpw2tHhr2FSCmMB3bkvARZblh9M37f4da9G9VmRkqnyBM5G5TImXtoq4dusvNWKvLW0qETciaipq7ws=" - matrix: - - PYTHON_VERSION='36' TEST_TARGET='default' TEST_MINIMAL=true - - PYTHON_VERSION='36' TEST_TARGET='default' TEST_BLACK=true - - PYTHON_VERSION='36' TEST_TARGET='gallery' - - PYTHON_VERSION='37' TEST_TARGET='default' TEST_MINIMAL=true - - PYTHON_VERSION='37' TEST_TARGET='default' TEST_BLACK=true - - PYTHON_VERSION='37' TEST_TARGET='gallery' - - PYTHON_VERSION='37' TEST_TARGET='doctest' PUSH_BUILT_DOCS=true - - PYTHON_VERSION='37' TEST_TARGET='linkcheck' - # TODO: Dependencies for sphinxcontrib-spelling to be in place before this - # spelling code block is enabled - #- PYTHON_VERSION='37' TEST_TARGET='spelling' - -git: - # We need a deep clone so that we can compute the age of the files using their git history. - depth: 10000 - -install: - - > - export IRIS_TEST_DATA_REF="fffb9b14b9cb472c5eb2ebb7fd19acb7f6414a30"; - export IRIS_TEST_DATA_SUFFIX=$(echo "${IRIS_TEST_DATA_REF}" | sed "s/^v//"); - - # Install miniconda - # ----------------- - - > - echo 'Installing miniconda'; - export CONDA_BASE="https://repo.continuum.io/miniconda/Miniconda"; - wget --quiet ${CONDA_BASE}3-latest-Linux-x86_64.sh -O miniconda.sh; - bash miniconda.sh -b -p ${HOME}/miniconda; - export PATH="${HOME}/miniconda/bin:${PATH}"; - - # Create the testing environment - # ------------------------------ - # Explicitly add defaults channel, see https://github.com/conda/conda/issues/2675 - - > - echo 'Configure conda and create an environment'; - conda config --set always_yes yes --set changeps1 no; - conda config --set show_channel_urls True; - conda config --add channels conda-forge; - conda update --quiet conda; - export ENV_NAME='iris-dev'; - ENV_FILE="requirements/ci/py${PYTHON_VERSION}.yml"; - cat ${ENV_FILE}; - conda env create --quiet --file=${ENV_FILE}; - source activate ${ENV_NAME}; - export PREFIX="${CONDA_PREFIX}"; - - # Output debug info - - > - conda list -n ${ENV_NAME}; - conda list -n ${ENV_NAME} --explicit; - conda info -a; - -# Pre-load Natural Earth data to avoid multiple, overlapping downloads. -# i.e. There should be no DownloadWarning reports in the log. - - python -c 'import cartopy; cartopy.io.shapereader.natural_earth()' - -# iris test data - - > - if [[ "${TEST_MINIMAL}" != true ]]; then - wget --quiet -O iris-test-data.zip https://github.com/SciTools/iris-test-data/archive/${IRIS_TEST_DATA_REF}.zip; - unzip -q iris-test-data.zip; - mv "iris-test-data-${IRIS_TEST_DATA_SUFFIX}" iris-test-data; - fi - -# set config paths - - > - SITE_CFG="lib/iris/etc/site.cfg"; - echo "[Resources]" > ${SITE_CFG}; - echo "test_data_dir = $(pwd)/iris-test-data/test_data" >> ${SITE_CFG}; - echo "doc_dir = $(pwd)/docs/iris" >> ${SITE_CFG}; - echo "[System]" >> ${SITE_CFG}; - echo "udunits2_path = ${PREFIX}/lib/libudunits2.so" >> ${SITE_CFG}; - - - python setup.py --quiet install - -script: - # Capture install-dir: As a test command must be last for get Travis to check - # the RC, so it's best to start each operation with an absolute cd. - - export INSTALL_DIR=$(pwd) - - - > - if [[ "${TEST_BLACK}" == 'true' ]]; then - echo $(black --version); - rm ${INSTALL_DIR}/.gitignore; - black --check ${INSTALL_DIR}; - fi - - - > - if [[ "${TEST_TARGET}" == 'default' ]]; then - export IRIS_REPO_DIR=${INSTALL_DIR}; - python -m iris.tests.runner --default-tests --system-tests; - fi - - - > - if [[ "${TEST_TARGET}" == 'gallery' ]]; then - python -m iris.tests.runner --gallery-tests; - fi - - # Build the docs. - - > - if [[ "${TEST_TARGET}" == 'doctest' ]]; then - MPL_RC_DIR="${HOME}/.config/matplotlib"; - mkdir -p ${MPL_RC_DIR}; - echo 'backend : agg' > ${MPL_RC_DIR}/matplotlibrc; - echo 'image.cmap : viridis' >> ${MPL_RC_DIR}/matplotlibrc; - cd ${INSTALL_DIR}/docs/iris; - make clean html && make doctest; - fi - - # check the links in the docs - - > - if [[ "${TEST_TARGET}" == 'linkcheck' ]]; then - MPL_RC_DIR="${HOME}/.config/matplotlib"; - mkdir -p ${MPL_RC_DIR}; - echo 'backend : agg' > ${MPL_RC_DIR}/matplotlibrc; - echo 'image.cmap : viridis' >> ${MPL_RC_DIR}/matplotlibrc; - cd ${INSTALL_DIR}/docs/iris; - make clean && make linkcheck; - fi - - # TODO: Dependencies for sphinxcontrib-spelling to be in place before this - # spelling code block is enabled - - # check the spelling in the docs - # - > - # if [[ "${TEST_TARGET}" == 'spelling' ]]; then - # MPL_RC_DIR="${HOME}/.config/matplotlib"; - # mkdir -p ${MPL_RC_DIR}; - # echo 'backend : agg' > ${MPL_RC_DIR}/matplotlibrc; - # echo 'image.cmap : viridis' >> ${MPL_RC_DIR}/matplotlibrc; - # cd ${INSTALL_DIR}/docs/iris; - # make clean && make spelling; - # fi - - # Split the organisation out of the slug. See https://stackoverflow.com/a/5257398/741316 for description. - # NOTE: a *separate* "export" command appears to be necessary here : A command of the - # form "export ORG=.." failed to define ORG for the following command (?!) - - > - ORG=$(echo ${TRAVIS_REPO_SLUG} | cut -d/ -f1); - export ORG - - - echo "Travis job context ORG=${ORG}; TRAVIS_EVENT_TYPE=${TRAVIS_EVENT_TYPE}; PUSH_BUILT_DOCS=${PUSH_BUILT_DOCS}" - - # When we merge a change to SciTools/iris, we can push docs to github pages. - # At present, only the Python 3.7 "doctest" job does this. - # Results appear at https://scitools-docs.github.io/iris/<>/index.html - - if [[ "${ORG}" == 'SciTools' && "${TRAVIS_EVENT_TYPE}" == 'push' && "${PUSH_BUILT_DOCS}" == 'true' ]]; then - cd ${INSTALL_DIR}; - conda install --quiet -n ${ENV_NAME} pip; - pip install doctr; - doctr deploy --deploy-repo SciTools-docs/iris --built-docs docs/iris/src/_build/html - --key-path .github/deploy_key.scitools-docs.enc - --no-require-master - ${TRAVIS_BRANCH:-${TRAVIS_TAG}}; - fi diff --git a/README.md b/README.md index aeadb52d93..6339491955 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,12 @@

- - -Travis-CI + +Cirrus-CI - Documentation Status +Documentation Status conda-forge downloads @@ -26,9 +25,6 @@ Latest version - -Stable docs Commits since last release diff --git a/docs/iris/src/common_links.inc b/docs/iris/src/common_links.inc index 94c2f3c92b..0bc8ca60e6 100644 --- a/docs/iris/src/common_links.inc +++ b/docs/iris/src/common_links.inc @@ -25,3 +25,4 @@ .. _sphinx: https://www.sphinx-doc.org/en/master/ .. _napolean: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/sphinxcontrib.napoleon.html .. _legacy documentation: https://scitools.org.uk/iris/docs/v2.4.0/ +.. _cirrus-ci: https://cirrus-ci.com/github/SciTools/iris diff --git a/docs/iris/src/conf.py b/docs/iris/src/conf.py index 9e6276f544..ab7689479a 100644 --- a/docs/iris/src/conf.py +++ b/docs/iris/src/conf.py @@ -261,13 +261,14 @@ def autolog(message): # url link checker. Some links work but report as broken, lets ignore them. # See https://www.sphinx-doc.org/en/1.2/config.html#options-for-the-linkcheck-builder linkcheck_ignore = [ - "https://github.com/SciTools/iris/commit/69597eb3d8501ff16ee3d56aef1f7b8f1c2bb316#diff-1680206bdc5cfaa83e14428f5ba0f848", - "http://www.wmo.int/pages/prog/www/DPFS/documents/485_Vol_I_en_colour.pdf", + "http://cfconventions.org", "http://code.google.com/p/msysgit/downloads/list", + "http://effbot.org", + "https://github.com", + "http://www.personal.psu.edu/cab38/ColorBrewer/ColorBrewer_updates.html", "http://schacon.github.com/git", - "https://github.com/SciTools/iris/pull", - "https://github.com/SciTools/iris/issue", - "http://cfconventions.org", + "http://scitools.github.com/cartopy", + "http://www.wmo.int/pages/prog/www/DPFS/documents/485_Vol_I_en_colour.pdf", ] # list of sources to exclude from the build. diff --git a/docs/iris/src/developers_guide/release.rst b/docs/iris/src/developers_guide/release.rst index d71f149186..2ec787a780 100644 --- a/docs/iris/src/developers_guide/release.rst +++ b/docs/iris/src/developers_guide/release.rst @@ -3,17 +3,28 @@ Releases ======== -A release of Iris is a `tag on the SciTools/Iris`_ +A release of Iris is a `tag on the SciTools/Iris`_ Github repository. The summary below is of the main areas that constitute the release. The final section details the :ref:`iris_development_releases_steps` to take. +Before release +-------------- + +Deprecations +~~~~~~~~~~~~ + +Ensure that any behaviour which has been deprecated for the correct number of +previous releases is now finally changed. More detail, including the correct +number of releases, is in :ref:`iris_development_deprecations`. + + Release branch -------------- -Once the features intended for the release are on master, a release branch +Once the features intended for the release are on master, a release branch should be created, in the SciTools/Iris repository. This will have the name: :literal:`v{major release number}.{minor release number}.x` @@ -35,12 +46,12 @@ number, e.g.: :literal:`v1.9.0rc1` -If created, the pre-release shall be available for a minimum of two weeks +If created, the pre-release shall be available for a minimum of two weeks prior to the release being cut. However a 4 week period should be the goal to allow user groups to be notified of the existence of the pre-release and encouraged to test the functionality. -A pre-release is expected for a minor release, but will not for a +A pre-release is expected for a major or minor release, but not for a point release. If new features are required for a release after a release candidate has been @@ -59,7 +70,7 @@ Steps to achieve this can be found in the :ref:`iris_development_releases_steps` The release ----------- -The final steps are to change the version string in the source of +The final steps are to change the version string in the source of :literal:`Iris.__init__.py` and include the release date in the relevant what's new page within the documentation. @@ -72,7 +83,7 @@ Conda recipe Once a release is cut, the `Iris feedstock`_ for the conda recipe must be updated to build the latest release of Iris and push this artefact to -`conda forge`_. +`conda forge`_. .. _Iris feedstock: https://github.com/conda-forge/iris-feedstock/tree/master/recipe .. _conda forge: https://anaconda.org/conda-forge/iris @@ -102,7 +113,7 @@ New features shall not be included in a point release, these are for bug fixes. A point release does not require a release candidate, but the rest of the release process is to be followed, including the merge back of changes into -:literal:`master`. +:literal:`master`. .. _iris_development_releases_steps: @@ -118,7 +129,7 @@ Release steps #. Create the branch ``1.9.x`` on the main repo, not in a forked repo, for the release candidate or release. The only exception is for a point/bugfix release as it should already exist -#. Update the what's new for the release: +#. Update the what's new for the release: * Copy ``docs/iris/src/whatsnew/latest.rst`` to a file named ``v1.9.rst`` @@ -128,6 +139,8 @@ Release steps the date and version in the format of ``v1.9 (DD MMM YYYY)``. For example ``v1.9 (03 Aug 2020)`` * Review the file for correctness + * Work with the development team to create a 'highlights' section at the + top of the file, providing extra detail on notable changes * Add ``v1.9.rst`` to git and commit all changes, including removal of ``latest.rst`` @@ -138,7 +151,7 @@ Release steps #. Update the ``Iris.__init__.py`` version string, to ``1.9.0`` #. Check your changes by building the documentation and viewing the changes -#. Once all the above steps are complete, the release is cut, using +#. Once all the above steps are complete, the release is cut, using the :guilabel:`Draft a new release` button on the `Iris release page `_ @@ -146,16 +159,16 @@ Release steps Post release steps ~~~~~~~~~~~~~~~~~~ -#. Check the documentation has built on `Read The Docs`_. The build is +#. Check the documentation has built on `Read The Docs`_. The build is triggered by any commit to master. Additionally check that the versions available in the pop out menu in the bottom left corner include the new release version. If it is not present you will need to configure the versions available in the **admin** dashboard in Read The Docs -#. Copy ``docs/iris/src/whatsnew/latest.rst.template`` to +#. Copy ``docs/iris/src/whatsnew/latest.rst.template`` to ``docs/iris/src/whatsnew/latest.rst``. This will reset the file with the ``unreleased`` heading and placeholders for the what's new headings -#. Add back in the reference to ``latest.rst`` to the what's new index +#. Add back in the reference to ``latest.rst`` to the what's new index ``docs/iris/src/whatsnew/index.rst`` #. Update ``Iris.__init__.py`` version string to show as ``1.10.dev0`` #. Merge back to master diff --git a/docs/iris/src/further_topics/lenient_metadata.rst b/docs/iris/src/further_topics/lenient_metadata.rst index 1b31759d9a..ada7049786 100644 --- a/docs/iris/src/further_topics/lenient_metadata.rst +++ b/docs/iris/src/further_topics/lenient_metadata.rst @@ -335,10 +335,10 @@ Lenient combination The behaviour of the lenient ``combine`` metadata class method is outlined in :numref:`lenient combine table`, and as with :ref:`lenient equality` and -:ref:`lenient difference` is enabled throught the ``lenient`` keyword argument. +:ref:`lenient difference` is enabled through the ``lenient`` keyword argument. The difference in behaviour between **lenient** and -:ref:`strict combination ` is centered around the lenient +:ref:`strict combination ` is centred around the lenient handling of combining **something** with **nothing** (``None``) to return **something**. Whereas strict combination will only return a result from combining identical objects. diff --git a/docs/iris/src/userguide/plotting_a_cube.rst b/docs/iris/src/userguide/plotting_a_cube.rst index f646aa4b3e..9de20dc6c9 100644 --- a/docs/iris/src/userguide/plotting_a_cube.rst +++ b/docs/iris/src/userguide/plotting_a_cube.rst @@ -209,7 +209,7 @@ the temperature at some latitude cross-sections. ``_. In order to run this example, you will need to copy the code into a file - and run it using ``python2.7 my_file.py``. + and run it using ``python my_file.py``. Plotting 2-dimensional cubes diff --git a/docs/iris/src/userguide/saving_iris_cubes.rst b/docs/iris/src/userguide/saving_iris_cubes.rst index cca8b44bd1..3a30321979 100644 --- a/docs/iris/src/userguide/saving_iris_cubes.rst +++ b/docs/iris/src/userguide/saving_iris_cubes.rst @@ -6,7 +6,7 @@ Saving Iris cubes Iris supports the saving of cubes and cube lists to: -* CF netCDF (version 1.6) +* CF netCDF (version 1.7) * GRIB edition 2 (if `iris-grib `_ is installed) * Met Office PP diff --git a/docs/iris/src/whatsnew/3.0.rst b/docs/iris/src/whatsnew/3.0.rst index 0caba69de8..0a9dcd89b0 100644 --- a/docs/iris/src/whatsnew/3.0.rst +++ b/docs/iris/src/whatsnew/3.0.rst @@ -1,12 +1,45 @@ .. include:: ../common_links.inc -v3.0 (01 Oct 2020) +v3.0 (02 Oct 2020) ****************** This document explains the changes made to Iris for this release (:doc:`View all changes `.) +.. dropdown:: :opticon:`report` Release Highlights + :container: + shadow + :title: text-primary text-center font-weight-bold + :body: bg-light + :animate: fade-in + :open: + + The highlights for this major release of Iris include: + + * We've finally dropped support for ``Python 2``, so welcome to ``Iris 3`` + and ``Python 3``! + * We've extended our coverage of the `CF Conventions and Metadata`_ by + introducing support for `CF Ancillary Data`_ and `Quality Flags`_, + * Lazy regridding is now available for several regridding schemes, + * Managing and manipulating metadata within Iris is now easier and more + consistent thanks to the introduction of a new common metadata API, + * :ref:`Cube arithmetic ` has been significantly improved with + regards to extended broadcasting, auto-transposition and a more lenient + behaviour towards handling metadata and coordinates, + * Our :ref:`documentation ` has been refreshed, + restructured, revitalised and rehosted on `readthedocs`_, + * It's now easier than ever to :ref:`install Iris ` + as a user or a developer, and the newly revamped developers guide walks + you though how you can :ref:`get involved ` + and contribute to Iris, + * Also, this is a major release of Iris, so please be aware of the + :ref:`incompatible changes ` and + :ref:`deprecations `. + + And finally, get in touch with us on `GitHub`_ if you have any issues or + feature requests for improving Iris. Enjoy! + + 📢 Announcements ================ @@ -16,6 +49,9 @@ This document explains the changes made to Iris for this release and performance metrics tool for routine evaluation of Earth system models in CMIP*". Welcome aboard! 🎉 +* Congratulations also goes to `@jonseddon`_ who recently became an Iris core + developer. We look forward to seeing more of your awesome contributions! 🎉 + ✨ Features =========== @@ -132,6 +168,9 @@ This document explains the changes made to Iris for this release Previously, the first tick label would occasionally be duplicated. This also removes the use of Matplotlib's deprecated ``IndexFormatter``. (:pull:`3857`) +* `@znicholls`_ fixed :meth:`~iris.quickplot._title` to only check ``units.is_time_reference`` if the ``units`` symbol is not used. (:pull:`3902`) + +.. _whatsnew 3.0 changes: 💣 Incompatible Changes ======================= @@ -166,6 +205,9 @@ This document explains the changes made to Iris for this release :func:`iris.experimental.concatenate.concatenate` function raised an exception. (:pull:`3523`) +* `@stephenworsley`_ changed the default units of :class:`~iris.coords.DimCoord` + and :class:`~iris.coords.AuxCoord` from `"1"` to `"unknown"`. (:pull:`3795`) + * `@stephenworsley`_ changed Iris objects loaded from NetCDF-CF files to have ``units='unknown'`` where the corresponding NetCDF variable has no ``units`` property. Previously these cases defaulted to ``units='1'``. @@ -191,6 +233,8 @@ This document explains the changes made to Iris for this release exception was raised. (:pull:`3785`) +.. _whatsnew 3.0 deprecations: + 🔥 Deprecations =============== @@ -238,6 +282,8 @@ This document explains the changes made to Iris for this release dependency group. We no longer consider it to be an extension. (:pull:`3762`) +.. _whatsnew 3.0 docs: + 📚 Documentation ================ @@ -308,6 +354,8 @@ This document explains the changes made to Iris for this release included documentation for :ref:`metadata`, :ref:`lenient metadata`, and :ref:`lenient maths`. (:pull:`3890`) +* `@jonseddon`_ updated the CF version of the netCDF saver in the + :ref:`saving_iris_cubes` section and in the equivalent function docstring. 💼 Internal =========== @@ -375,6 +423,16 @@ This document explains the changes made to Iris for this release * `@owena11`_ identified and optimised a bottleneck in ``FieldsFile`` header loading due to the use of :func:`numpy.fromfile`. (:pull:`3791`) +* `@znicholls`_ added a test for plotting with the label being taken from the unit's symbol, see :meth:`~iris.tests.test_quickplot.TestLabels.test_pcolormesh_str_symbol` (:pull:`3902`). + +* `@znicholls`_ made :func:`~iris.tests.idiff.step_over_diffs` robust to hyphens (``-``) in the input path (i.e. the ``result_dir`` argument) (:pull:`3902`). + +* `@bjlittle`_ migrated the CIaaS from `travis-ci`_ to `cirrus-ci`_. (:pull:`3928`) + +* `@bjlittle`_ introduced `nox`_ as a common and easy entry-point for test automation. + It can be used both from `cirrus-ci`_ in the cloud, and locally by the developer to + run the Iris tests, the doc-tests, the gallery doc-tests, and lint Iris + with `flake8`_ and `black`_. (:pull:`3928`) .. _Read the Docs: https://scitools-iris.readthedocs.io/en/latest/ .. _Matplotlib: https://matplotlib.org/ @@ -408,6 +466,7 @@ This document explains the changes made to Iris for this release .. _@rcomer: https://github.com/rcomer .. _@jvegasbsc: https://github.com/jvegasbsc .. _@zklaus: https://github.com/zklaus +.. _@znicholls: https://github.com/znicholls .. _ESMValTool: https://github.com/ESMValGroup/ESMValTool .. _v75: https://cfconventions.org/Data/cf-standard-names/75/build/cf-standard-name-table.html .. _sphinx-panels: https://sphinx-panels.readthedocs.io/en/latest/ @@ -417,3 +476,8 @@ This document explains the changes made to Iris for this release .. _PyKE: https://pypi.org/project/scitools-pyke/ .. _matplotlib.rcdefaults: https://matplotlib.org/3.1.1/api/matplotlib_configuration_api.html?highlight=rcdefaults#matplotlib.rcdefaults .. _@owena11: https://github.com/owena11 +.. _GitHub: https://github.com/SciTools/iris/issues/new/choose +.. _readthedocs: https://readthedocs.org/ +.. _CF Conventions and Metadata: https://cfconventions.org/ +.. _flake8: https://flake8.pycqa.org/en/stable/ +.. _nox: https://nox.thea.codes/en/stable/ diff --git a/lib/iris/fileformats/cf.py b/lib/iris/fileformats/cf.py index 5c6e11f3ac..47ff6291b0 100644 --- a/lib/iris/fileformats/cf.py +++ b/lib/iris/fileformats/cf.py @@ -9,7 +9,7 @@ References: -[CF] NetCDF Climate and Forecast (CF) Metadata conventions, Version 1.5, October, 2010. +[CF] NetCDF Climate and Forecast (CF) Metadata conventions. [NUG] NetCDF User's Guide, https://www.unidata.ucar.edu/software/netcdf/documentation/NUG/ """ diff --git a/lib/iris/fileformats/netcdf.py b/lib/iris/fileformats/netcdf.py index d0c3a3c534..98f712a970 100644 --- a/lib/iris/fileformats/netcdf.py +++ b/lib/iris/fileformats/netcdf.py @@ -8,8 +8,7 @@ See also: `netCDF4 python `_. -Also refer to document 'NetCDF Climate and Forecast (CF) Metadata Conventions', -Version 1.4, 27 February 2009. +Also refer to document 'NetCDF Climate and Forecast (CF) Metadata Conventions'. """ @@ -2490,7 +2489,7 @@ def save( """ Save cube(s) to a netCDF file, given the cube and the filename. - * Iris will write CF 1.5 compliant NetCDF files. + * Iris will write CF 1.7 compliant NetCDF files. * The attributes dictionaries on each cube in the saved cube list will be compared and common attributes saved as NetCDF global attributes where appropriate. diff --git a/lib/iris/quickplot.py b/lib/iris/quickplot.py index 42c0dba46a..350d61b537 100644 --- a/lib/iris/quickplot.py +++ b/lib/iris/quickplot.py @@ -49,7 +49,7 @@ def _title(cube_or_coord, with_units): if _use_symbol(units): units = units.symbol - if units.is_time_reference(): + elif units.is_time_reference(): # iris.plot uses matplotlib.dates.date2num, which is fixed to the below unit. if version.parse(_mpl_version) >= version.parse("3.3"): days_since = "1970-01-01" diff --git a/lib/iris/tests/idiff.py b/lib/iris/tests/idiff.py index e45d8a709e..84a966624f 100755 --- a/lib/iris/tests/idiff.py +++ b/lib/iris/tests/idiff.py @@ -220,7 +220,9 @@ def step_over_diffs(result_dir, action, display=True): count = len(results) for count_index, result_fname in enumerate(results): - key = os.path.splitext("-".join(result_fname.split("-")[1:]))[0] + key = os.path.splitext( + "-".join(result_fname.split("result-")[1:]) + )[0] try: # Calculate the test result perceptual image hash. phash = imagehash.phash( diff --git a/lib/iris/tests/results/analysis/sqrt.cml b/lib/iris/tests/results/analysis/sqrt.cml index 0dd0fe20b3..c6b9b88e9a 100644 --- a/lib/iris/tests/results/analysis/sqrt.cml +++ b/lib/iris/tests/results/analysis/sqrt.cml @@ -1,6 +1,6 @@ - + @@ -39,6 +39,6 @@ - + diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json index f9430ae9f5..a7fd9e1faf 100644 --- a/lib/iris/tests/results/imagerepo.json +++ b/lib/iris/tests/results/imagerepo.json @@ -908,6 +908,9 @@ "https://scitools.github.io/test-iris-imagehash/images/v4/bb433d4e94a4c6b9c15adaadc1fb6a469c8de43a3e07904e5f016b57984e1ea1.png", "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f3f81c07e3fa12da1c27e3f80.png" ], + "iris.tests.test_quickplot.TestLabels.test_pcolormesh_str_symbol.0": [ + "https://scitools.github.io/test-iris-imagehash/images/v4/eea16affc05ab500956e974ac53f3d80925ac03f3f80c07e3fa12da1c27f3f80.png" + ], "iris.tests.test_quickplot.TestQuickplotCoordinatesGiven.test_non_cube_coordinate.0": [ "https://scitools.github.io/test-iris-imagehash/images/v4/fa816a85857a955ae17e957ec57e7a81855fc17e3a81c57e1a813a85c57a1a05.png", "https://scitools.github.io/test-iris-imagehash/images/v4/fe816a85857a957ac07f957ac07f3e80956ac07f3e80c07f3e813e85c07e3f80.png" diff --git a/lib/iris/tests/test_basic_maths.py b/lib/iris/tests/test_basic_maths.py index a559ee0e8a..4b3cde95e4 100644 --- a/lib/iris/tests/test_basic_maths.py +++ b/lib/iris/tests/test_basic_maths.py @@ -537,11 +537,12 @@ def test_multiplication_not_in_place(self): class TestExponentiate(tests.IrisTest): def setUp(self): self.cube = iris.tests.stock.global_pp() - self.cube.data = self.cube.data - 260 + # Increase dtype from float32 to float64 in order + # to avoid dtype quantization errors during maths. + self.cube.data = self.cube.data.astype(np.float64) - 260.0 def test_exponentiate(self): a = self.cube - a.data = a.data.astype(np.float64) e = pow(a, 4) self.assertCMLApproxData(e, ("analysis", "exponentiate.cml")) @@ -553,8 +554,8 @@ def test_square_root(self): e = a ** 0.5 - self.assertCML(e, ("analysis", "sqrt.cml")) self.assertArrayEqual(e.data, a.data ** 0.5) + self.assertCML(e, ("analysis", "sqrt.cml")) self.assertRaises(ValueError, iris.analysis.maths.exponentiate, a, 0.3) def test_type_error(self): diff --git a/lib/iris/tests/test_coding_standards.py b/lib/iris/tests/test_coding_standards.py index 00ce7b7d44..79dff535eb 100644 --- a/lib/iris/tests/test_coding_standards.py +++ b/lib/iris/tests/test_coding_standards.py @@ -102,6 +102,7 @@ def last_change_by_fname(): def test_license_headers(self): exclude_patterns = ( "setup.py", + "noxfile.py", "build/*", "dist/*", "docs/iris/gallery_code/*/*.py", diff --git a/lib/iris/tests/test_netcdf.py b/lib/iris/tests/test_netcdf.py index 75266ff3fe..2d1b4a53d5 100644 --- a/lib/iris/tests/test_netcdf.py +++ b/lib/iris/tests/test_netcdf.py @@ -543,17 +543,20 @@ def test_noexist_directory(self): pass def test_bad_permissions(self): - # Non-exhaustive check that wrong permissions results in a suitable - # exception being raised. - dir_name = tempfile.mkdtemp() - fnme = os.path.join(dir_name, "tmp.nc") - try: - os.chmod(dir_name, stat.S_IREAD) - with self.assertRaises(IOError): - iris.fileformats.netcdf.Saver(fnme, "NETCDF4") - self.assertFalse(os.path.exists(fnme)) - finally: - os.rmdir(dir_name) + # Skip this test for the root user. This is applicable to + # running within a Docker container and/or CIaaS hosted testing. + if os.getuid(): + # Non-exhaustive check that wrong permissions results in a suitable + # exception being raised. + dir_name = tempfile.mkdtemp() + fname = os.path.join(dir_name, "tmp.nc") + try: + os.chmod(dir_name, stat.S_IREAD) + with self.assertRaises(PermissionError): + iris.fileformats.netcdf.Saver(fname, "NETCDF4") + self.assertFalse(os.path.exists(fname)) + finally: + shutil.rmtree(dir_name) @tests.skip_data diff --git a/lib/iris/tests/test_quickplot.py b/lib/iris/tests/test_quickplot.py index cf25324ea7..8abbf48a94 100644 --- a/lib/iris/tests/test_quickplot.py +++ b/lib/iris/tests/test_quickplot.py @@ -201,6 +201,13 @@ def test_pcolormesh(self): self.check_graphic() + def test_pcolormesh_str_symbol(self): + pcube = self._small().copy() + pcube.coords("level_height")[0].units = "centimeters" + qplt.pcolormesh(pcube) + + self.check_graphic() + def test_map(self): cube = self._slice(["grid_latitude", "grid_longitude"]) qplt.contour(cube) diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000..cd97e8ef8b --- /dev/null +++ b/noxfile.py @@ -0,0 +1,312 @@ +""" +Perform test automation with nox. + +For further details, see https://nox.thea.codes/en/stable/# + +""" + +import hashlib +import os +from pathlib import Path + +import nox + + +#: Default to reusing any pre-existing nox environments. +nox.options.reuse_existing_virtualenvs = True + +#: Name of the package to test. +PACKAGE = str("lib" / Path("iris")) + +#: Cirrus-CI environment variable hook. +PY_VER = os.environ.get("PY_VER", "3.7") + +#: Default cartopy cache directory. +CARTOPY_CACHE_DIR = os.environ.get("HOME") / Path(".local/share/cartopy") + + +def venv_cached(session): + """ + Determine whether the nox session environment has been cached. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + Returns + ------- + bool + Whether the session has been cached. + + """ + result = False + yml = Path(f"requirements/ci/py{PY_VER.replace('.', '')}.yml") + tmp_dir = Path(session.create_tmp()) + cache = tmp_dir / yml.name + if cache.is_file(): + with open(yml, "rb") as fi: + expected = hashlib.sha256(fi.read()).hexdigest() + with open(cache, "r") as fi: + actual = fi.read() + result = actual == expected + return result + + +def cache_venv(session): + """ + Cache the nox session environment. + + This consists of saving a hexdigest (sha256) of the associated + conda requirements YAML file. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + """ + yml = Path(f"requirements/ci/py{PY_VER.replace('.', '')}.yml") + with open(yml, "rb") as fi: + hexdigest = hashlib.sha256(fi.read()).hexdigest() + tmp_dir = Path(session.create_tmp()) + cache = tmp_dir / yml.name + with open(cache, "w") as fo: + fo.write(hexdigest) + + +def cache_cartopy(session): + """ + Determine whether to cache the cartopy natural earth shapefiles. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + """ + if not CARTOPY_CACHE_DIR.is_dir(): + session.run( + "python", + "-c", + "import cartopy; cartopy.io.shapereader.natural_earth()", + ) + + +@nox.session +def flake8(session): + """ + Perform flake8 linting of iris. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + """ + # Pip install the session requirements. + session.install("flake8") + # Execute the flake8 linter on the package. + session.run("flake8", PACKAGE) + # Execute the flake8 linter on this file. + session.run("flake8", __file__) + + +@nox.session +def black(session): + """ + Perform black format checking of iris. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + """ + # Pip install the session requirements. + session.install("black==20.8b1") + # Execute the black format checker on the package. + session.run("black", "--check", PACKAGE) + # Execute the black format checker on this file. + session.run("black", "--check", __file__) + + +@nox.session(python=[PY_VER], venv_backend="conda") +def tests(session): + """ + Perform iris system, integration and unit tests. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + Notes + ----- + See + - https://github.com/theacodes/nox/issues/346 + - https://github.com/theacodes/nox/issues/260 + + """ + if not venv_cached(session): + # Determine the conda requirements yaml file. + fname = f"requirements/ci/py{PY_VER.replace('.', '')}.yml" + # Back-door approach to force nox to use "conda env update". + command = ( + "conda", + "env", + "update", + f"--prefix={session.virtualenv.location}", + f"--file={fname}", + "--prune", + ) + session._run(*command, silent=True, external="error") + cache_venv(session) + + cache_cartopy(session) + session.run("python", "setup.py", "develop") + session.run( + "python", + "-m", + "iris.tests.runner", + "--default-tests", + "--system-tests", + ) + + +@nox.session(python=[PY_VER], venv_backend="conda") +def gallery(session): + """ + Perform iris gallery doc-tests. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + Notes + ----- + See + - https://github.com/theacodes/nox/issues/346 + - https://github.com/theacodes/nox/issues/260 + + """ + if not venv_cached(session): + # Determine the conda requirements yaml file. + fname = f"requirements/ci/py{PY_VER.replace('.', '')}.yml" + # Back-door approach to force nox to use "conda env update". + command = ( + "conda", + "env", + "update", + f"--prefix={session.virtualenv.location}", + f"--file={fname}", + "--prune", + ) + session._run(*command, silent=True, external="error") + cache_venv(session) + + cache_cartopy(session) + session.run("python", "setup.py", "develop") + session.run( + "python", + "-m", + "iris.tests.runner", + "--gallery-tests", + ) + + +@nox.session(python=[PY_VER], venv_backend="conda") +def doctest(session): + """ + Perform iris doc-tests. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + Notes + ----- + See + - https://github.com/theacodes/nox/issues/346 + - https://github.com/theacodes/nox/issues/260 + + """ + if not venv_cached(session): + # Determine the conda requirements yaml file. + fname = f"requirements/ci/py{PY_VER.replace('.', '')}.yml" + # Back-door approach to force nox to use "conda env update". + command = ( + "conda", + "env", + "update", + f"--prefix={session.virtualenv.location}", + f"--file={fname}", + "--prune", + ) + session._run(*command, silent=True, external="error") + cache_venv(session) + + cache_cartopy(session) + session.run("python", "setup.py", "develop") + session.cd("docs/iris") + session.run( + "make", + "clean", + "html", + external=True, + ) + session.run( + "make", + "doctest", + external=True, + ) + + +@nox.session(python=[PY_VER], venv_backend="conda") +def linkcheck(session): + """ + Perform iris doc link check. + + Parameters + ---------- + session: object + A `nox.sessions.Session` object. + + Notes + ----- + See + - https://github.com/theacodes/nox/issues/346 + - https://github.com/theacodes/nox/issues/260 + + """ + if not venv_cached(session): + # Determine the conda requirements yaml file. + fname = f"requirements/ci/py{PY_VER.replace('.', '')}.yml" + # Back-door approach to force nox to use "conda env update". + command = ( + "conda", + "env", + "update", + f"--prefix={session.virtualenv.location}", + f"--file={fname}", + "--prune", + ) + session._run(*command, silent=True, external="error") + cache_venv(session) + + cache_cartopy(session) + session.run("python", "setup.py", "develop") + session.cd("docs/iris") + session.run( + "make", + "clean", + "html", + external=True, + ) + session.run( + "make", + "linkcheck", + external=True, + ) diff --git a/requirements/ci/py36.yml b/requirements/ci/py36.yml index 0461cdf880..2b40fbad4e 100644 --- a/requirements/ci/py36.yml +++ b/requirements/ci/py36.yml @@ -35,6 +35,7 @@ dependencies: - asv - black=20.8b1 - filelock + - flake8 - imagehash>=4.0 - nose - pillow<7 diff --git a/requirements/ci/py37.yml b/requirements/ci/py37.yml index 8817f575b7..0f01f0ef75 100644 --- a/requirements/ci/py37.yml +++ b/requirements/ci/py37.yml @@ -35,6 +35,7 @@ dependencies: - asv - black=20.8b1 - filelock + - flake8 - imagehash>=4.0 - nose - pillow<7 diff --git a/requirements/setup.txt b/requirements/setup.txt index 2e14da4905..9232946a6a 100644 --- a/requirements/setup.txt +++ b/requirements/setup.txt @@ -1,6 +1,4 @@ # Dependencies necessary to run setup.py of iris # ---------------------------------------------- -scitools-pyke setuptools>=40.8.0 -wheel diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a87902cbfd..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,44 +0,0 @@ -[flake8] -ignore = E402,\ # Due to conditional imports - E226 # Due to whitespace around operators (e.g. 2*x + 3) -exclude = */iris/std_names.py,\ - */iris/fileformats/cf.py,\ - */iris/fileformats/dot.py,\ - */iris/fileformats/pp_load_rules.py,\ - */iris/fileformats/rules.py,\ - */iris/fileformats/um_cf_map.py,\ - */iris/fileformats/_pyke_rules/compiled_krb/*,\ - */iris/io/__init__.py,\ - */iris/io/format_picker.py,\ - */iris/tests/__init__.py,\ - */iris/tests/pp.py,\ - */iris/tests/system_test.py,\ - */iris/tests/test_analysis.py,\ - */iris/tests/test_analysis_calculus.py,\ - */iris/tests/test_basic_maths.py,\ - */iris/tests/test_cartography.py,\ - */iris/tests/test_cdm.py,\ - */iris/tests/test_cell.py,\ - */iris/tests/test_cf.py,\ - */iris/tests/test_constraints.py,\ - */iris/tests/test_coord_api.py,\ - */iris/tests/test_coord_categorisation.py,\ - */iris/tests/test_coordsystem.py,\ - */iris/tests/test_cube_to_pp.py,\ - */iris/tests/test_file_load.py,\ - */iris/tests/test_file_save.py,\ - */iris/tests/test_hybrid.py,\ - */iris/tests/test_intersect.py,\ - */iris/tests/test_io_init.py,\ - */iris/tests/test_iterate.py,\ - */iris/tests/test_load.py,\ - */iris/tests/test_merge.py,\ - */iris/tests/test_pp_cf.py,\ - */iris/tests/test_pp_module.py,\ - */iris/tests/test_pp_stash.py,\ - */iris/tests/test_pp_to_cube.py,\ - */iris/tests/test_quickplot.py,\ - */iris/tests/test_std_names.py,\ - */iris/tests/test_uri_callback.py,\ - */iris/tests/test_util.py -