diff --git a/.cirrus.yml b/.cirrus.yml index da425a5691..0a7c972821 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,5 +1,6 @@ # Reference: # - https://cirrus-ci.org/guide/writing-tasks/ +# - https://cirrus-ci.org/guide/writing-tasks/#environment-variables # - https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks # - https://cirrus-ci.org/guide/linux/ # - https://cirrus-ci.org/guide/macOS/ @@ -17,6 +18,16 @@ container: env: + # Skip specific tasks by name. Set to a non-empty string to skip. + SKIP_LINT_TASK: "" + SKIP_TEST_MINIMAL_TASK: "" + SKIP_TEST_FULL_TASK: "" + SKIP_GALLERY_TASK: "" + SKIP_DOCTEST_TASK: "" + SKIP_LINKCHECK_TASK: "" + # Skip task groups by type. Set to a non-empty string to skip. + SKIP_ALL_TEST_TASKS: "" + SKIP_ALL_DOC_TASKS: "" # Maximum cache period (in weeks) before forcing a new cache upload. CACHE_PERIOD: "2" # Increment the build number to force new cartopy cache upload. @@ -35,25 +46,6 @@ env: 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. # @@ -68,7 +60,7 @@ linux_task_template: &LINUX_TASK_TEMPLATE 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}" + - echo "$(date +%Y).$(expr $(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 @@ -80,19 +72,49 @@ linux_task_template: &LINUX_TASK_TEMPLATE folder: ${HOME}/.local/share/cartopy fingerprint_script: - echo "${CIRRUS_OS}" - - echo "$(date +%Y).$(($(date +%U) / ${CACHE_PERIOD})):${CARTOPY_CACHE_BUILD}" + - echo "$(date +%Y).$(expr $(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}" + - echo "$(date +%Y).$(expr $(date +%U) / ${CACHE_PERIOD}):${NOX_CACHE_BUILD}" - sha256sum ${CIRRUS_WORKING_DIR}/requirements/ci/py$(echo ${PY_VER} | tr -d ".").yml +# +# YAML alias for compute credits +# +compute_credits_template: &CREDITS_TEMPLATE + # Only use credits for non-DRAFT pull-requests to SciTools/iris master branch by collaborators + use_compute_credits: $CIRRUS_REPO_FULL_NAME == 'SciTools/iris' && $CIRRUS_USER_COLLABORATOR == 'true' && $CIRRUS_PR_DRAFT == 'false' && $CIRRUS_BASE_BRANCH == 'master' && $CIRRUS_PR != '' + +# +# Linting +# +lint_task: + only_if: $SKIP_LINT_TASK == "" + << : *CREDITS_TEMPLATE + auto_cancellation: true + name: "${CIRRUS_OS}: flake8 and black" + pip_cache: + folder: ~/.cache/pip + fingerprint_script: + - echo "${CIRRUS_TASK_NAME}" + - echo "$(date +%Y).$(expr $(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 + + # # Testing Minimal (Linux) # -linux_minimal_task: +test_minimal_task: + only_if: $SKIP_TEST_MINIMAL_TASK == "" && $SKIP_ALL_TEST_TASKS == "" + << : *CREDITS_TEMPLATE matrix: env: PY_VER: 3.6 @@ -115,7 +137,9 @@ linux_minimal_task: # # Testing Full (Linux) # -linux_task: +test_full_task: + only_if: $SKIP_TEST_FULL_TASK == "" && $SKIP_ALL_TEST_TASKS == "" + << : *CREDITS_TEMPLATE matrix: env: PY_VER: 3.6 @@ -148,6 +172,8 @@ linux_task: # Testing Documentation Gallery (Linux) # gallery_task: + only_if: $SKIP_GALLERY_TASK == "" && $SKIP_ALL_DOC_TASKS == "" + << : *CREDITS_TEMPLATE matrix: env: PY_VER: 3.8 @@ -176,6 +202,8 @@ gallery_task: # Testing Documentation (Linux) # doctest_task: + only_if: $SKIP_DOCTEST_TASK == "" && $SKIP_ALL_DOC_TASKS == "" + << : *CREDITS_TEMPLATE matrix: env: PY_VER: 3.8 @@ -209,7 +237,9 @@ doctest_task: # # Testing Documentation Link Check (Linux) # -link_task: +linkcheck_task: + only_if: $SKIP_LINKCHECK_TASK == "" && $SKIP_ALL_DOC_TASKS == "" + << : *CREDITS_TEMPLATE matrix: env: PY_VER: 3.8 diff --git a/docs/src/_static/theme_override.css b/docs/src/_static/theme_override.css index 5edc286630..c56b720f69 100644 --- a/docs/src/_static/theme_override.css +++ b/docs/src/_static/theme_override.css @@ -26,3 +26,17 @@ table.docutils td { word-wrap: break-word; } +/* Used for very strong warning */ +#slim-red-box-message { + background: #ff0000; + box-sizing: border-box; + color: #ffffff; + font-weight: normal; + padding: 0.5em; +} + +#slim-red-box-message a { + color: #ffffff; + font-weight: normal; + text-decoration:underline; +} diff --git a/docs/src/_templates/layout.html b/docs/src/_templates/layout.html index 9b4983697e..96a2e0913e 100644 --- a/docs/src/_templates/layout.html +++ b/docs/src/_templates/layout.html @@ -1,5 +1,27 @@ {% extends "!layout.html" %} +{# This uses blocks. See: + https://www.sphinx-doc.org/en/master/templating.html +#} + +/*---------------------------------------------------------------------------*/ + +{%- block document %} + {% if READTHEDOCS and rtd_version == 'latest' %} +
+ You are viewing the latest unreleased documentation + v{{ version }}. You may prefer a + stable + version. +
+

