diff --git a/.circleci/config.yml b/.circleci/config.yml index 85c510efc5..c39f436453 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,174 +7,189 @@ orbs: commands: check_changes: steps: - - run: | - if (test "$CIRCLE_BRANCH" = main || - git --no-pager diff --name-only origin/main... | - grep -q -E -f .circleci/install_triggers) - then - echo Running installation tests - else - echo Skipping installation tests - circleci step halt - fi - -jobs: - run_tests: - # Run Python 3 tests - working_directory: /test - docker: - - image: esmvalgroup/esmvaltool:development + - run: + name: Check whether or not installation tests are needed + command: | + if (test "$CIRCLE_BRANCH" = main || + git --no-pager diff --name-only origin/main... | + grep -q -E -f .circleci/install_triggers) + then + echo Running installation tests + else + echo Skipping installation tests + circleci step halt + fi + test_and_report: steps: - - checkout - run: - # Update environment if needed and run tests + name: Run tests command: | . /opt/conda/etc/profile.d/conda.sh - mkdir /logs - conda --version > /logs/conda_version.txt conda activate esmvaltool - python --version > /logs/python_version.txt - conda env export > /logs/environment.yml - pip install .[test] > /logs/install.txt - # Run flake8 check with source code (it doesn't work without it) - pytest -n 2 -m flake8 - # Update/install Julia dependencies - esmvaltool install Julia > /logs/install_Julia.txt - # Remove source to test installed software - rm -r esmvaltool - pytest -n 2 -m 'not flake8' + pytest -n 4 -m 'not flake8' --junitxml=test-reports/report.xml + esmvaltool version + esmvaltool -- --help + ncl -V + cdo --version + - store_test_results: + path: test-reports/ - store_artifacts: path: /logs + - run: + name: Compress pytest artifacts + command: tar -cvzf pytest.tar.gz -C /tmp/pytest-of-root/ . + when: always - store_artifacts: - path: test-reports/ - - store_test_results: - path: test-reports/ - - coverage-reporter/send_report: - coverage-reports: 'test-reports/coverage.xml' - project-token: $CODACY_PROJECT_TOKEN - # Skip if token isn't defined. - # Useful to let forks CI pass without passing secrets - skip: true - - test_installation_from_source_test_mode: - # Test Python 3 installation - working_directory: /test_installation - docker: - - image: condaforge/mambaforge:latest + path: pytest.tar.gz + - run: + name: Compress test-report artifacts + command: tar -cvzf test-reports.tar.gz test-reports/ + when: always + - store_artifacts: + path: test-reports.tar.gz + test_installation_from_source: + parameters: + extra: + type: string + flags: + type: string + default: "" steps: + - run: + name: Install git+ssh + environment: + DEBIAN_FRONTEND: noninteractive # needed to install tzdata + command: apt update && apt install -y git ssh - checkout - check_changes + - run: + name: Generate cache key + command: date '+%Y-%V' | tee cache_key.txt - restore_cache: - key: test-install-{{ .Branch }} + key: install-<< parameters.extra >>-{{ .Branch }}-{{ checksum "cache_key.txt" }} - run: + name: Install dependencies command: | + # Install . /opt/conda/etc/profile.d/conda.sh - set -x mkdir /logs - # Install - mamba create --name esmvaltool 'python=3.10' >> /logs/conda.txt 2>&1 - mamba env update --name esmvaltool >> /logs/conda.txt 2>&1 - set +x; conda activate esmvaltool; set -x - mamba install julia >> /logs/conda.txt 2>&1 - pip install .[test] > /logs/install.txt 2>&1 - esmvaltool install Julia - # Remove source to test installed software - rm -r esmvaltool - # Log versions + mamba env create >> /logs/conda.txt 2>&1 + conda activate esmvaltool + pip install << parameters.flags >> ".[<>]"> /logs/install.txt 2>&1 + esmvaltool install Julia > /logs/install_julia.txt 2>&1 + if [[ "<>" != *'--editable'* ]] + then + rm -r esmvaltool + fi + - run: + name: Log versions + command: | + . /opt/conda/etc/profile.d/conda.sh + conda activate esmvaltool dpkg -l > /logs/versions.txt - mamba env export > /logs/environment.yml + conda env export > /logs/environment.yml pip freeze > /logs/requirements.txt - # Test installation - pytest -n 2 - esmvaltool -h - ncl -V - # cdo test, check that it supports hdf5 - cdo --version - echo 0 | cdo -f nc input,r1x1 tmp.nc - ncdump tmp.nc | ncgen -k hdf5 -o tmp.nc - cdo -f nc copy tmp.nc tmp2.nc - no_output_timeout: 30m + - run: + name: Lint source code + command: | + . /opt/conda/etc/profile.d/conda.sh + conda activate esmvaltool + pytest -n 4 -m flake8 + - test_and_report - save_cache: - key: test-install-{{ .Branch }} + key: install-<< parameters.extra >>-{{ .Branch }}-{{ checksum "cache_key.txt" }} paths: - - "/opt/conda/pkgs" - - ".eggs" - - store_artifacts: - path: /logs - - store_artifacts: - path: test-reports/ - - store_test_results: - path: test-reports/ + - /opt/conda/pkgs + - /root/.cache/pip + - .pytest_cache - test_installation_from_source_develop_mode: - # Test development installation - working_directory: /develop +jobs: + run_tests: + # Run tests docker: - - image: condaforge/mambaforge:latest + - image: esmvalgroup/esmvaltool:development + resource_class: large steps: - checkout - - check_changes - run: + name: Generate cache key + command: date '+%Y-%V' | tee cache_key.txt + - restore_cache: + key: test-{{ .Branch }}-{{ checksum "cache_key.txt" }} + - run: + name: Install dependencies command: | . /opt/conda/etc/profile.d/conda.sh - set -x mkdir /logs - # Install - mamba create --name esmvaltool 'python=3.10' >> /logs/conda.txt 2>&1 - mamba env update --name esmvaltool >> /logs/conda.txt 2>&1 - set +x; conda activate esmvaltool; set -x - mamba install julia >> /logs/conda.txt 2>&1 - pip install -e .[develop] > /logs/install.txt 2>&1 - esmvaltool install Julia - # Log versions - dpkg -l > /logs/versions.txt - mamba env export > /logs/environment.yml - pip freeze > /logs/requirements.txt - # Test installation - pytest -n 2 - esmvaltool -h - ncl -V - cdo --version - no_output_timeout: 30m - - store_artifacts: - path: /logs + conda activate esmvaltool + pip install .[test] > /logs/install.txt 2>&1 + # Run flake8 check with source code (it doesn't work without it) + pytest -n 4 -m flake8 + # Update/install Julia dependencies + esmvaltool install Julia > /logs/install_julia.txt 2>&1 + # Remove source to test installed software + rm -r esmvaltool + - test_and_report + - save_cache: + key: test-{{ .Branch }}-{{ checksum "cache_key.txt" }} + paths: + - /root/.cache/pip + - .pytest_cache + - coverage-reporter/send_report: + coverage-reports: 'test-reports/coverage.xml' + project-token: $CODACY_PROJECT_TOKEN + skip: true # skip if project-token is not defined (i.e. on a fork) + + test_installation_from_source_test_mode: + # Test installation from source + docker: + - image: condaforge/mambaforge + resource_class: large + steps: + - test_installation_from_source: + extra: test + + test_installation_from_source_develop_mode: + # Test development installation + docker: + - image: condaforge/mambaforge + resource_class: large + steps: + - test_installation_from_source: + extra: develop + flags: "--editable" build_documentation: # Test building documentation - working_directory: /doc docker: - - image: condaforge/mambaforge:latest + - image: condaforge/mambaforge + resource_class: small steps: - checkout - - restore_cache: - key: documentation-{{ .Branch }} - run: command: | mkdir /logs + . /opt/conda/etc/profile.d/conda.sh # Install - pip install -r doc/sphinx/source/requirements.txt > /logs/install.txt 2>&1 - pip install sphinx sphinx_rtd_theme >> /logs/install.txt 2>&1 + mamba env create + conda activate esmvaltool + pip install .[doc] # Log versions dpkg -l > /logs/versions.txt + conda env export > /logs/environment.yml pip freeze > /logs/requirements.txt # Test building documentation - MPLBACKEND=Agg sphinx-build -W doc/sphinx/source/ doc/sphinx/build/ - - save_cache: - key: documentation-{{ .Branch }} - paths: - - ".eggs" + MPLBACKEND=Agg sphinx-build -W doc/sphinx/source doc/spinx/build - store_artifacts: path: /logs test_installation_from_conda: # Test conda package installation - working_directory: /esmvaltool docker: - image: condaforge/mambaforge + resource_class: medium steps: - run: - environment: - R_INSTALL_N_CPUS: 2 command: | . /opt/conda/etc/profile.d/conda.sh set -x @@ -184,15 +199,15 @@ jobs: mamba create -y --name esmvaltool -c conda-forge esmvaltool julia 'python=3.10' >> /logs/conda.txt 2>&1 # Activate the environment set +x; conda activate esmvaltool; set -x - # install the package - mamba install -c conda-forge esmvaltool >> /logs/install.txt 2>&1 + # install the Julia dependencies + esmvaltool install Julia > /logs/install_Julia.txt 2>&1 # Log versions mamba env export > /logs/environment.yml # Test installation - esmvaltool -h + esmvaltool -- --help + esmvaltool version ncl -V cdo --version - no_output_timeout: 30m - store_artifacts: path: /logs @@ -202,8 +217,6 @@ workflows: - run_tests - test_installation_from_source_test_mode - test_installation_from_source_develop_mode - - build_documentation - - test_installation_from_conda nightly: triggers: - schedule: diff --git a/.dockerignore b/.dockerignore index cfb2cc4159..7168bcfade 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,10 +2,9 @@ **/__pycache__ .* doc -package tests ESMValTool.egg-info - +!.git !.zenodo.json diff --git a/.github/workflows/citation_file_validator.yml b/.github/workflows/citation_file_validator.yml index 0184331b65..43a4d5c444 100644 --- a/.github/workflows/citation_file_validator.yml +++ b/.github/workflows/citation_file_validator.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out a copy of the repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Check whether the citation metadata from CITATION.cff is valid uses: citation-file-format/cffconvert-github-action@2.0.0 diff --git a/.github/workflows/create-condalock-file.yml b/.github/workflows/create-condalock-file.yml index 430b2532dc..7cd0b21d5b 100644 --- a/.github/workflows/create-condalock-file.yml +++ b/.github/workflows/create-condalock-file.yml @@ -3,7 +3,7 @@ name: Conda lock file creation on: # Trigger on push on main or other branch for testing # NOTE that push: main will create the file very often - # and hence lots of automated PRs + # and hence lots of automated PRs # push: # branches: # - condalock-update @@ -15,7 +15,9 @@ jobs: name: Create conda lock file for latest Python runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true diff --git a/.github/workflows/install-from-conda.yml b/.github/workflows/install-from-conda.yml index 0525328ba6..c42716770f 100644 --- a/.github/workflows/install-from-conda.yml +++ b/.github/workflows/install-from-conda.yml @@ -18,7 +18,6 @@ jobs: python-version: ["3.8", "3.9", "3.10"] name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 - uses: conda-incubator/setup-miniconda@v2 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/install-from-condalock-file.yml b/.github/workflows/install-from-condalock-file.yml index ca90115c51..7a82b5bd9f 100644 --- a/.github/workflows/install-from-condalock-file.yml +++ b/.github/workflows/install-from-condalock-file.yml @@ -29,7 +29,9 @@ jobs: fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool-fromlock diff --git a/.github/workflows/install-from-source.yml b/.github/workflows/install-from-source.yml index 5221435771..2721fc65b9 100644 --- a/.github/workflows/install-from-source.yml +++ b/.github/workflows/install-from-source.yml @@ -18,7 +18,9 @@ jobs: fail-fast: false name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool diff --git a/.github/workflows/pypi-build-and-deploy.yml b/.github/workflows/pypi-build-and-deploy.yml index 244e7376d7..9e7ab07b88 100644 --- a/.github/workflows/pypi-build-and-deploy.yml +++ b/.github/workflows/pypi-build-and-deploy.yml @@ -14,7 +14,9 @@ jobs: name: Build and publish ESMValTool on PyPi runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Set up Python 3.10 uses: actions/setup-python@v1 with: @@ -44,6 +46,7 @@ jobs: # Publish on PyPi - name: Publish distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@v1.5.0 with: + user: __token__ password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/run-tests-monitor.yml b/.github/workflows/run-tests-monitor.yml index 2a6dd52559..26eba85855 100644 --- a/.github/workflows/run-tests-monitor.yml +++ b/.github/workflows/run-tests-monitor.yml @@ -21,7 +21,9 @@ jobs: python-version: ["3.8", "3.9", "3.10"] name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool @@ -43,9 +45,6 @@ jobs: - name: Install ESMValTool shell: bash -l {0} run: pip install -e .[develop] 2>&1 | tee test_linux_artifacts_python_${{ matrix.python-version }}/install.txt - - name: Install R dependencies - shell: bash -l {0} - run: esmvaltool install R - name: Install Julia dependencies shell: bash -l {0} run: esmvaltool install Julia @@ -72,7 +71,9 @@ jobs: fail-fast: false name: OSX Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool diff --git a/.github/workflows/test-development.yml b/.github/workflows/test-development.yml index 8bef81bf0e..a5d80b290d 100644 --- a/.github/workflows/test-development.yml +++ b/.github/workflows/test-development.yml @@ -24,7 +24,9 @@ jobs: python-version: ["3.8", "3.9", "3.10"] name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool @@ -43,9 +45,6 @@ jobs: - name: Install ESMValTool shell: bash -l {0} run: pip install -e .[develop] 2>&1 | tee develop_test_linux_artifacts_python_${{ matrix.python-version }}/install.txt - - name: Install R dependencies - shell: bash -l {0} - run: esmvaltool install R - name: Install Julia dependencies shell: bash -l {0} run: esmvaltool install Julia diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4d9599c734..da49c08dbc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,9 @@ jobs: python-version: ["3.8", "3.9", "3.10"] name: Linux Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool @@ -37,9 +39,6 @@ jobs: - name: Install ESMValTool shell: bash -l {0} run: pip install -e .[develop] 2>&1 | tee test_linux_artifacts_python_${{ matrix.python-version }}/install.txt - - name: Install R dependencies - shell: bash -l {0} - run: esmvaltool install R - name: Install Julia dependencies shell: bash -l {0} run: esmvaltool install Julia @@ -61,7 +60,9 @@ jobs: fail-fast: false name: OSX Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: conda-incubator/setup-miniconda@v2 with: activate-environment: esmvaltool diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f8ccd23175..c481981750 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,19 +7,27 @@ version: 2 # Set the version of Python and other tools you might need build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: - python: "3.10" + python: "mambaforge-4.10" -# Build documentation in the docs/sphinx/source directory with Sphinx +# Declare the requirements required to build your docs +conda: + environment: + environment.yml + +python: + install: + - method: pip + path: . + extra_requirements: + - doc + +# Build documentation in the doc directory with Sphinx sphinx: - configuration: doc/sphinx/source/conf.py + configuration: doc/sphinx/source/conf.py + fail_on_warning: true # If using Sphinx, optionally build your docs in additional formats such as PDF formats: - - pdf - -# Optionally declare the Python requirements required to build your docs -python: - install: - - requirements: doc/sphinx/source/requirements.txt + - pdf diff --git a/.stickler.yml b/.stickler.yml deleted file mode 100644 index 173ac052bd..0000000000 --- a/.stickler.yml +++ /dev/null @@ -1,24 +0,0 @@ -# stickler-ci configuration - ---- - -linters: - flake8: - pep8: - yamllint: - shellcheck: -files: - ignore: [ - 'doc/sphinx', - 'esmvaltool/doc/sphinx', - # ignore old stuff, recent versions are in 'esmvaltool' dir - 'backend', - 'diag_scripts', - 'interface_data', - 'interface_scripts', - 'main.py', - 'nml', - 'plot_scripts', - 'reformat_scripts', - 'variable_defs', - ] diff --git a/doc/sphinx/source/api/esmvaltool.diag_scripts.mlr/custom_sklearn.rst b/doc/sphinx/source/api/esmvaltool.diag_scripts.mlr/custom_sklearn.rst index 50f6465b80..48078b9a39 100644 --- a/doc/sphinx/source/api/esmvaltool.diag_scripts.mlr/custom_sklearn.rst +++ b/doc/sphinx/source/api/esmvaltool.diag_scripts.mlr/custom_sklearn.rst @@ -4,3 +4,7 @@ Custom extensions of sklearn functionalities ============================================ .. automodule:: esmvaltool.diag_scripts.mlr.custom_sklearn + :no-autosummary: +.. + enabling autosummary with autodocsumm 0.2.8 gives a warning message: + WARNING: autosummary: failed to import AdvancedPipeline.steps. diff --git a/doc/sphinx/source/community/code_documentation.rst b/doc/sphinx/source/community/code_documentation.rst index 96f16fa2d3..555abad4f1 100644 --- a/doc/sphinx/source/community/code_documentation.rst +++ b/doc/sphinx/source/community/code_documentation.rst @@ -447,22 +447,11 @@ When adding or removing dependencies, please consider applying the changes in the following files: - ``environment.yml`` - contains development dependencies that cannot be installed from - `PyPI `__/`CRAN `__/`Julia package registry `__ + contains dependencies that cannot be installed from + `PyPI `__/`Julia package registry `__ - ``environment_osx.yml`` contains development dependencies for MacOSX. Should be the same as ``environment.yml``, but currently without multi language support. -- ``docs/sphinx/source/requirements.txt`` - contains Python dependencies needed to build the documentation that can be - installed from PyPI -- ``docs/sphinx/source/conf.py`` - contains a list of Python dependencies needed to build the documentation that - cannot be installed from PyPI and need to be mocked when building the - documentation. - (We do not use conda to build the documentation because this is too time - consuming.) -- ``esmvaltool/install/R/r_requirements.txt`` - contains R dependencies that can be installed from CRAN - ``esmvaltool/install/Julia/Project.toml`` contains Julia dependencies that can be installed from the default Julia package registry diff --git a/doc/sphinx/source/community/release_strategy.rst b/doc/sphinx/source/community/release_strategy.rst index 0174177f72..29752cbacf 100644 --- a/doc/sphinx/source/community/release_strategy.rst +++ b/doc/sphinx/source/community/release_strategy.rst @@ -325,9 +325,10 @@ All tests should pass before making a release (branch). 2. Increase the version number ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The version number is stored in ``esmvaltool/__init__.py``, ``CITATION.cff``. -Make sure to update both files. -Also update the release date in ``CITATION.cff``. +The version number is automatically generated from the information provided by +git using [setuptools-scm](https://pypi.org/project/setuptools-scm/), but a +static version number is stored in ``CITATION.cff``. +Make sure to update the version number and release date in ``CITATION.cff``. See https://semver.org for more information on choosing a version number. Make a pull request and get it merged into ``main``. diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py index 55b45ff0b3..0234dfed1f 100644 --- a/doc/sphinx/source/conf.py +++ b/doc/sphinx/source/conf.py @@ -32,7 +32,7 @@ # This is used for linking and such so we link to the thing we're building rtd_version = os.environ.get("READTHEDOCS_VERSION", "latest") -if rtd_version not in ["latest", "doc"]: # TODO: add "stable" once we have it +if rtd_version not in ["latest", "stable", "doc"]: rtd_version = "latest" # Generate gallery @@ -70,25 +70,6 @@ 'autosummary': True, } -autodoc_mock_imports = [ - 'cartopy', - 'cf_units', - 'cftime', - 'eofs', - 'ESMPy', - 'esmvalcore', - 'GDAL', - 'iris', - 'mapgenerator', - 'psutil', - 'psyplot', - 'rasterio', - 'scipy', - 'sklearn', - 'xesmf', - 'xgboost', -] - # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/sphinx/source/quickstart/installation.rst b/doc/sphinx/source/quickstart/installation.rst index 7b7e8e3e65..b035ea8276 100644 --- a/doc/sphinx/source/quickstart/installation.rst +++ b/doc/sphinx/source/quickstart/installation.rst @@ -260,13 +260,6 @@ install the Julia dependencies: esmvaltool install Julia -If you would like to run R diagnostic scripts, you will also need to install the R -dependencies: - -.. code-block:: bash - - esmvaltool install R - .. _install_with_docker: Docker installation @@ -551,13 +544,6 @@ install the ESMValTool Julia dependencies: esmvaltool install Julia -If you would like to run R diagnostic scripts, you will also need to install the R -dependencies. Install the R dependency packages: - -.. code-block:: bash - - esmvaltool install R - The next step is to check that the installation works properly. To do this, run the tool with: diff --git a/doc/sphinx/source/requirements.txt b/doc/sphinx/source/requirements.txt deleted file mode 100644 index 79a9897222..0000000000 --- a/doc/sphinx/source/requirements.txt +++ /dev/null @@ -1,30 +0,0 @@ -autodocsumm>=0.2.2 -cdo -cdsapi -cmocean -cython -dask[array] -docutils <0.17 -ecmwf-api-client -eofs -fiona -jinja2 -joblib -lime -matplotlib -nc-time-axis -netCDF4 -numpy -pandas -pillow -pyproj -pyyaml -scikit-learn -seaborn -seawater -shapely -sphinx>2 -xarray -xgboost -xlsxwriter -yamale diff --git a/docker/Dockerfile b/docker/Dockerfile index aaccb74010..9ee3ddf0f8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,18 +1,15 @@ # To build this container, go to ESMValTool root folder and execute: # docker build -t esmvaltool:latest . -f docker/Dockerfile -FROM continuumio/miniconda3 +FROM condaforge/mambaforge -COPY ./environment.yml /src/ESMValTool/environment.yml WORKDIR /src/ESMValTool -RUN conda update -y conda && conda install -y -c conda-forge mamba && mamba env create --name esmvaltool --file environment.yml && conda clean --all -y +COPY environment.yml . +RUN mamba update -y conda mamba pip && mamba env create --name esmvaltool --file environment.yml && conda clean --all -y # Make RUN commands use the new environment: SHELL ["conda", "run", "--name", "esmvaltool", "/bin/bash", "-c"] -COPY ./esmvaltool/install/R /src/ESMValTool/esmvaltool/install/R -RUN Rscript ./esmvaltool/install/R/setup.R - -COPY . /src/ESMValTool -RUN pip install -e . && pip cache purge && esmvaltool install Julia +COPY . . +RUN pip install --no-cache . && esmvaltool install Julia ENTRYPOINT ["conda", "run", "--name", "esmvaltool", "esmvaltool"] diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 2120e71eb6..65f1a34ea5 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,19 +1,14 @@ # To build this container, go to ESMValTool root folder and execute: # docker build -t esmvaltool:development . -f docker/Dockerfile.dev -FROM continuumio/miniconda3 +FROM condaforge/mambaforge -COPY ./environment.yml /src/ESMValTool/environment.yml WORKDIR /src/ESMValTool -RUN conda update -y conda && conda install -y -c conda-forge mamba && mamba env create --name esmvaltool --file environment.yml && conda clean --all -y +RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y curl git ssh && apt clean +COPY environment.yml . +RUN mamba update -y conda mamba pip && mamba env create --name esmvaltool --file environment.yml && conda clean --all -y # Make RUN commands use the new environment: SHELL ["conda", "run", "--name", "esmvaltool", "/bin/bash", "-c"] -COPY ./esmvaltool/install/R /src/ESMValTool/esmvaltool/install/R -RUN Rscript ./esmvaltool/install/R/setup.R - -COPY . /src/ESMValTool -RUN pip install -e .[test] && pip cache purge && esmvaltool install Julia && pip uninstall esmvaltool -y -RUN rm -rf /src - -ENTRYPOINT ["conda", "run", "--name", "esmvaltool", "esmvaltool"] +COPY . . +RUN pip install --no-cache .[test] && esmvaltool install Julia && pip uninstall esmvaltool -y diff --git a/docker/Dockerfile.exp b/docker/Dockerfile.exp index e43f8863f1..a522995fc4 100644 --- a/docker/Dockerfile.exp +++ b/docker/Dockerfile.exp @@ -1,20 +1,18 @@ # To build this container, go to ESMValTool root folder and execute: # docker build -t esmvaltool:experimental . -f docker/Dockerfile.exp -FROM esmvalgroup/esmvalcore:latest +FROM condaforge/mambaforge +RUN apt update && apt install -y git && apt clean -COPY ./environment.yml /src/ESMValTool/environment.yml WORKDIR /src/ESMValTool -RUN conda update -n base -y conda && conda install -y -c conda-forge mamba && mamba env create --name esmvaltool --file environment.yml && conda clean --all -y +COPY environment.yml . +RUN mamba update -y conda mamba pip \ + && mamba env create --name esmvaltool --file environment.yml \ + && conda clean --all -y -# Make RUN commands use the new environment: SHELL ["conda", "run", "--name", "esmvaltool", "/bin/bash", "-c"] -COPY . /src/ESMValTool -RUN pip install -e .[develop] -RUN mamba install -y -c conda-forge r-base -RUN esmvaltool install R -RUN mamba install -y -c conda-forge julia -RUN esmvaltool install Julia -RUN pip install ../ESMValCore && pip cache purge +COPY . . +RUN pip install --no-cache git+https://github.com/ESMValGroup/ESMValCore.git#egg=ESMValCore . \ + && esmvaltool install Julia ENTRYPOINT ["conda", "run", "--name", "esmvaltool", "esmvaltool"] diff --git a/environment.yml b/environment.yml index bd170d38eb..ceb92902c8 100644 --- a/environment.yml +++ b/environment.yml @@ -11,35 +11,118 @@ channels: dependencies: - pip!=21.3 - python>=3.8 - # Python packages that cannot be installed from PyPI: - cartopy - - compilers + - cdo>=1.9.7 + - cdsapi + - cf-units >=3.0.0,<3.1.0,!=3.0.1.post0 # see https://github.com/ESMValGroup/ESMValCore/issues/1655 + - cftime + - cmocean + - cython + - dask + - ecmwf-api-client + - eofs - esmpy - - esmvalcore + - esmvalcore 2.5.* + - fiona + - fire - gdal + - iris>=3.1.0 + - jinja2 + - joblib + - lime + - mapgenerator>=1.0.5 - matplotlib-base - # Non-Python dependencies - - cdo - - imagemagick - - julia - - nco + - natsort + - nc-time-axis + - netCDF4 + - numpy + - openpyxl + - pandas + - progressbar2 + - prov - psyplot - psy-maps - psy-reg - psy-simple + - pyproj>=2.1 - python-cdo + - python-dateutil + - pyyaml - rasterio + - requests - ruamel.yaml - - scikit-learn # may hit hw-specific issue if from pypi https://github.com/scikit-learn/scikit-learn/issues/14485 + - scikit-image + - scikit-learn + - scipy + - seaborn + - seawater + - shapely + - xarray>=0.12.0 + - xesmf==0.3.0 - xgboost + - xlsxwriter + # Python packages needed for testing + - pytest >=3.9,!=6.0.0rc1,!=6.0.0 + - pytest-cov + - pytest-env + - pytest-flake8 + - pytest-html !=2.1.0 + - pytest-metadata >=1.5.1 + - pytest-xdist + # Python packages needed for building docs + - autodocsumm>=0.2.2 + - sphinx>=5 + - sphinx_rtd_theme + # Python packages needed for development + - codespell + - docformatter + - isort + - pre-commit + - prospector + - pyroma + # - vprof not on conda-forge + - yamllint + - yapf - # Multi language support: + # NCL and dependencies - ncl - - r-base - # R packages needed for development + - cdo + - imagemagick + - nco + + # R and dependencies + - cdo + - r-base >=3.5 + - r-abind + - r-akima <=0.6-2.3 # see https://github.com/ESMValGroup/ESMValTool/issues/2695 + - r-climdex.pcic + - r-climprojdiags - r-docopt - - r-git2r # dependency of lintr + - r-dotcall64 + - r-functional + - r-ggplot2 + - r-gridextra - r-lintr + - r-logging + - r-mapproj + - r-maps + - r-multiapply + - r-ncdf4 + - r-ncdf4.helpers + - r-pcict + - r-plyr + - r-rcolorbrewer + - r-rcpp + - r-s2dverification + - r-snow + - r-spei - r-styler + - r-udunits2 - r-yaml - - r-udunits2 # needed by the docker build + # R packages needed for development + - r-git2r # dependency of lintr + - r-lintr + - r-styler + + # Julia (dependencies installed by separate script) + - julia diff --git a/environment_osx.yml b/environment_osx.yml index caf1b2a3ec..c268f6d463 100644 --- a/environment_osx.yml +++ b/environment_osx.yml @@ -11,25 +11,75 @@ channels: dependencies: - pip!=21.3 - python>=3.8 - # Python packages that cannot be installed from PyPI: - cartopy - - compilers + - cdo>=1.9.7 + - cdsapi + - cf-units >=3.0.0,<3.1.0,!=3.0.1.post0 # see https://github.com/ESMValGroup/ESMValCore/issues/1655 + - cftime + - cmocean + - cython + - dask + - ecmwf-api-client + - eofs - esmpy - - esmvalcore + - esmvalcore 2.5.* + - fiona + - fire - gdal + - iris>=3.1.0 + - jinja2 + - joblib + - lime + - mapgenerator>=1.0.5 - matplotlib-base - # Non-Python dependencies - - cdo - - imagemagick - - nco + - natsort + - nc-time-axis + - netCDF4 + - numpy + - openpyxl + - pandas + - progressbar2 + - prov + - psyplot - psy-maps - psy-reg - psy-simple - - psyplot + - pyproj>=2.1 - python-cdo + - python-dateutil + - pyyaml - rasterio + - requests - ruamel.yaml - # may hit hw-specific issue if from pypi - # https://github.com/scikit-learn/scikit-learn/issues/14485 + - scikit-image - scikit-learn + - scipy + - seaborn + - seawater + - shapely + - xarray>=0.12.0 + - xesmf==0.3.0 - xgboost + - xlsxwriter + # Python packages needed for testing + - pytest >=3.9,!=6.0.0rc1,!=6.0.0 + - pytest-cov + - pytest-env + - pytest-flake8 + - pytest-html !=2.1.0 + - pytest-metadata >=1.5.1 + - pytest-xdist + # Python packages needed for building docs + - autodocsumm>=0.2.2 + - sphinx>=5 + - sphinx_rtd_theme + # Python packages needed for development + - codespell + - docformatter + - isort + - pre-commit + - prospector + - pyroma + # - vprof not on conda-forge + - yamllint + - yapf diff --git a/esmvaltool/__init__.py b/esmvaltool/__init__.py index 154d3fee24..6f3375ec51 100644 --- a/esmvaltool/__init__.py +++ b/esmvaltool/__init__.py @@ -1,5 +1,12 @@ """ESMValTool diagnostics package.""" -__version__ = '2.5.0' +from importlib.metadata import PackageNotFoundError, version + +try: + __version__ = version("ESMValTool") +except PackageNotFoundError as exc: + raise PackageNotFoundError( + "ESMValTool package not found, please run `pip install -e .` before " + "importing the package.") from exc class ESMValToolDeprecationWarning(UserWarning): diff --git a/esmvaltool/install/R/r_requirements.yml b/esmvaltool/install/R/r_requirements.yml deleted file mode 100644 index 14fa06678b..0000000000 --- a/esmvaltool/install/R/r_requirements.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- -# R packages to be installed -# We support several installation procedures: - -# Latest version from CRAN: -# mypackage: - -# Specific version from CRAN (use only if last version can not be used): -# mypackage: -# origin: cran -# version: my_version - -# The next ones are NOT allowed on final code, but we support them to -# facilitate developing and testing of recipes before merging if the CRAN -# version is not available yet - -# From GitHub: -# mypackage: -# origin: github -# path: my_user/my_packge_repo@my_version - -# From a generic git: -# mypackage: -# origin: git -# url: my_repo_url -# ref: my_branch_commit_or_tag - -abind: -akima: -climdex.pcic: -ClimProjDiags: -docopt: -dotCall64: -functional: -ggplot2: -gridExtra: -lintr: -logging: -mapproj: -maps: -multiApply: -ncdf4: -ncdf4.helpers: -PCICt: -plyr: -RColorBrewer: -Rcpp: -s2dverification: -snow: -SPEI: -styler: -tools: -udunits2: -yaml: diff --git a/esmvaltool/install/R/setup.R b/esmvaltool/install/R/setup.R deleted file mode 100644 index e740baaf01..0000000000 --- a/esmvaltool/install/R/setup.R +++ /dev/null @@ -1,147 +0,0 @@ -log <- function(..., level = "INFO") { - cat(format(Sys.time(), "%Y-%m-%d %X"), level, ":", ..., "\n") -} - -# check for present library paths -rlibpath <- .libPaths() -Sys.setenv(R_INSTALL_STAGED = FALSE) - -# check if we can write in the present R libraries paths -if (any(file.access(rlibpath, 2) == 0)) { - # if possible, use the standard one for following installation - rlibloc <- rlibpath[which(file.access(rlibpath, 2) == 0)[1]] -} else { - # if not possible, create a local library in the home directory - rlibloc <- Sys.getenv("R_LIBS_USER") - dir.create( - path = Sys.getenv("R_LIBS_USER"), - showWarnings = FALSE, - recursive = TRUE - ) -} - -log("Installing packages to --> ", rlibloc) - -# define the R mirror to download packages -pkg_mirror <- "https://cloud.r-project.org" -log("Using mirror: ", pkg_mirror) - -# install the yaml package fist to handle the yaml-file -if (!require("yaml")) { - install.packages("yaml", repos = pkg_mirror) -} - -# get the script path -initial_options <- commandArgs(trailingOnly = FALSE) -file_arg_name <- "--file=" -script_name <- sub( - file_arg_name, "", - initial_options[grep(file_arg_name, initial_options)] -) -script_dirname <- dirname(script_name) - -# read the dependencies -dependencies <- yaml::read_yaml( - paste(script_dirname, "r_requirements.yml", sep = "/"), -) -# TODO: find a solution for script directory - -log(paste(unlist(names(dependencies)), collapse = ", ")) -inst_packages <- installed.packages() -package_list <- - names(dependencies)[!(names(dependencies) %in% inst_packages[, "Package"])] - -if (length(package_list) == 0) { - log("All packages are already installed!") - quit() -} else { - log("Number of packages to be installed: ", length(package_list)) -} - -n_cpus_override <- strtoi(Sys.getenv("R_INSTALL_N_CPUS", unset = "0")) -if (n_cpus_override) { - n_cpus <- n_cpus_override -} else { - n_cpus <- parallel::detectCores(all.tests = TRUE, logical = FALSE) - if (is.na(n_cpus)) { - n_cpus <- 1 - } -} -log("Using", n_cpus, "threads to compile packages") - -# Install packages required for installation -install_dependencies <- list("remotes", "devtools") -install_dependencies <- - install_dependencies[!install_dependencies %in% inst_packages[, "Package"]] -if (length(install_dependencies) > 0) { - log( - "Installing installation dependencies from CRAN:", - paste(unlist(install_dependencies), collapse = ", ") - ) - install.packages( - unlist(install_dependencies), - repos = pkg_mirror, - Ncpus = n_cpus, - dependencies = c("Depends", "Imports", "LinkingTo") - ) -} - -# Special installations -install <- function(package, info) { - if (!is.null(info)) { - if (info$origin == "github") { - log("Installing ", package, "from Github") - remotes::install_github(info$path) - } - if (info$origin == "git") { - log("Installing ", package, "from Git") - remotes::install_git(info$url, ref = info$ref) - } - if (info$origin == "cran") { - log("Installing version", info$version, "of", package, "from CRAN") - devtools::install_version( - package, - version = info$version, - repos = pkg_mirror - ) - } - } -} -for (package_name in names(dependencies)) { - install(package_name, dependencies[[package_name]]) -} - -# Get missing packages from CRAN, last versions -inst_packages <- installed.packages() -package_list <- - names(dependencies)[!(names(dependencies) %in% inst_packages[, "Package"])] -log( - "Installing packages from CRAN:", - paste(unlist(package_list), collapse = ", ") -) -if (length(package_list) != 0) { - install.packages( - package_list, - repos = pkg_mirror, - Ncpus = n_cpus, - dependencies = c("Depends", "Imports", "LinkingTo") - ) -} - -failed <- list() -for (package_name in names(dependencies)) { - success <- library( - package_name, - character.only = TRUE, - logical.return = TRUE - ) - if (!success) { - failed <- c(failed, package_name) - } -} -if (length(failed) != 0) { - log("Failed to install packages:", paste(failed, collapse = ", ")) - quit(status = 1, save = "no") -} - -log("Successfully installed all packages") diff --git a/pyproject.toml b/pyproject.toml index 121a39f5f2..8b33e256d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,5 @@ [build-system] -requires = ["setuptools >= 40.6.0", "wheel"] +requires = ["setuptools >= 40.6.0", "wheel", "setuptools_scm>=6.2"] build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] diff --git a/setup.py b/setup.py index 4cb9537190..15c4a22733 100755 --- a/setup.py +++ b/setup.py @@ -8,9 +8,6 @@ from setuptools import Command, setup -sys.path.insert(0, os.path.dirname(__file__)) -from esmvaltool import __version__ # noqa: E402 - PACKAGES = [ 'esmvaltool', ] @@ -26,7 +23,9 @@ 'cartopy', 'cdo', 'cdsapi', - 'cf-units', + # see https://github.com/SciTools/cf-units/issues/218 + # see https://github.com/ESMValGroup/ESMValCore/issues/1655 + 'cf-units>=3.0.0,<3.1.0,!=3.0.1.post0', 'cftime', 'cmocean', 'dask', @@ -64,7 +63,7 @@ 'seawater', 'shapely', 'xarray', - 'xesmf', + 'xesmf==0.3.0', 'xgboost', 'xlsxwriter', ], @@ -80,17 +79,20 @@ 'pytest-metadata>=1.5.1', 'pytest-xdist', ], + # Documentation dependencies + 'doc': [ + 'autodocsumm>=0.2.2', + 'sphinx>=5', + 'sphinx_rtd_theme', + ], # Development dependencies # Use pip install -e .[develop] to install in development mode 'develop': [ - 'autodocsumm>=0.2.2', 'codespell', 'docformatter', 'isort', 'pre-commit', 'prospector[with_pyroma]!=1.1.6.3,!=1.1.6.4', - 'sphinx>2', - 'sphinx_rtd_theme', 'vprof', 'yamllint', 'yapf', @@ -192,7 +194,6 @@ def read_description(filename): setup( name='ESMValTool', - version=__version__, author=read_authors('.zenodo.json'), description=read_description('.zenodo.json'), long_description=Path('README.md').read_text(), @@ -224,8 +225,10 @@ def read_description(filename): install_requires=REQUIREMENTS['install'], tests_require=REQUIREMENTS['test'], extras_require={ - 'develop': (set(REQUIREMENTS['develop'] + REQUIREMENTS['test']) - - {'pycodestyle'}), + 'develop': + REQUIREMENTS['develop'] + REQUIREMENTS['test'] + REQUIREMENTS['doc'], + 'doc': + REQUIREMENTS['doc'], 'test': REQUIREMENTS['test'], },