+ {%- endif %} + + {{ super() }} +{%- endblock %} + +/*-----------------------------------------------------z----------------------*/ + {% block menu %} {{ super() }} diff --git a/docs/src/common_links.inc b/docs/src/common_links.inc index 3c465b67dc..d9df15be8b 100644 --- a/docs/src/common_links.inc +++ b/docs/src/common_links.inc @@ -3,6 +3,7 @@ .. _black: https://black.readthedocs.io/en/stable/ .. _.cirrus.yml: https://github.com/SciTools/iris/blob/master/.cirrus.yml +.. _flake8: https://flake8.pycqa.org/en/stable/ .. _.flake8.yml: https://github.com/SciTools/iris/blob/master/.flake8 .. _cirrus-ci: https://cirrus-ci.com/github/SciTools/iris .. _conda: https://docs.conda.io/en/latest/ @@ -24,6 +25,7 @@ .. _New Issue: https://github.com/scitools/iris/issues/new/choose .. _pull request: https://github.com/SciTools/iris/pulls .. _pull requests: https://github.com/SciTools/iris/pulls +.. _Read the Docs: https://scitools-iris.readthedocs.io/en/latest/ .. _readthedocs.yml: https://github.com/SciTools/iris/blob/master/requirements/ci/readthedocs.yml .. _SciTools: https://github.com/SciTools .. _sphinx: https://www.sphinx-doc.org/en/master/ diff --git a/docs/src/conf.py b/docs/src/conf.py index 9bab5850b8..ab05312fca 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -43,6 +43,9 @@ def autolog(message): for item, value in os.environ.items(): autolog("[READTHEDOCS] {} = {}".format(item, value)) +# This is the rtd reference to the version, such as: latest, stable, v3.0.1 etc +# For local testing purposes this could be explicitly set latest or stable. +rtd_version = os.environ.get("READTHEDOCS_VERSION") # -- Path setup -------------------------------------------------------------- @@ -131,7 +134,6 @@ def autolog(message): "custom_data_autodoc", "generate_package_rst", ] - # -- panels extension --------------------------------------------------------- # See https://sphinx-panels.readthedocs.io/en/latest/ @@ -165,7 +167,7 @@ def autolog(message): # See https://sphinx-copybutton.readthedocs.io/en/latest/ copybutton_prompt_text = ">>> " -# sphinx.ext.todo configuration +# sphinx.ext.todo configuration ----------------------------------------------- # See https://www.sphinx-doc.org/en/master/usage/extensions/todo.html todo_include_todos = True @@ -228,6 +230,8 @@ def autolog(message): } html_context = { + "rtd_version": rtd_version, + "version": version, "copyright_years": copyright_years, "python_version": build_python_version, # menu_links and menu_links_name are used in _templates/layout.html @@ -296,7 +300,6 @@ def autolog(message): "ignore_pattern": r"__init__\.py", } - # ----------------------------------------------------------------------------- # Remove matplotlib agg warnings from generated doc when using plt.show warnings.filterwarnings( @@ -306,7 +309,6 @@ def autolog(message): " non-GUI backend, so cannot show the figure.", ) - # -- numfig options (built-in) ------------------------------------------------ # Enable numfig. numfig = True diff --git a/docs/src/developers_guide/contributing_ci_tests.rst b/docs/src/developers_guide/contributing_ci_tests.rst index a6bdac4ae0..8594612fe1 100644 --- a/docs/src/developers_guide/contributing_ci_tests.rst +++ b/docs/src/developers_guide/contributing_ci_tests.rst @@ -5,9 +5,9 @@ Continuous Integration (CI) Testing =================================== -The `Iris`_ GitHub repository is configured to run checks on the code -automatically when a pull request is created, updated or merged against -Iris **master**. The checks performed are: +The `Iris`_ GitHub repository is configured to run checks against all its +branches automatically whenever a pull request is created, updated or merged. +The checks performed are: * :ref:`testing_cla` * :ref:`testing_cirrus` @@ -18,9 +18,9 @@ Iris **master**. The checks performed are: SciTools CLA Checker ******************** -A bot that checks the user who created the pull request has signed the -**Contributor's License Agreement (CLA)**. For more information on this this -please see https://scitools.org.uk/organisation.html#governance +A bot which checks that the GitHub author of the pull request has signed the +**SciTools Contributor's License Agreement (CLA)**. For more information on +this please see https://scitools.org.uk/organisation.html#governance. .. _testing_cirrus: @@ -28,19 +28,55 @@ please see https://scitools.org.uk/organisation.html#governance Cirrus-CI ********* -The unit and integration tests in Iris are an essential mechanism to ensure +Iris unit and integration tests are an essential mechanism to ensure that the Iris code base is working as expected. :ref:`developer_running_tests` -may be run manually but to ensure the checks are performed a -continuous integration testing tool named `cirrus-ci`_ is used. +may be performed manually by a developer locally. However Iris is configured to +use the `cirrus-ci`_ service for automated Continuous Integration (CI) testing. -A `cirrus-ci`_ configuration file named `.cirrus.yml`_ -is in the Iris repository which tells Cirrus-CI what commands to run. The -commands include retrieving the Iris code base and associated test files using -conda and then running the tests. `cirrus-ci`_ allows for a matrix of tests to -be performed to ensure that all expected variations test successfully. +The `cirrus-ci`_ configuration file `.cirrus.yml`_ in the root of the Iris repository +defines the tasks to be performed by `cirrus-ci`_. For further details +refer to the `Cirrus-CI Documentation`_. The tasks performed during CI include: + +* linting the code base and ensuring it adheres to the `black`_ format +* running the system, integration and unit tests for Iris +* ensuring the documentation gallery builds successfully +* performing all doc-tests within the code base +* checking all URL references within the code base and documentation are valid + +The above `cirrus-ci`_ tasks are run automatically against all `Iris`_ branches +on GitHub whenever a pull request is submitted, updated or merged. See the +`Cirrus-CI Dashboard`_ for details of recent past and active Iris jobs. + +.. _skipping Cirrus-CI tasks: + +Skipping Cirrus-CI Tasks +------------------------ + +As a developer you may wish to not run all the CI tasks when you are actively +developing e.g., you are writing documentation and there is no need for linting, +or long running compute intensive testing tasks to be executed. + +As a convenience, it is possible to easily skip one or more tasks by setting +the appropriate environment variable within the `.cirrus.yml`_ file to a +**non-empty** string: + +* ``SKIP_LINT_TASK`` to skip `flake8`_ linting and `black`_ formatting +* ``SKIP_TEST_MINIMAL_TASK`` to skip restricted unit and integration testing +* ``SKIP_TEST_FULL_TASK`` to skip full unit and integration testing +* ``SKIP_GALLERY_TASK`` to skip building the documentation gallery +* ``SKIP_DOCTEST_TASK`` to skip running the documentation doc-tests +* ``SKIP_LINKCHECK_TASK`` to skip checking for broken documentation URL references +* ``SKIP_ALL_TEST_TASKS`` which is equivalent to setting ``SKIP_TEST_MINIMAL_TASK`` and ``SKIP_TEST_FULL_TASK`` +* ``SKIP_ALL_DOC_TASKS`` which is equivalent to setting ``SKIP_GALLERY_TASK``, ``SKIP_DOCTEST_TASK``, and ``SKIP_LINKCHECK_TASK`` + +e.g., to skip the linting task, the following are all equivalent:: + + SKIP_LINT_TASK: "1" + SKIP_LINT_TASK: "true" + SKIP_LINT_TASK: "false" + SKIP_LINT_TASK: "skip" + SKIP_LINT_TASK: "unicorn" -The `cirrus-ci`_ tests are run automatically against the `Iris`_ master -repository when a pull request is submitted, updated or merged. GitHub Checklist **************** @@ -50,6 +86,10 @@ passing: .. image:: ci_checks.png -If any CI checks fail, then the pull request is unlikely to be merged to the +If any CI tasks fail, then the pull request is unlikely to be merged to the Iris target branch by a core developer. + +.. _Cirrus-CI Dashboard: https://cirrus-ci.com/github/SciTools/iris +.. _Cirrus-CI Documentation: https://cirrus-ci.org/guide/writing-tasks/ + diff --git a/docs/src/developers_guide/contributing_code_formatting.rst b/docs/src/developers_guide/contributing_code_formatting.rst index 6bf8dca717..1a3573d135 100644 --- a/docs/src/developers_guide/contributing_code_formatting.rst +++ b/docs/src/developers_guide/contributing_code_formatting.rst @@ -58,6 +58,4 @@ will look similar to:: their officially documentation for more information. -.. _black: https://black.readthedocs.io/en/stable/ -.. _flake8: https://flake8.pycqa.org/en/stable/ .. _pre-commit: https://pre-commit.com/ diff --git a/docs/src/developers_guide/release.rst b/docs/src/developers_guide/release.rst index 56328f910f..90938b32d3 100644 --- a/docs/src/developers_guide/release.rst +++ b/docs/src/developers_guide/release.rst @@ -3,8 +3,7 @@ Releases ======== -A release of Iris is a `tag on the SciTools/Iris`_ -Github repository. +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. @@ -24,8 +23,8 @@ number of releases, is in :ref:`iris_development_deprecations`. 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: +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` @@ -41,10 +40,10 @@ Release Candidate ----------------- Prior to a release, a release candidate tag may be created, marked as a -pre-release in github, with a tag ending with :literal:`rc` followed by a -number, e.g.: +pre-release in GitHub, with a tag ending with :literal:`rc` followed by a +number (0-based), e.g.,: - :literal:`v1.9.0rc1` + :literal:`v1.9.0rc0` 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 @@ -57,11 +56,16 @@ point release. If new features are required for a release after a release candidate has been cut, a new pre-release shall be issued first. +Make the release candidate available as a conda package on the +`conda-forge Anaconda channel`_ using the `rc_iris`_ label. To do this visit +the `conda-forge iris-feedstock`_ and follow `CFEP-05`_. For further information +see the `conda-forge User Documentation`_. + Documentation ------------- -The documentation should include all of the what's new entries for the release. +The documentation should include all of the ``whatsnew`` entries for the release. This content should be reviewed and adapted as required. Steps to achieve this can be found in the :ref:`iris_development_releases_steps`. @@ -70,50 +74,48 @@ 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 -:literal:`Iris.__init__.py` and include the release date in the relevant what's -new page within the documentation. +The final steps of the release are to change the version string ``__version__`` +in the source of :literal:`iris.__init__.py` and ensure the release date and details +are correct in the relevant ``whatsnew`` page within the documentation. Once all checks are complete, the release is cut by the creation of a new tag -in the SciTools Iris repository. +in the ``SciTools/iris`` repository. 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`_. +Once a release is cut on GitHub, update the Iris conda recipe on the +`conda-forge iris-feedstock`_ for the release. This will build and publish the +conda package on the `conda-forge Anaconda channel`_. -.. _Iris feedstock: https://github.com/conda-forge/iris-feedstock/tree/master/recipe -.. _conda forge: https://anaconda.org/conda-forge/iris Merge Back ---------- -After the release is cut, the changes shall be merged back onto the -Scitools/iris master branch. +After the release is cut, the changes from the release branch should be merged +back onto the ``SciTools/iris`` ``master`` branch. -To achieve this, first cut a local branch from the release branch, -:literal:`{release}.x`. Next add a commit changing the release string to match -the release string on scitools/master. This branch can now be proposed as a -pull request to master. This work flow ensures that the commit identifiers are -consistent between the :literal:`.x` branch and :literal:`master`. +To achieve this, first cut a local branch from the latest ``master`` branch, +and `git merge` the :literal:`.x` release branch into it. Ensure that the +``iris.__version__``, ``docs/src/whatsnew/index.rst`` and ``docs/src/whatsnew/latest.rst`` +are correct, before committing these changes and then proposing a pull-request +on the ``master`` branch of ``SciTools/iris``. Point Releases -------------- -Bug fixes may be implemented and targeted as the :literal:`.x` branch. These -should lead to a new point release, another tag. For example, a fix for a -problem with 1.9.0 will be merged into 1.9.x, and then released by tagging -1.9.1. +Bug fixes may be implemented and targeted on the :literal:`.x` release branch. +These should lead to a new point release, and another tag. For example, a fix +for a problem with the ``v1.9.0`` release will be merged into ``v1.9.x`` release +branch, and then released by tagging ``v1.9.1``. 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`. +``master``. .. _iris_development_releases_steps: @@ -121,19 +123,19 @@ release process is to be followed, including the merge back of changes into Maintainer Steps ---------------- -These steps assume a release for ``v1.9`` is to be created. +These steps assume a release for ``1.9.0`` is to be created. Release Steps ~~~~~~~~~~~~~ -#. Create the release feature branch ``1.9.x`` on `SciTools/iris`_. +#. Create the release feature branch ``v1.9.x`` on `SciTools/iris`_. The only exception is for a point/bugfix release, as it should already exist #. Update the ``iris.__init__.py`` version string e.g., to ``1.9.0`` -#. Update the what's new for the release: +#. Update the ``whatsnew`` for the release: - * Use git to rename ``docs/src/whatsnew/latest.rst`` to the release + * Use ``git`` to rename ``docs/src/whatsnew/latest.rst`` to the release version file ``v1.9.rst`` - * Use git to delete the ``docs/src/whatsnew/latest.rst.template`` file + * Use ``git`` to delete the ``docs/src/whatsnew/latest.rst.template`` file * In ``v1.9.rst`` remove the ``[unreleased]`` caption from the page title. Note that, the Iris version and release date are updated automatically when the documentation is built @@ -141,10 +143,10 @@ Release Steps * Work with the development team to populate the ``Release Highlights`` dropdown at the top of the file, which provides extra detail on notable changes - * Use git to add and commit all changes, including removal of + * Use ``git`` to add and commit all changes, including removal of ``latest.rst.template`` -#. Update the what's new index ``docs/src/whatsnew/index.rst`` +#. Update the ``whatsnew`` index ``docs/src/whatsnew/index.rst`` * Remove the reference to ``latest.rst`` * Add a reference to ``v1.9.rst`` to the top of the list @@ -159,20 +161,31 @@ Post Release Steps ~~~~~~~~~~~~~~~~~~ #. Check the documentation has built on `Read The Docs`_. The build is - triggered by any commit to master. Additionally check that the versions + 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 + versions available in the **admin** dashboard in `Read The Docs`_. +#. Review the `Active Versions`_ for the ``scitools-iris`` project on + `Read The Docs`_ to ensure that the appropriate versions are ``Active`` + and/or ``Hidden``. To do this ``Edit`` the appropriate version e.g., + see `Editing v3.0.0rc0`_. #. Copy ``docs/src/whatsnew/latest.rst.template`` to ``docs/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 + the file with the ``unreleased`` heading and placeholders for the + ``whatsnew`` headings +#. Add back in the reference to ``latest.rst`` to the ``whatsnew`` index ``docs/src/whatsnew/index.rst`` #. Update ``iris.__init__.py`` version string to show as ``1.10.dev0`` -#. Merge back to master +#. Merge back to ``master`` .. _Read The Docs: https://readthedocs.org/projects/scitools-iris/builds/ .. _SciTools/iris: https://github.com/SciTools/iris .. _tag on the SciTools/Iris: https://github.com/SciTools/iris/releases +.. _conda-forge Anaconda channel: https://anaconda.org/conda-forge/iris +.. _conda-forge iris-feedstock: https://github.com/conda-forge/iris-feedstock +.. _CFEP-05: https://github.com/conda-forge/cfep/blob/master/cfep-05.md +.. _conda-forge User Documentation: https://conda-forge.org/docs/user/00_intro.html +.. _Active Versions: https://readthedocs.org/projects/scitools-iris/versions/ +.. _Editing v3.0.0rc0: https://readthedocs.org/dashboard/scitools-iris/version/v3.0.0rc0/ +.. _rc_iris: https://anaconda.org/conda-forge/iris/labels diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 68872beb64..6fcdcfb7bc 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -27,6 +27,11 @@ This document explains the changes made to Iris for this release #. Congratulations to `@jamesp`_ who recently became an Iris core developer after joining the Iris development team at the `Met Office`_. 🎉 +#. A special thanks goes to `@akuhnregnier`_, `@gcaria`_, `@jamesp`_ and + `@MHBalsmeier`_ all of whom made their first contributions to Iris, which + were gratefully received and included in this release. Keep up the awesome + work! 🍻 + ✨ Features =========== @@ -37,6 +42,12 @@ This document explains the changes made to Iris for this release ``iris.plot.plot(z_cube)`` will produce a z-vs-phenomenon plot, where before it would have produced a phenomenon-vs-z plot. (:pull:`3906`) +#. `@bjlittle`_ introduced :func:`iris.common.metadata.hexdigest` to the + public API. Previously it was a private function introduced in ``v3.0.0``. + Given any object, :func:`~iris.common.metadata.hexdigest` returns a string + representation of the 64-bit non-cryptographic hash of the object using the + extremely fast `xxhash`_ hashing algorithm. (:pull:`4020`) + 🐛 Bugs Fixed ============= @@ -91,6 +102,10 @@ This document explains the changes made to Iris for this release #. `@bjlittle`_ added the |PyPI|_ badge to the `README.md`_. (:pull:`4004`) +#. `@tkknight`_ added a banner at the top of every page of the unreleased + development documentation if being viewed on `Read the Docs`_. + (:pull:`3999`) + 💼 Internal =========== @@ -109,11 +124,22 @@ This document explains the changes made to Iris for this release each ``nox`` session to list its ``conda`` environment packages and environment info. (:pull:`3990`) +#. `@bjlittle`_ enabled `cirrus-ci`_ compute credits for non-draft pull-requests + from collaborators targeting the Iris ``master`` branch. (:pull:`4007`) + +#. `@akuhnregnier`_ replaced `deprecated numpy 1.20 aliases for builtin types`_. + (:pull:`3997`) + +#. `@bjlittle`_ added conditional task execution to `.cirrus.yml`_ to allow + developers to easily disable `cirrus-ci`_ tasks. See + :ref:`skipping Cirrus-CI tasks`. (:pull:`4019`) + .. comment Whatsnew author names (@github name) in alphabetical order. Note that, core dev names are automatically included by the common_links.inc: +.. _@akuhnregnier: https://github.com/akuhnregnier .. _@gcaria: https://github.com/gcaria .. _@MHBalsmeier: https://github.com/MHBalsmeier @@ -122,6 +148,7 @@ This document explains the changes made to Iris for this release Whatsnew resources in alphabetical order: .. _abstract base class: https://docs.python.org/3/library/abc.html +.. _deprecated numpy 1.20 aliases for builtin types: https://numpy.org/doc/1.20/release/1.20.0-notes.html#using-the-aliases-of-builtin-types-like-np-int-is-deprecated .. _GitHub: https://github.com/SciTools/iris/issues/new/choose .. _Met Office: https://www.metoffice.gov.uk/ .. _numpy: https://numpy.org/doc/stable/release/1.20.0-notes.html @@ -129,3 +156,4 @@ This document explains the changes made to Iris for this release .. _PyPI: https://pypi.org/project/scitools-iris/ .. _Python 3.8: https://www.python.org/downloads/release/python-380/ .. _README.md: https://github.com/SciTools/iris#----- +.. _xxhash: http://cyan4973.github.io/xxHash/ diff --git a/lib/iris/_constraints.py b/lib/iris/_constraints.py index 0f6a8ab6c6..5179d89039 100644 --- a/lib/iris/_constraints.py +++ b/lib/iris/_constraints.py @@ -294,7 +294,7 @@ def call_func(c): except TypeError: try_quick = False if try_quick: - r = np.zeros(coord.shape, dtype=np.bool) + r = np.zeros(coord.shape, dtype=np.bool_) if coord.cell(i) == self._coord_thing: r[i] = True else: diff --git a/lib/iris/_representation.py b/lib/iris/_representation.py index ee1e1a0d55..63974d1e50 100644 --- a/lib/iris/_representation.py +++ b/lib/iris/_representation.py @@ -9,7 +9,7 @@ import re import iris.util -from iris.common.metadata import _hexdigest as quickhash +from iris.common.metadata import hexdigest class DimensionHeader: @@ -101,7 +101,7 @@ def _summary_coord_extra(self, cube, coord): # ..except setdefault fails if values are numpy arrays. if key not in attributes: attributes[key] = value - elif quickhash(attributes[key]) != quickhash(value): + elif hexdigest(attributes[key]) != hexdigest(value): # NOTE: fast and array-safe comparison, as used in # :mod:`iris.common.metadata`. vary.add(key) diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py index a049d060c2..cf9f258be1 100644 --- a/lib/iris/analysis/__init__.py +++ b/lib/iris/analysis/__init__.py @@ -1454,7 +1454,7 @@ def interp_order(length): slices[-1] = endslice slices = tuple(slices) # Numpy>=1.16 : index with tuple, *not* list. - if isinstance(array.dtype, np.float): + if isinstance(array.dtype, np.float64): data = array[slices] else: # Cast non-float data type. diff --git a/lib/iris/analysis/_regrid.py b/lib/iris/analysis/_regrid.py index 7063fdef43..b0341fafaa 100644 --- a/lib/iris/analysis/_regrid.py +++ b/lib/iris/analysis/_regrid.py @@ -696,7 +696,7 @@ def _regrid( if ma.isMaskedArray(src_data): data = ma.empty(shape, dtype=dtype) - data.mask = np.zeros(data.shape, dtype=np.bool) + data.mask = np.zeros(data.shape, dtype=np.bool_) else: data = np.empty(shape, dtype=dtype) diff --git a/lib/iris/common/metadata.py b/lib/iris/common/metadata.py index 3f8d7e6bf0..174f115187 100644 --- a/lib/iris/common/metadata.py +++ b/lib/iris/common/metadata.py @@ -37,6 +37,7 @@ "CoordMetadata", "CubeMetadata", "DimCoordMetadata", + "hexdigest", "metadata_manager_factory", ] @@ -48,34 +49,46 @@ logger = get_logger(__name__, fmt="[%(cls)s.%(funcName)s]") -def _hexdigest(value): +def hexdigest(item): """ - Return a hexidecimal string hash representation of the provided value. + Calculate a hexidecimal string hash representation of the provided item. - Calculates a 64-bit non-cryptographic hash of the provided value, - and returns the hexdigest string representation of the calculated hash. + Calculates a 64-bit non-cryptographic hash of the provided item, using + the extremely fast ``xxhash`` hashing algorithm, and returns the hexdigest + string representation of the hash. + + This provides a means to compare large and/or complex objects through + simple string hexdigest comparison. + + Args: + + * item (object): + The item that requires to have its hexdigest calculated. + + Returns: + The string hexidecimal representation of the item's 64-bit hash. """ # Special case: deal with numpy arrays. - if ma.isMaskedArray(value): + if ma.isMaskedArray(item): parts = ( - value.shape, - xxh64_hexdigest(value.data), - xxh64_hexdigest(value.mask), + item.shape, + xxh64_hexdigest(item.data), + xxh64_hexdigest(item.mask), ) - value = str(parts) - elif isinstance(value, np.ndarray): - parts = (value.shape, xxh64_hexdigest(value)) - value = str(parts) + item = str(parts) + elif isinstance(item, np.ndarray): + parts = (item.shape, xxh64_hexdigest(item)) + item = str(parts) try: # Calculate single-shot hash to avoid allocating state on the heap - result = xxh64_hexdigest(value) + result = xxh64_hexdigest(item) except TypeError: # xxhash expects a bytes-like object, so try hashing the - # string representation of the provided value instead, but + # string representation of the provided item instead, but # also fold in the object type... - parts = (type(value), value) + parts = (type(item), item) result = xxh64_hexdigest(str(parts)) return result @@ -339,8 +352,8 @@ def _combine_lenient_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} # Intersection of common items. common = sleft & sright # Items in sleft different from sright. @@ -368,8 +381,8 @@ def _combine_strict_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} # Intersection of common items. common = sleft & sright # Now bring the result together. @@ -427,8 +440,8 @@ def _compare_lenient_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} # Items in sleft different from sright. dsleft = dict(sleft - sright) # Items in sright different from sleft. @@ -444,8 +457,8 @@ def _compare_strict_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} return sleft == sright @@ -513,8 +526,8 @@ def _difference_lenient_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} # Items in sleft different from sright. dsleft = dict(sleft - sright) # Items in sright different from sleft. @@ -541,8 +554,8 @@ def _difference_strict_attributes(left, right): # Use xxhash to perform an extremely fast non-cryptographic hash of # each dictionary key rvalue, thus ensuring that the dictionary is # completely hashable, as required by a set. - sleft = {(k, _hexdigest(v)) for k, v in left.items()} - sright = {(k, _hexdigest(v)) for k, v in right.items()} + sleft = {(k, hexdigest(v)) for k, v in left.items()} + sright = {(k, hexdigest(v)) for k, v in right.items()} # Items in sleft different from sright. dsleft = dict(sleft - sright) # Items in sright different from sleft. diff --git a/lib/iris/cube.py b/lib/iris/cube.py index 5578507d28..a15951900b 100644 --- a/lib/iris/cube.py +++ b/lib/iris/cube.py @@ -2652,9 +2652,6 @@ def vector_summary( def __str__(self): return self.summary() - def __unicode__(self): - return self.summary() - def __repr__(self): return "" % self.summary( shorten=True, name_padding=1 diff --git a/lib/iris/experimental/regrid.py b/lib/iris/experimental/regrid.py index e08b71c403..7608b2608a 100644 --- a/lib/iris/experimental/regrid.py +++ b/lib/iris/experimental/regrid.py @@ -498,9 +498,9 @@ def _regrid_area_weighted_array(src_data, x_dim, y_dim, weights_info, mdtol=0): # Flag to indicate whether the original data was a masked array. src_masked = src_data.mask.any() if ma.isMaskedArray(src_data) else False if src_masked: - src_area_masks = np.full(src_areas_shape, True, dtype=np.bool) + src_area_masks = np.full(src_areas_shape, True, dtype=np.bool_) else: - new_data_mask = np.full(new_shape, False, dtype=np.bool) + new_data_mask = np.full(new_shape, False, dtype=np.bool_) # Axes of data over which the weighted mean is calculated. axis = (y_dim, x_dim) diff --git a/lib/iris/fileformats/_ff.py b/lib/iris/fileformats/_ff.py index 1b1b2377ff..a198bb5740 100644 --- a/lib/iris/fileformats/_ff.py +++ b/lib/iris/fileformats/_ff.py @@ -816,7 +816,7 @@ def __iter__(self): return pp._interpret_fields(self._extract_field()) -def _parse_binary_stream(file_like, dtype=np.float, count=-1): +def _parse_binary_stream(file_like, dtype=np.float64, count=-1): """ Replacement :func:`numpy.fromfile` due to python3 performance issues. diff --git a/lib/iris/fileformats/pp.py b/lib/iris/fileformats/pp.py index 406da925b1..0627bcc4a2 100644 --- a/lib/iris/fileformats/pp.py +++ b/lib/iris/fileformats/pp.py @@ -752,7 +752,7 @@ def _data_bytes_to_shaped_array( # However, we still mask any MDI values in the array (below). pass else: - land_mask = mask.data.astype(np.bool) + land_mask = mask.data.astype(np.bool_) sea_mask = ~land_mask new_data = np.ma.masked_all(land_mask.shape) new_data.fill_value = mdi diff --git a/lib/iris/fileformats/pp_load_rules.py b/lib/iris/fileformats/pp_load_rules.py index 53d9f4dc35..ab99b5c7f8 100644 --- a/lib/iris/fileformats/pp_load_rules.py +++ b/lib/iris/fileformats/pp_load_rules.py @@ -580,7 +580,7 @@ def _epoch_date_hours(epoch_hours_unit, datetime): # numpy.float64. The behaviour of round is to recast this to an # int, which is not the desired behaviour for PP files. # So, cast the answer to numpy.float_ to be safe. - epoch_hours = np.float_(epoch_hours_unit.date2num(datetime)) + epoch_hours = np.float64(epoch_hours_unit.date2num(datetime)) if days_offset is not None: # Correct for any modifications to achieve a valid date. diff --git a/lib/iris/tests/__init__.py b/lib/iris/tests/__init__.py index 4a85e5cdb2..b2eebc4f03 100644 --- a/lib/iris/tests/__init__.py +++ b/lib/iris/tests/__init__.py @@ -483,26 +483,26 @@ def assertDataAlmostEqual(self, data, reference_filename, **kwargs): stats.get("max", 0.0), stats.get("min", 0.0), ), - dtype=np.float_, + dtype=np.float64, ) if math.isnan(stats.get("mean", 0.0)): self.assertTrue(math.isnan(data.mean())) else: data_stats = np.array( (data.mean(), data.std(), data.max(), data.min()), - dtype=np.float_, + dtype=np.float64, ) self.assertArrayAllClose(nstats, data_stats, **kwargs) else: self._ensure_folder(reference_path) stats = collections.OrderedDict( [ - ("std", np.float_(data.std())), - ("min", np.float_(data.min())), - ("max", np.float_(data.max())), + ("std", np.float64(data.std())), + ("min", np.float64(data.min())), + ("max", np.float64(data.max())), ("shape", data.shape), ("masked", ma.is_masked(data)), - ("mean", np.float_(data.mean())), + ("mean", np.float64(data.mean())), ] ) with open(reference_path, "w") as reference_file: diff --git a/lib/iris/tests/experimental/regrid/test_regrid_conservative_via_esmpy.py b/lib/iris/tests/experimental/regrid/test_regrid_conservative_via_esmpy.py index 5dc258dfb4..fa2da8e60c 100644 --- a/lib/iris/tests/experimental/regrid/test_regrid_conservative_via_esmpy.py +++ b/lib/iris/tests/experimental/regrid/test_regrid_conservative_via_esmpy.py @@ -675,7 +675,7 @@ def test_rotated(self): [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], ], - dtype=np.float, + dtype=np.float64, ) c1_areasum = _cube_area_sum(c1) @@ -715,7 +715,7 @@ def test_rotated(self): [100, 100, 199, 199, 100], [100, 100, 199, 199, 199], ], - dtype=np.float, + dtype=np.float64, ) c2_areasum = _cube_area_sum(c2) @@ -770,7 +770,7 @@ def test_missing_data_rotated(self): [100, 100, 100, 100, 100, 100, 100, 100, 100], [100, 100, 100, 100, 100, 100, 100, 100, 100], ], - dtype=np.float, + dtype=np.float64, ) if do_add_missing: diff --git a/lib/iris/tests/test_cell.py b/lib/iris/tests/test_cell.py index d6c6ace808..4690cedfa6 100644 --- a/lib/iris/tests/test_cell.py +++ b/lib/iris/tests/test_cell.py @@ -130,7 +130,7 @@ class Terry: self.assertEqual(self.d.__ne__(Terry()), NotImplemented) def test_numpy_int_equality(self): - dtypes = (np.int, np.int16, np.int32, np.int64) + dtypes = (np.int_, np.int16, np.int32, np.int64) for dtype in dtypes: val = dtype(3) cell = iris.coords.Cell(val, None) @@ -138,7 +138,7 @@ def test_numpy_int_equality(self): def test_numpy_float_equality(self): dtypes = ( - np.float, + np.float_, np.float16, np.float32, np.float64, diff --git a/lib/iris/tests/test_concatenate.py b/lib/iris/tests/test_concatenate.py index d45a884a2f..1f6c5c3843 100644 --- a/lib/iris/tests/test_concatenate.py +++ b/lib/iris/tests/test_concatenate.py @@ -415,7 +415,7 @@ def test_concat_masked_2x2d(self): self.assertEqual(result[0].shape, (2, 4)) mask = np.array( [[True, False, False, True], [False, True, True, False]], - dtype=np.bool, + dtype=np.bool_, ) self.assertArrayEqual(result[0].data.mask, mask) @@ -436,7 +436,7 @@ def test_concat_masked_2y2d(self): self.assertEqual(result[0].shape, (4, 2)) mask = np.array( [[True, False], [False, True], [False, True], [True, False]], - dtype=np.bool, + dtype=np.bool_, ) self.assertArrayEqual(result[0].data.mask, mask) @@ -458,7 +458,7 @@ def test_concat_masked_2y2d_with_concrete_and_lazy(self): self.assertEqual(result[0].shape, (4, 2)) mask = np.array( [[True, False], [False, True], [False, True], [True, False]], - dtype=np.bool, + dtype=np.bool_, ) self.assertArrayEqual(result[0].data.mask, mask) @@ -480,7 +480,7 @@ def test_concat_masked_2y2d_with_lazy_and_concrete(self): self.assertEqual(result[0].shape, (4, 2)) mask = np.array( [[True, False], [False, True], [False, True], [True, False]], - dtype=np.bool, + dtype=np.bool_, ) self.assertArrayEqual(result[0].data.mask, mask) diff --git a/lib/iris/tests/test_merge.py b/lib/iris/tests/test_merge.py index e404216143..185beb1bae 100644 --- a/lib/iris/tests/test_merge.py +++ b/lib/iris/tests/test_merge.py @@ -468,7 +468,7 @@ def _make_cube(self, a, b, c, d, data=0): ) for name, value in zip(["a", "b", "c", "d"], [a, b, c, d]): - dtype = np.str if isinstance(value, str) else np.float32 + dtype = np.str_ if isinstance(value, str) else np.float32 cube.add_aux_coord( AuxCoord( np.array([value], dtype=dtype), long_name=name, units="1" @@ -613,7 +613,7 @@ def _make_cube(self, a, b, data=0, a_dim=False, b_dim=False): ) for name, value, dim in zip(["a", "b"], [a, b], [a_dim, b_dim]): - dtype = np.str if isinstance(value, str) else np.float32 + dtype = np.str_ if isinstance(value, str) else np.float32 ctype = DimCoord if dim else AuxCoord coord = ctype( np.array([value], dtype=dtype), long_name=name, units="1" diff --git a/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py b/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py index 8ac86da7ec..0741a24926 100644 --- a/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py +++ b/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py @@ -431,7 +431,7 @@ def test_rotated_to_osgb(self): [1, 1, 1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 1, 1, 1], ], - np.bool, + np.bool_, ) self.assertArrayEqual(expected_mask, ut.data.mask) self.assertArrayEqual(expected_mask, vt.data.mask) diff --git a/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py b/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py index f1e385731a..5c7b5ea7d4 100644 --- a/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py +++ b/lib/iris/tests/unit/analysis/regrid/test_RectilinearRegridder.py @@ -266,7 +266,7 @@ def _regrid(self, data, method, extrapolation_mode=None): def test_default_ndarray(self): # NaN -> NaN # Extrapolated -> NaN - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method) @@ -278,7 +278,7 @@ def test_default_maskedarray(self): # NaN -> NaN # Extrapolated -> Masked # Masked -> Masked - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -293,7 +293,7 @@ def test_default_maskedarray_none_masked(self): # NaN -> NaN # Extrapolated -> Masked # Masked -> N/A - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method) @@ -307,7 +307,7 @@ def test_default_maskedarray_none_masked_expanded(self): # NaN -> NaN # Extrapolated -> Masked # Masked -> N/A - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) # Make sure the mask has been expanded data.mask = False data[0, 0] = np.nan @@ -322,7 +322,7 @@ def test_default_maskedarray_none_masked_expanded(self): def test_method_ndarray(self): # NaN -> NaN # Extrapolated -> linear - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method, "extrapolate") @@ -334,7 +334,7 @@ def test_method_maskedarray(self): # NaN -> NaN # Extrapolated -> linear # Masked -> Masked - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -348,7 +348,7 @@ def test_method_maskedarray(self): def test_nan_ndarray(self): # NaN -> NaN # Extrapolated -> NaN - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method, "nan") @@ -360,7 +360,7 @@ def test_nan_maskedarray(self): # NaN -> NaN # Extrapolated -> NaN # Masked -> Masked - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -373,7 +373,7 @@ def test_nan_maskedarray(self): def test_error_ndarray(self): # Values irrelevant - the function raises an error. - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: with self.assertRaisesRegex(ValueError, "out of bounds"): @@ -381,7 +381,7 @@ def test_error_ndarray(self): def test_error_maskedarray(self): # Values irrelevant - the function raises an error. - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -392,7 +392,7 @@ def test_mask_ndarray(self): # NaN -> NaN # Extrapolated -> Masked (this is different from all the other # modes) - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method, "mask") @@ -406,7 +406,7 @@ def test_mask_maskedarray(self): # NaN -> NaN # Extrapolated -> Masked # Masked -> Masked - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -420,7 +420,7 @@ def test_mask_maskedarray(self): def test_nanmask_ndarray(self): # NaN -> NaN # Extrapolated -> NaN - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan for method in self.methods: result = self._regrid(data, method, "nanmask") @@ -432,7 +432,7 @@ def test_nanmask_maskedarray(self): # NaN -> NaN # Extrapolated -> Masked # Masked -> Masked - data = ma.arange(12, dtype=np.float).reshape(3, 4) + data = ma.arange(12, dtype=np.float64).reshape(3, 4) data[0, 0] = np.nan data[2, 3] = ma.masked for method in self.methods: @@ -444,7 +444,7 @@ def test_nanmask_maskedarray(self): self.assertMaskedArrayEqual(result, expected) def test_invalid(self): - data = np.arange(12, dtype=np.float).reshape(3, 4) + data = np.arange(12, dtype=np.float64).reshape(3, 4) emsg = "Invalid extrapolation mode" for method in self.methods: with self.assertRaisesRegex(ValueError, emsg): diff --git a/lib/iris/tests/unit/aux_factory/test_OceanSFactory.py b/lib/iris/tests/unit/aux_factory/test_OceanSFactory.py index 6e8e40cd1b..a5cb5480a0 100644 --- a/lib/iris/tests/unit/aux_factory/test_OceanSFactory.py +++ b/lib/iris/tests/unit/aux_factory/test_OceanSFactory.py @@ -195,12 +195,12 @@ def setUp(self): np.arange(-0.975, 0, 0.05, dtype=float), units="1", long_name="s" ) self.eta = AuxCoord( - np.arange(-1, 3, dtype=np.float).reshape(2, 2), + np.arange(-1, 3, dtype=np.float64).reshape(2, 2), long_name="eta", units="m", ) self.depth = AuxCoord( - np.arange(4, dtype=np.float).reshape(2, 2) * 1e3, + np.arange(4, dtype=np.float64).reshape(2, 2) * 1e3, long_name="depth", units="m", ) diff --git a/lib/iris/tests/unit/aux_factory/test_OceanSg1Factory.py b/lib/iris/tests/unit/aux_factory/test_OceanSg1Factory.py index 238df2f073..321a013ef8 100644 --- a/lib/iris/tests/unit/aux_factory/test_OceanSg1Factory.py +++ b/lib/iris/tests/unit/aux_factory/test_OceanSg1Factory.py @@ -179,12 +179,12 @@ def setUp(self): np.linspace(-0.959, -0.001, 36), units="1", long_name="c" ) self.eta = AuxCoord( - np.arange(-1, 3, dtype=np.float).reshape(2, 2), + np.arange(-1, 3, dtype=np.float64).reshape(2, 2), long_name="eta", units="m", ) self.depth = AuxCoord( - np.array([[5, 200], [1000, 4000]], dtype=np.float), + np.array([[5, 200], [1000, 4000]], dtype=np.float64), long_name="depth", units="m", ) diff --git a/lib/iris/tests/unit/aux_factory/test_OceanSg2Factory.py b/lib/iris/tests/unit/aux_factory/test_OceanSg2Factory.py index fb3ada382e..d16285cd5d 100644 --- a/lib/iris/tests/unit/aux_factory/test_OceanSg2Factory.py +++ b/lib/iris/tests/unit/aux_factory/test_OceanSg2Factory.py @@ -179,12 +179,12 @@ def setUp(self): np.linspace(-0.959, -0.001, 36), units="1", long_name="c" ) self.eta = AuxCoord( - np.arange(-1, 3, dtype=np.float).reshape(2, 2), + np.arange(-1, 3, dtype=np.float64).reshape(2, 2), long_name="eta", units="m", ) self.depth = AuxCoord( - np.array([[5, 200], [1000, 4000]], dtype=np.float), + np.array([[5, 200], [1000, 4000]], dtype=np.float64), long_name="depth", units="m", ) diff --git a/lib/iris/tests/unit/aux_factory/test_OceanSigmaFactory.py b/lib/iris/tests/unit/aux_factory/test_OceanSigmaFactory.py index 69a8a32c6e..d4c0a33fdf 100644 --- a/lib/iris/tests/unit/aux_factory/test_OceanSigmaFactory.py +++ b/lib/iris/tests/unit/aux_factory/test_OceanSigmaFactory.py @@ -102,12 +102,12 @@ def setUp(self): np.linspace(-0.05, -1, 5), long_name="sigma", units="1" ) self.eta = AuxCoord( - np.arange(-1, 3, dtype=np.float).reshape(2, 2), + np.arange(-1, 3, dtype=np.float64).reshape(2, 2), long_name="eta", units="m", ) self.depth = AuxCoord( - np.arange(4, dtype=np.float).reshape(2, 2) * 1e3, + np.arange(4, dtype=np.float64).reshape(2, 2) * 1e3, long_name="depth", units="m", ) diff --git a/lib/iris/tests/unit/aux_factory/test_OceanSigmaZFactory.py b/lib/iris/tests/unit/aux_factory/test_OceanSigmaZFactory.py index 4a4e30b9ca..b8a574258d 100644 --- a/lib/iris/tests/unit/aux_factory/test_OceanSigmaZFactory.py +++ b/lib/iris/tests/unit/aux_factory/test_OceanSigmaZFactory.py @@ -199,22 +199,22 @@ def derive(sigma, eta, depth, depth_c, nsigma, zlev, coord=True): def setUp(self): self.sigma = DimCoord( - np.arange(5, dtype=np.float) * 10, long_name="sigma", units="1" + np.arange(5, dtype=np.float64) * 10, long_name="sigma", units="1" ) self.eta = AuxCoord( - np.arange(4, dtype=np.float).reshape(2, 2), + np.arange(4, dtype=np.float64).reshape(2, 2), long_name="eta", units="m", ) self.depth = AuxCoord( - np.arange(4, dtype=np.float).reshape(2, 2) * 10, + np.arange(4, dtype=np.float64).reshape(2, 2) * 10, long_name="depth", units="m", ) self.depth_c = AuxCoord([15], long_name="depth_c", units="m") self.nsigma = AuxCoord([3], long_name="nsigma") self.zlev = DimCoord( - np.arange(5, dtype=np.float) * 10, long_name="zlev", units="m" + np.arange(5, dtype=np.float64) * 10, long_name="zlev", units="m" ) self.kwargs = dict( sigma=self.sigma, diff --git a/lib/iris/tests/unit/common/metadata/test_BaseMetadata.py b/lib/iris/tests/unit/common/metadata/test_BaseMetadata.py index 71fabe6c73..4ff8ec0de6 100644 --- a/lib/iris/tests/unit/common/metadata/test_BaseMetadata.py +++ b/lib/iris/tests/unit/common/metadata/test_BaseMetadata.py @@ -1074,8 +1074,8 @@ def setUp(self): self.values = OrderedDict( one=sentinel.one, two=sentinel.two, - three=np.float(3.14), - four=np.arange(10, dtype=np.float), + three=np.float64(3.14), + four=np.arange(10, dtype=np.float64), five=ma.arange(10, dtype=np.int16), ) self.cls = BaseMetadata diff --git a/lib/iris/tests/unit/common/metadata/test__hexdigest.py b/lib/iris/tests/unit/common/metadata/test_hexdigest.py similarity index 90% rename from lib/iris/tests/unit/common/metadata/test__hexdigest.py rename to lib/iris/tests/unit/common/metadata/test_hexdigest.py index 798f71bcd0..55c697ea6d 100644 --- a/lib/iris/tests/unit/common/metadata/test__hexdigest.py +++ b/lib/iris/tests/unit/common/metadata/test_hexdigest.py @@ -4,7 +4,7 @@ # See COPYING and COPYING.LESSER in the root of the repository for full # licensing details. """ -Unit tests for the :func:`iris.common.metadata._hexdigest`. +Unit tests for the :func:`iris.common.metadata.hexdigest`. """ @@ -18,7 +18,7 @@ import numpy as np from xxhash import xxh64, xxh64_hexdigest -from iris.common.metadata import _hexdigest as hexdigest +from iris.common.metadata import hexdigest class TestBytesLikeObject(tests.IrisTest): @@ -49,18 +49,18 @@ def test_string(self): self.assertEqual(expected, hexdigest(value)) def test_numpy_array_int(self): - value = np.arange(10, dtype=np.int) + value = np.arange(10, dtype=np.int_) expected = self._ndarray(value) self.assertEqual(expected, hexdigest(value)) def test_numpy_array_float(self): - value = np.arange(10, dtype=np.float) + value = np.arange(10, dtype=np.float64) expected = self._ndarray(value) self.assertEqual(expected, hexdigest(value)) def test_numpy_array_float_not_int(self): - ivalue = np.arange(10, dtype=np.int) - fvalue = np.arange(10, dtype=np.float) + ivalue = np.arange(10, dtype=np.int_) + fvalue = np.arange(10, dtype=np.float64) expected = self._ndarray(ivalue) self.assertNotEqual(expected, hexdigest(fvalue)) @@ -75,7 +75,7 @@ def test_numpy_array_reshape_not_flat(self): self.assertNotEqual(expected, hexdigest(value.flatten())) def test_masked_array_int(self): - value = ma.arange(10, dtype=np.int) + value = ma.arange(10, dtype=np.int_) expected = self._masked(value) self.assertEqual(expected, hexdigest(value)) @@ -85,7 +85,7 @@ def test_masked_array_int(self): self.assertEqual(expected, hexdigest(value)) def test_masked_array_float(self): - value = ma.arange(10, dtype=np.float) + value = ma.arange(10, dtype=np.float64) expected = self._masked(value) self.assertEqual(expected, hexdigest(value)) @@ -95,8 +95,8 @@ def test_masked_array_float(self): self.assertEqual(expected, hexdigest(value)) def test_masked_array_float_not_int(self): - ivalue = ma.arange(10, dtype=np.int) - fvalue = ma.arange(10, dtype=np.float) + ivalue = ma.arange(10, dtype=np.int_) + fvalue = ma.arange(10, dtype=np.float64) expected = self._masked(ivalue) self.assertNotEqual(expected, hexdigest(fvalue)) @@ -127,7 +127,7 @@ def test_int(self): self.assertEqual(expected, hexdigest(value)) def test_numpy_int(self): - value = np.int(123) + value = int(123) expected = self._expected(value) self.assertEqual(expected, hexdigest(value)) @@ -137,7 +137,7 @@ def test_float(self): self.assertEqual(expected, hexdigest(value)) def test_numpy_float(self): - value = np.float(123.4) + value = float(123.4) expected = self._expected(value) self.assertEqual(expected, hexdigest(value)) diff --git a/lib/iris/tests/unit/experimental/regrid/test_regrid_weighted_curvilinear_to_rectilinear.py b/lib/iris/tests/unit/experimental/regrid/test_regrid_weighted_curvilinear_to_rectilinear.py index f61b489c2b..6b8064faca 100644 --- a/lib/iris/tests/unit/experimental/regrid/test_regrid_weighted_curvilinear_to_rectilinear.py +++ b/lib/iris/tests/unit/experimental/regrid/test_regrid_weighted_curvilinear_to_rectilinear.py @@ -37,7 +37,7 @@ def setUp(self): # Source cube. self.test_src_name = "air_temperature" self.test_src_units = "K" - self.test_src_data = ma.arange(1, 13, dtype=np.float).reshape(3, 4) + self.test_src_data = ma.arange(1, 13, dtype=np.float64).reshape(3, 4) self.test_src_attributes = dict(wibble="wobble") self.test_scalar_coord = iris.coords.DimCoord( [1], long_name="test_scalar_coord" @@ -135,7 +135,7 @@ def setUp(self): ) def _weighted_mean(self, points): - points = np.asarray(points, dtype=np.float) + points = np.asarray(points, dtype=np.float64) weights = points * self.weight_factor numerator = denominator = 0 for point, weight in zip(points, weights): diff --git a/lib/iris/tests/unit/fileformats/cf/test_CFReader.py b/lib/iris/tests/unit/fileformats/cf/test_CFReader.py index 7d4bf232fc..26296daccf 100644 --- a/lib/iris/tests/unit/fileformats/cf/test_CFReader.py +++ b/lib/iris/tests/unit/fileformats/cf/test_CFReader.py @@ -59,7 +59,7 @@ def netcdf_variable( class Test_translate__global_attributes(tests.IrisTest): def setUp(self): - ncvar = netcdf_variable("ncvar", "height", np.float) + ncvar = netcdf_variable("ncvar", "height", np.float64) ncattrs = mock.Mock(return_value=["dimensions"]) getncattr = mock.Mock(return_value="something something_else") self.dataset = mock.Mock( @@ -80,24 +80,24 @@ def test_create_global_attributes(self): class Test_translate__formula_terms(tests.IrisTest): def setUp(self): self.delta = netcdf_variable( - "delta", "height", np.float, bounds="delta_bnds" + "delta", "height", np.float64, bounds="delta_bnds" ) self.delta_bnds = netcdf_variable( "delta_bnds", "height bnds", np.float ) self.sigma = netcdf_variable( - "sigma", "height", np.float, bounds="sigma_bnds" + "sigma", "height", np.float64, bounds="sigma_bnds" ) self.sigma_bnds = netcdf_variable( "sigma_bnds", "height bnds", np.float ) - self.orography = netcdf_variable("orography", "lat lon", np.float) + self.orography = netcdf_variable("orography", "lat lon", np.float64) formula_terms = "a: delta b: sigma orog: orography" standard_name = "atmosphere_hybrid_height_coordinate" self.height = netcdf_variable( "height", "height", - np.float, + np.float64, formula_terms=formula_terms, bounds="height_bnds", standard_name=standard_name, @@ -106,13 +106,16 @@ def setUp(self): # which will be ignored by the cf loader. formula_terms = "a: delta_bnds b: sigma_bnds orog: orography" self.height_bnds = netcdf_variable( - "height_bnds", "height bnds", np.float, formula_terms=formula_terms + "height_bnds", + "height bnds", + np.float64, + formula_terms=formula_terms, ) - self.lat = netcdf_variable("lat", "lat", np.float) - self.lon = netcdf_variable("lon", "lon", np.float) + self.lat = netcdf_variable("lat", "lat", np.float64) + self.lon = netcdf_variable("lon", "lon", np.float64) # Note that, only lat and lon are explicitly associated as coordinates. self.temp = netcdf_variable( - "temp", "height lat lon", np.float, coordinates="lat lon" + "temp", "height lat lon", np.float64, coordinates="lat lon" ) self.variables = dict( @@ -179,24 +182,24 @@ def test_create_formula_terms(self): class Test_build_cf_groups__formula_terms(tests.IrisTest): def setUp(self): self.delta = netcdf_variable( - "delta", "height", np.float, bounds="delta_bnds" + "delta", "height", np.float64, bounds="delta_bnds" ) self.delta_bnds = netcdf_variable( "delta_bnds", "height bnds", np.float ) self.sigma = netcdf_variable( - "sigma", "height", np.float, bounds="sigma_bnds" + "sigma", "height", np.float64, bounds="sigma_bnds" ) self.sigma_bnds = netcdf_variable( "sigma_bnds", "height bnds", np.float ) - self.orography = netcdf_variable("orography", "lat lon", np.float) + self.orography = netcdf_variable("orography", "lat lon", np.float64) formula_terms = "a: delta b: sigma orog: orography" standard_name = "atmosphere_hybrid_height_coordinate" self.height = netcdf_variable( "height", "height", - np.float, + np.float64, formula_terms=formula_terms, bounds="height_bnds", standard_name=standard_name, @@ -205,15 +208,18 @@ def setUp(self): # which will be ignored by the cf loader. formula_terms = "a: delta_bnds b: sigma_bnds orog: orography" self.height_bnds = netcdf_variable( - "height_bnds", "height bnds", np.float, formula_terms=formula_terms + "height_bnds", + "height bnds", + np.float64, + formula_terms=formula_terms, ) - self.lat = netcdf_variable("lat", "lat", np.float) - self.lon = netcdf_variable("lon", "lon", np.float) - self.x = netcdf_variable("x", "lat lon", np.float) - self.y = netcdf_variable("y", "lat lon", np.float) + self.lat = netcdf_variable("lat", "lat", np.float64) + self.lon = netcdf_variable("lon", "lon", np.float64) + self.x = netcdf_variable("x", "lat lon", np.float64) + self.y = netcdf_variable("y", "lat lon", np.float64) # Note that, only lat and lon are explicitly associated as coordinates. self.temp = netcdf_variable( - "temp", "height lat lon", np.float, coordinates="x y" + "temp", "height lat lon", np.float64, coordinates="x y" ) self.variables = dict( @@ -332,7 +338,7 @@ def test_auxiliary_ignore(self): self.assertEqual(warn.call_count, 1) def test_promoted_auxiliary_ignore(self): - self.wibble = netcdf_variable("wibble", "lat wibble", np.float) + self.wibble = netcdf_variable("wibble", "lat wibble", np.float64) self.variables["wibble"] = self.wibble self.orography.coordinates = "wibble" with mock.patch( diff --git a/lib/iris/tests/unit/fileformats/pp/test_PPField.py b/lib/iris/tests/unit/fileformats/pp/test_PPField.py index 7c3ef33182..20a431994c 100644 --- a/lib/iris/tests/unit/fileformats/pp/test_PPField.py +++ b/lib/iris/tests/unit/fileformats/pp/test_PPField.py @@ -199,8 +199,8 @@ def test_odd_bplon_rotated(self): class Test__init__(tests.IrisTest): def setUp(self): - header_longs = np.zeros(pp.NUM_LONG_HEADERS, dtype=np.int) - header_floats = np.zeros(pp.NUM_FLOAT_HEADERS, dtype=np.float) + header_longs = np.zeros(pp.NUM_LONG_HEADERS, dtype=np.int_) + header_floats = np.zeros(pp.NUM_FLOAT_HEADERS, dtype=np.float64) self.header = list(header_longs) + list(header_floats) def test_no_headers(self): @@ -232,8 +232,8 @@ def test_raw_lbpack(self): class Test__getattr__(tests.IrisTest): def setUp(self): - header_longs = np.zeros(pp.NUM_LONG_HEADERS, dtype=np.int) - header_floats = np.zeros(pp.NUM_FLOAT_HEADERS, dtype=np.float) + header_longs = np.zeros(pp.NUM_LONG_HEADERS, dtype=np.int_) + header_floats = np.zeros(pp.NUM_FLOAT_HEADERS, dtype=np.float64) self.header = list(header_longs) + list(header_floats) def test_attr_singular_long(self): diff --git a/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py b/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py index b6b9cb3263..8bf9a0435b 100644 --- a/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py +++ b/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py @@ -32,7 +32,7 @@ def setUp(self): decompressed = np.arange(data_len).reshape(*self.data_shape) decompressed *= np.arange(self.data_shape[1]) % 3 + 1 - decompressed_mask = np.zeros(self.data_shape, np.bool) + decompressed_mask = np.zeros(self.data_shape, np.bool_) decompressed_mask[ y_halo + rim : -(y_halo + rim), x_halo + rim : -(x_halo + rim) ] = True @@ -81,7 +81,7 @@ def setUp(self): self.land = np.array( [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], dtype=np.float64 ) - sea = ~self.land.astype(np.bool) + sea = ~self.land.astype(np.bool_) self.land_masked_data = np.array([1, 3, 4.5]) self.sea_masked_data = np.array([1, 3, 4.5, -4, 5, 0, 1, 2, 3]) diff --git a/lib/iris/tests/unit/lazy_data/test_lazy_elementwise.py b/lib/iris/tests/unit/lazy_data/test_lazy_elementwise.py index c8aebc34e4..f02b86cf33 100644 --- a/lib/iris/tests/unit/lazy_data/test_lazy_elementwise.py +++ b/lib/iris/tests/unit/lazy_data/test_lazy_elementwise.py @@ -44,7 +44,7 @@ def test_dtype_change(self): lazy_array = as_lazy_data(concrete_array) wrapped = lazy_elementwise(lazy_array, _test_elementwise_op) self.assertTrue(is_lazy_data(wrapped)) - self.assertEqual(wrapped.dtype, np.int) + self.assertEqual(wrapped.dtype, np.int_) self.assertEqual(wrapped.compute().dtype, wrapped.dtype)